Merge inbound to mozilla-central. a=merge
authorBrindusan Cristian <cbrindusan@mozilla.com>
Tue, 11 Dec 2018 00:05:18 +0200
changeset 449737 13f891b92db19ea8ec85ef329eff7a793c8b368f
parent 449736 773061bafdd08f23078a247fbea15c95048c4199 (current diff)
parent 449701 39b04fe4eae8f8a2995808e73757e1e2be033f03 (diff)
child 449738 acdf73e4551a642919f7571b13c6b6a6ad2f2591
child 449891 e67df0b7834b623caf7f571373d50ba036ca871d
push id110425
push usercbrindusan@mozilla.com
push dateMon, 10 Dec 2018 22:12:29 +0000
treeherdermozilla-inbound@acdf73e4551a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone66.0a1
first release with
nightly linux32
13f891b92db1 / 66.0a1 / 20181210220553 / files
nightly linux64
13f891b92db1 / 66.0a1 / 20181210220553 / files
nightly mac
13f891b92db1 / 66.0a1 / 20181210220553 / files
nightly win32
13f891b92db1 / 66.0a1 / 20181210220553 / files
nightly win64
13f891b92db1 / 66.0a1 / 20181210220553 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
--- a/.gitignore
+++ b/.gitignore
@@ -151,8 +151,11 @@ lextab.py
 # Ignore Visual Studio Code workspace files.
 .vscode/
 !.vscode/extensions.json
 !.vscode/tasks.json
 
 # Ignore temp files created by patch command.
 *.orig
 *.rej
+
+# Ignore file generated by lalrpop at build time.
+third_party/rust/lalrpop/src/parser/lrgrammar.rs
--- a/.hgignore
+++ b/.hgignore
@@ -184,8 +184,11 @@ subinclude:servo/.hgignore
 # https://bz.mercurial-scm.org/show_bug.cgi?id=5322
 ^comm/
 
 # Ignore various raptor performance framework files
 ^testing/raptor/.raptor-venv
 ^testing/raptor/raptor-venv
 ^testing/raptor/raptor/tests/.*.json
 ^testing/raptor/webext/raptor/auto_gen_test_config.js
+
+# Ignore file generated by lalrpop at build time.
+^third_party/rust/lalrpop/src/parser/lrgrammar.rs
--- a/accessible/base/nsAccUtils.cpp
+++ b/accessible/base/nsAccUtils.cpp
@@ -87,31 +87,33 @@ int32_t nsAccUtils::GetARIAOrDefaultLeve
                            &level);
 
   if (level != 0) return level;
 
   return GetDefaultLevel(aAccessible);
 }
 
 int32_t nsAccUtils::GetLevelForXULContainerItem(nsIContent* aContent) {
-  nsCOMPtr<nsIDOMXULContainerItemElement> item(do_QueryInterface(aContent));
+  nsCOMPtr<nsIDOMXULContainerItemElement> item =
+      aContent->AsElement()->AsXULContainerItem();
   if (!item) return 0;
 
-  nsCOMPtr<nsIDOMXULContainerElement> container;
-  item->GetParentContainer(getter_AddRefs(container));
+  nsCOMPtr<Element> containerElement;
+  item->GetParentContainer(getter_AddRefs(containerElement));
+  nsCOMPtr<nsIDOMXULContainerElement> container =
+      containerElement ? containerElement->AsXULContainer() : nullptr;
   if (!container) return 0;
 
   // Get level of the item.
   int32_t level = -1;
   while (container) {
     level++;
 
-    nsCOMPtr<nsIDOMXULContainerElement> parentContainer;
-    container->GetParentContainer(getter_AddRefs(parentContainer));
-    parentContainer.swap(container);
+    container->GetParentContainer(getter_AddRefs(containerElement));
+    container = containerElement ? containerElement->AsXULContainer() : nullptr;
   }
 
   return level;
 }
 
 void nsAccUtils::SetLiveContainerAttributes(
     nsIPersistentProperties* aAttributes, nsIContent* aStartContent,
     dom::Element* aTopEl) {
--- a/accessible/generic/Accessible.cpp
+++ b/accessible/generic/Accessible.cpp
@@ -729,23 +729,25 @@ void Accessible::XULElmName(DocAccessibl
    *        - label has either value="foo" or children
    * Once a label is found, the search is discontinued, so a control
    *  that has a label child as well as having a label external to
    *  the control that uses the control="controlID" syntax will use
    *  the child label for its Name.
    */
 
   // CASE #1 (via label attribute) -- great majority of the cases
-  nsCOMPtr<nsIDOMXULSelectControlItemElement> itemEl = do_QueryInterface(aElm);
+  nsCOMPtr<nsIDOMXULSelectControlItemElement> itemEl =
+      aElm->AsElement()->AsXULSelectControlItem();
   if (itemEl) {
     itemEl->GetLabel(aName);
   } else {
     // Use @label if this is not a select control element, which uses label
     // attribute to indicate, which option is selected.
-    nsCOMPtr<nsIDOMXULSelectControlElement> select = do_QueryInterface(aElm);
+    nsCOMPtr<nsIDOMXULSelectControlElement> select =
+        aElm->AsElement()->AsXULSelectControl();
     if (!select) {
       aElm->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::label, aName);
     }
   }
 
   // CASES #2 and #3 ------ label as a child or <label control="id" ... >
   // </label>
   if (aName.IsEmpty()) {
@@ -1668,40 +1670,51 @@ Relation Accessible::RelationByType(Rela
             nsCOMPtr<nsIContent> formContent =
                 do_QueryInterface(form->GetDefaultSubmitElement());
             return Relation(mDoc, formContent);
           }
         }
       } else {
         // In XUL, use first <button default="true" .../> in the document
         nsIDocument* doc = mContent->OwnerDoc();
-        nsCOMPtr<nsIDOMXULButtonElement> buttonEl;
+        nsIContent* buttonEl = nullptr;
         if (doc->IsXULDocument()) {
           dom::XULDocument* xulDoc = doc->AsXULDocument();
           nsCOMPtr<nsIHTMLCollection> possibleDefaultButtons =
               xulDoc->GetElementsByAttribute(NS_LITERAL_STRING("default"),
                                              NS_LITERAL_STRING("true"));
           if (possibleDefaultButtons) {
             uint32_t length = possibleDefaultButtons->Length();
             // Check for button in list of default="true" elements
             for (uint32_t count = 0; count < length && !buttonEl; count++) {
-              buttonEl = do_QueryInterface(possibleDefaultButtons->Item(count));
+              nsIContent* item = possibleDefaultButtons->Item(count);
+              RefPtr<nsIDOMXULButtonElement> button =
+                  item->IsElement() ? item->AsElement()->AsXULButton()
+                                    : nullptr;
+              if (button) {
+                buttonEl = item;
+              }
             }
           }
           if (!buttonEl) {  // Check for anonymous accept button in <dialog>
             dom::Element* rootElm = mContent->OwnerDoc()->GetRootElement();
             if (rootElm) {
               nsIContent* possibleButtonEl =
                   rootElm->OwnerDoc()->GetAnonymousElementByAttribute(
                       rootElm, nsGkAtoms::_default, NS_LITERAL_STRING("true"));
-              buttonEl = do_QueryInterface(possibleButtonEl);
+              if (possibleButtonEl && possibleButtonEl->IsElement()) {
+                RefPtr<nsIDOMXULButtonElement> button =
+                    possibleButtonEl->AsElement()->AsXULButton();
+                if (button) {
+                  buttonEl = possibleButtonEl;
+                }
+              }
             }
           }
-          nsCOMPtr<nsIContent> relatedContent(do_QueryInterface(buttonEl));
-          return Relation(mDoc, relatedContent);
+          return Relation(mDoc, buttonEl);
         }
       }
       return Relation();
     }
 
     case RelationType::CONTAINING_DOCUMENT:
       return Relation(mDoc);
 
--- a/accessible/generic/RootAccessible.cpp
+++ b/accessible/generic/RootAccessible.cpp
@@ -332,17 +332,17 @@ void RootAccessible::ProcessDOMEvent(Eve
 
   nsINode* targetNode = accessible->GetNode();
   if (treeItemAcc && eventType.EqualsLiteral("select")) {
     // XXX: We shouldn't be based on DOM select event which doesn't provide us
     // any context info. We should integrate into nsTreeSelection instead.
     // If multiselect tree, we should fire selectionadd or selection removed
     if (FocusMgr()->HasDOMFocus(targetNode)) {
       nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSel =
-          do_QueryInterface(targetNode);
+          targetNode->AsElement()->AsXULMultiSelectControl();
       nsAutoString selType;
       multiSel->GetSelType(selType);
       if (selType.IsEmpty() || !selType.EqualsLiteral("single")) {
         // XXX: We need to fire EVENT_SELECTION_ADD and EVENT_SELECTION_REMOVE
         // for each tree item. Perhaps each tree item will need to cache its
         // selection state and fire an event after a DOM "select" event when
         // that state changes. XULTreeAccessible::UpdateTreeSelection();
         nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_SELECTION_WITHIN,
--- a/accessible/xul/XULComboboxAccessible.cpp
+++ b/accessible/xul/XULComboboxAccessible.cpp
@@ -49,76 +49,74 @@ uint64_t XULComboboxAccessible::NativeSt
   //     STATE_FOCUSABLE
   //     STATE_HASPOPUP
   //     STATE_EXPANDED
   //     STATE_COLLAPSED
 
   // Get focus status from base class
   uint64_t state = Accessible::NativeState();
 
-  nsCOMPtr<nsIDOMXULMenuListElement> menuList(do_QueryInterface(mContent));
+  nsCOMPtr<nsIDOMXULMenuListElement> menuList = Elm()->AsXULMenuList();
   if (menuList) {
     bool isOpen = false;
     menuList->GetOpen(&isOpen);
     if (isOpen)
       state |= states::EXPANDED;
     else
       state |= states::COLLAPSED;
   }
 
   return state | states::HASPOPUP;
 }
 
 void XULComboboxAccessible::Description(nsString& aDescription) {
   aDescription.Truncate();
   // Use description of currently focused option
-  nsCOMPtr<nsIDOMXULMenuListElement> menuListElm(do_QueryInterface(mContent));
+  nsCOMPtr<nsIDOMXULMenuListElement> menuListElm = Elm()->AsXULMenuList();
   if (!menuListElm) return;
 
-  nsCOMPtr<nsIDOMXULSelectControlItemElement> focusedOptionItem;
+  nsCOMPtr<Element> focusedOptionItem;
   menuListElm->GetSelectedItem(getter_AddRefs(focusedOptionItem));
-  nsCOMPtr<nsIContent> focusedOptionContent =
-      do_QueryInterface(focusedOptionItem);
-  if (focusedOptionContent && mDoc) {
-    Accessible* focusedOptionAcc = mDoc->GetAccessible(focusedOptionContent);
+  if (focusedOptionItem && mDoc) {
+    Accessible* focusedOptionAcc = mDoc->GetAccessible(focusedOptionItem);
     if (focusedOptionAcc) focusedOptionAcc->Description(aDescription);
   }
 }
 
 void XULComboboxAccessible::Value(nsString& aValue) const {
   aValue.Truncate();
 
   // The value is the option or text shown entered in the combobox.
-  nsCOMPtr<nsIDOMXULMenuListElement> menuList(do_QueryInterface(mContent));
+  nsCOMPtr<nsIDOMXULMenuListElement> menuList = Elm()->AsXULMenuList();
   if (menuList) menuList->GetLabel(aValue);
 }
 
 uint8_t XULComboboxAccessible::ActionCount() const {
   // Just one action (click).
   return 1;
 }
 
 bool XULComboboxAccessible::DoAction(uint8_t aIndex) const {
   if (aIndex != XULComboboxAccessible::eAction_Click) return false;
 
   // Programmaticaly toggle the combo box.
-  nsCOMPtr<nsIDOMXULMenuListElement> menuList(do_QueryInterface(mContent));
+  nsCOMPtr<nsIDOMXULMenuListElement> menuList = Elm()->AsXULMenuList();
   if (!menuList) return false;
 
   bool isDroppedDown = false;
   menuList->GetOpen(&isDroppedDown);
   menuList->SetOpen(!isDroppedDown);
   return true;
 }
 
 void XULComboboxAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) {
   aName.Truncate();
   if (aIndex != XULComboboxAccessible::eAction_Click) return;
 
-  nsCOMPtr<nsIDOMXULMenuListElement> menuList(do_QueryInterface(mContent));
+  nsCOMPtr<nsIDOMXULMenuListElement> menuList = Elm()->AsXULMenuList();
   if (!menuList) return;
 
   bool isDroppedDown = false;
   menuList->GetOpen(&isDroppedDown);
   if (isDroppedDown)
     aName.AssignLiteral("close");
   else
     aName.AssignLiteral("open");
@@ -150,17 +148,17 @@ bool XULComboboxAccessible::AreItemsOper
     if (autoCompleteInputElm) {
       bool isOpen = false;
       autoCompleteInputElm->GetPopupOpen(&isOpen);
       return isOpen;
     }
     return false;
   }
 
-  nsCOMPtr<nsIDOMXULMenuListElement> menuListElm = do_QueryInterface(mContent);
+  nsCOMPtr<nsIDOMXULMenuListElement> menuListElm = Elm()->AsXULMenuList();
   if (menuListElm) {
     bool isOpen = false;
     menuListElm->GetOpen(&isOpen);
     return isOpen;
   }
 
   return false;
 }
--- a/accessible/xul/XULFormControlAccessible.cpp
+++ b/accessible/xul/XULFormControlAccessible.cpp
@@ -70,18 +70,17 @@ role XULButtonAccessible::NativeRole() c
 
 uint64_t XULButtonAccessible::NativeState() const {
   // Possible states: focused, focusable, unavailable(disabled).
 
   // get focus and disable status from base class
   uint64_t state = Accessible::NativeState();
 
   // Buttons can be checked -- they simply appear pressed in rather than checked
-  nsCOMPtr<nsIDOMXULButtonElement> xulButtonElement(
-      do_QueryInterface(mContent));
+  nsCOMPtr<nsIDOMXULButtonElement> xulButtonElement = Elm()->AsXULButton();
   if (xulButtonElement) {
     nsAutoString type;
     xulButtonElement->GetType(type);
     if (type.EqualsLiteral("checkbox") || type.EqualsLiteral("radio")) {
       state |= states::CHECKABLE;
     }
     // Some buttons can have their checked state set without being of type
     // checkbox or radio. Expose the pressed state unconditionally.
@@ -152,25 +151,25 @@ uint8_t XULDropmarkerAccessible::ActionC
 
 bool XULDropmarkerAccessible::DropmarkerOpen(bool aToggleOpen) const {
   bool isOpen = false;
 
   nsIContent* parent = mContent->GetFlattenedTreeParent();
 
   while (parent) {
     nsCOMPtr<nsIDOMXULButtonElement> parentButtonElement =
-        do_QueryInterface(parent);
+        parent->AsElement()->AsXULButton();
     if (parentButtonElement) {
       parentButtonElement->GetOpen(&isOpen);
       if (aToggleOpen) parentButtonElement->SetOpen(!isOpen);
       return isOpen;
     }
 
     nsCOMPtr<nsIDOMXULMenuListElement> parentMenuListElement =
-        do_QueryInterface(parent);
+        parent->AsElement()->AsXULMenuList();
     if (parentMenuListElement) {
       parentMenuListElement->GetOpen(&isOpen);
       if (aToggleOpen) parentMenuListElement->SetOpen(!isOpen);
       return isOpen;
     }
     parent = parent->GetFlattenedTreeParent();
   }
 
@@ -242,17 +241,17 @@ XULRadioButtonAccessible::XULRadioButton
                                                    DocAccessible* aDoc)
     : RadioButtonAccessible(aContent, aDoc) {}
 
 uint64_t XULRadioButtonAccessible::NativeState() const {
   uint64_t state = LeafAccessible::NativeState();
   state |= states::CHECKABLE;
 
   nsCOMPtr<nsIDOMXULSelectControlItemElement> radioButton =
-      do_QueryInterface(mContent);
+      Elm()->AsXULSelectControlItem();
   if (radioButton) {
     bool selected = false;  // Radio buttons can be selected
     radioButton->GetSelected(&selected);
     if (selected) {
       state |= states::CHECKED;
     }
   }
 
--- a/accessible/xul/XULListboxAccessible.cpp
+++ b/accessible/xul/XULListboxAccessible.cpp
@@ -101,20 +101,23 @@ uint64_t XULListboxAccessible::NativeSta
 }
 
 /**
  * Our value is the label of our ( first ) selected child.
  */
 void XULListboxAccessible::Value(nsString& aValue) const {
   aValue.Truncate();
 
-  nsCOMPtr<nsIDOMXULSelectControlElement> select(do_QueryInterface(mContent));
+  nsCOMPtr<nsIDOMXULSelectControlElement> select = Elm()->AsXULSelectControl();
   if (select) {
-    nsCOMPtr<nsIDOMXULSelectControlItemElement> selectedItem;
-    select->GetSelectedItem(getter_AddRefs(selectedItem));
+    RefPtr<Element> element;
+    select->GetSelectedItem(getter_AddRefs(element));
+
+    nsCOMPtr<nsIDOMXULSelectControlItemElement> selectedItem =
+        element->AsXULSelectControlItem();
     if (selectedItem) selectedItem->GetLabel(aValue);
   }
 }
 
 role XULListboxAccessible::NativeRole() const {
   // A richlistbox is used with the new autocomplete URL bar, and has a parent
   // popup <panel>.
   if (mContent->GetParent() &&
@@ -125,119 +128,119 @@ role XULListboxAccessible::NativeRole() 
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // XULListboxAccessible: Table
 
 uint32_t XULListboxAccessible::ColCount() const { return 0; }
 
 uint32_t XULListboxAccessible::RowCount() {
-  nsCOMPtr<nsIDOMXULSelectControlElement> element(do_QueryInterface(mContent));
+  nsCOMPtr<nsIDOMXULSelectControlElement> element = Elm()->AsXULSelectControl();
 
   uint32_t itemCount = 0;
   if (element) element->GetItemCount(&itemCount);
 
   return itemCount;
 }
 
 Accessible* XULListboxAccessible::CellAt(uint32_t aRowIndex,
                                          uint32_t aColumnIndex) {
-  nsCOMPtr<nsIDOMXULSelectControlElement> control = do_QueryInterface(mContent);
+  nsCOMPtr<nsIDOMXULSelectControlElement> control = Elm()->AsXULSelectControl();
   NS_ENSURE_TRUE(control, nullptr);
 
-  nsCOMPtr<nsIDOMXULSelectControlItemElement> item;
-  control->GetItemAtIndex(aRowIndex, getter_AddRefs(item));
-  if (!item) return nullptr;
+  RefPtr<Element> element;
+  control->GetItemAtIndex(aRowIndex, getter_AddRefs(element));
+  if (!element) return nullptr;
 
-  nsCOMPtr<nsIContent> itemContent(do_QueryInterface(item));
-  if (!itemContent) return nullptr;
-
-  Accessible* row = mDoc->GetAccessible(itemContent);
+  Accessible* row = mDoc->GetAccessible(element);
   NS_ENSURE_TRUE(row, nullptr);
 
   return row->GetChildAt(aColumnIndex);
 }
 
 bool XULListboxAccessible::IsColSelected(uint32_t aColIdx) {
   nsCOMPtr<nsIDOMXULMultiSelectControlElement> control =
-      do_QueryInterface(mContent);
+      Elm()->AsXULMultiSelectControl();
   NS_ASSERTION(control,
                "Doesn't implement nsIDOMXULMultiSelectControlElement.");
 
   int32_t selectedrowCount = 0;
   nsresult rv = control->GetSelectedCount(&selectedrowCount);
   NS_ENSURE_SUCCESS(rv, false);
 
   return selectedrowCount == static_cast<int32_t>(RowCount());
 }
 
 bool XULListboxAccessible::IsRowSelected(uint32_t aRowIdx) {
-  nsCOMPtr<nsIDOMXULSelectControlElement> control = do_QueryInterface(mContent);
+  nsCOMPtr<nsIDOMXULSelectControlElement> control = Elm()->AsXULSelectControl();
   NS_ASSERTION(control, "Doesn't implement nsIDOMXULSelectControlElement.");
 
-  nsCOMPtr<nsIDOMXULSelectControlItemElement> item;
-  nsresult rv = control->GetItemAtIndex(aRowIdx, getter_AddRefs(item));
+  RefPtr<Element> element;
+  nsresult rv = control->GetItemAtIndex(aRowIdx, getter_AddRefs(element));
   NS_ENSURE_SUCCESS(rv, false);
 
+  nsCOMPtr<nsIDOMXULSelectControlItemElement> item =
+      element->AsXULSelectControlItem();
+
   bool isSelected = false;
   item->GetSelected(&isSelected);
   return isSelected;
 }
 
 bool XULListboxAccessible::IsCellSelected(uint32_t aRowIdx, uint32_t aColIdx) {
   return IsRowSelected(aRowIdx);
 }
 
 uint32_t XULListboxAccessible::SelectedCellCount() {
   nsCOMPtr<nsIDOMXULMultiSelectControlElement> control =
-      do_QueryInterface(mContent);
+      Elm()->AsXULMultiSelectControl();
   NS_ASSERTION(control,
                "Doesn't implement nsIDOMXULMultiSelectControlElement.");
 
   nsCOMPtr<nsINodeList> selectedItems;
   control->GetSelectedItems(getter_AddRefs(selectedItems));
   if (!selectedItems) return 0;
 
   uint32_t selectedItemsCount = selectedItems->Length();
 
   return selectedItemsCount * ColCount();
 }
 
 uint32_t XULListboxAccessible::SelectedColCount() {
   nsCOMPtr<nsIDOMXULMultiSelectControlElement> control =
-      do_QueryInterface(mContent);
+      Elm()->AsXULMultiSelectControl();
   NS_ASSERTION(control,
                "Doesn't implement nsIDOMXULMultiSelectControlElement.");
 
   int32_t selectedRowCount = 0;
   nsresult rv = control->GetSelectedCount(&selectedRowCount);
   NS_ENSURE_SUCCESS(rv, 0);
 
   return selectedRowCount > 0 &&
                  selectedRowCount == static_cast<int32_t>(RowCount())
              ? ColCount()
              : 0;
 }
 
 uint32_t XULListboxAccessible::SelectedRowCount() {
   nsCOMPtr<nsIDOMXULMultiSelectControlElement> control =
-      do_QueryInterface(mContent);
+      Elm()->AsXULMultiSelectControl();
   NS_ASSERTION(control,
                "Doesn't implement nsIDOMXULMultiSelectControlElement.");
 
   int32_t selectedRowCount = 0;
   nsresult rv = control->GetSelectedCount(&selectedRowCount);
   NS_ENSURE_SUCCESS(rv, 0);
 
   return selectedRowCount >= 0 ? selectedRowCount : 0;
 }
 
 void XULListboxAccessible::SelectedCells(nsTArray<Accessible*>* aCells) {
   nsCOMPtr<nsIDOMXULMultiSelectControlElement> control =
-      do_QueryInterface(mContent);
+      Elm()->AsXULMultiSelectControl();
   NS_ASSERTION(control,
                "Doesn't implement nsIDOMXULMultiSelectControlElement.");
 
   nsCOMPtr<nsINodeList> selectedItems;
   control->GetSelectedItems(getter_AddRefs(selectedItems));
   if (!selectedItems) return;
 
   uint32_t selectedItemsCount = selectedItems->Length();
@@ -253,36 +256,36 @@ void XULListboxAccessible::SelectedCells
         if (cell->Role() == roles::CELL) aCells->AppendElement(cell);
       }
     }
   }
 }
 
 void XULListboxAccessible::SelectedCellIndices(nsTArray<uint32_t>* aCells) {
   nsCOMPtr<nsIDOMXULMultiSelectControlElement> control =
-      do_QueryInterface(mContent);
+      Elm()->AsXULMultiSelectControl();
   NS_ASSERTION(control,
                "Doesn't implement nsIDOMXULMultiSelectControlElement.");
 
   nsCOMPtr<nsINodeList> selectedItems;
   control->GetSelectedItems(getter_AddRefs(selectedItems));
   if (!selectedItems) return;
 
   uint32_t selectedItemsCount = selectedItems->Length();
 
   uint32_t colCount = ColCount();
   aCells->SetCapacity(selectedItemsCount * colCount);
   aCells->AppendElements(selectedItemsCount * colCount);
 
   for (uint32_t selItemsIdx = 0, cellsIdx = 0; selItemsIdx < selectedItemsCount;
        selItemsIdx++) {
     nsIContent* itemContent = selectedItems->Item(selItemsIdx);
+
     nsCOMPtr<nsIDOMXULSelectControlItemElement> item =
-        do_QueryInterface(itemContent);
-
+        itemContent->AsElement()->AsXULSelectControlItem();
     if (item) {
       int32_t itemIdx = -1;
       control->GetIndexOfItem(item, &itemIdx);
       if (itemIdx >= 0)
         for (uint32_t colIdx = 0; colIdx < colCount; colIdx++, cellsIdx++)
           aCells->ElementAt(cellsIdx) = itemIdx * colCount + colIdx;
     }
   }
@@ -293,17 +296,17 @@ void XULListboxAccessible::SelectedColIn
   aCols->SetCapacity(selColCount);
 
   for (uint32_t colIdx = 0; colIdx < selColCount; colIdx++)
     aCols->AppendElement(colIdx);
 }
 
 void XULListboxAccessible::SelectedRowIndices(nsTArray<uint32_t>* aRows) {
   nsCOMPtr<nsIDOMXULMultiSelectControlElement> control =
-      do_QueryInterface(mContent);
+      Elm()->AsXULMultiSelectControl();
   NS_ASSERTION(control,
                "Doesn't implement nsIDOMXULMultiSelectControlElement.");
 
   nsCOMPtr<nsINodeList> selectedItems;
   control->GetSelectedItems(getter_AddRefs(selectedItems));
   if (!selectedItems) return;
 
   uint32_t rowCount = selectedItems->Length();
@@ -311,46 +314,52 @@ void XULListboxAccessible::SelectedRowIn
   if (!rowCount) return;
 
   aRows->SetCapacity(rowCount);
   aRows->AppendElements(rowCount);
 
   for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) {
     nsIContent* itemContent = selectedItems->Item(rowIdx);
     nsCOMPtr<nsIDOMXULSelectControlItemElement> item =
-        do_QueryInterface(itemContent);
+        itemContent->AsElement()->AsXULSelectControlItem();
 
     if (item) {
       int32_t itemIdx = -1;
       control->GetIndexOfItem(item, &itemIdx);
       if (itemIdx >= 0) aRows->ElementAt(rowIdx) = itemIdx;
     }
   }
 }
 
 void XULListboxAccessible::SelectRow(uint32_t aRowIdx) {
   nsCOMPtr<nsIDOMXULMultiSelectControlElement> control =
-      do_QueryInterface(mContent);
+      Elm()->AsXULMultiSelectControl();
   NS_ASSERTION(control,
                "Doesn't implement nsIDOMXULMultiSelectControlElement.");
 
-  nsCOMPtr<nsIDOMXULSelectControlItemElement> item;
+  RefPtr<Element> item;
   control->GetItemAtIndex(aRowIdx, getter_AddRefs(item));
-  control->SelectItem(item);
+
+  nsCOMPtr<nsIDOMXULSelectControlItemElement> itemElm =
+      item->AsXULSelectControlItem();
+  control->SelectItem(itemElm);
 }
 
 void XULListboxAccessible::UnselectRow(uint32_t aRowIdx) {
   nsCOMPtr<nsIDOMXULMultiSelectControlElement> control =
-      do_QueryInterface(mContent);
+      Elm()->AsXULMultiSelectControl();
   NS_ASSERTION(control,
                "Doesn't implement nsIDOMXULMultiSelectControlElement.");
 
-  nsCOMPtr<nsIDOMXULSelectControlItemElement> item;
+  RefPtr<Element> item;
   control->GetItemAtIndex(aRowIdx, getter_AddRefs(item));
-  control->RemoveItemFromSelection(item);
+
+  nsCOMPtr<nsIDOMXULSelectControlItemElement> itemElm =
+      item->AsXULSelectControlItem();
+  control->RemoveItemFromSelection(itemElm);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // XULListboxAccessible: Widgets
 
 bool XULListboxAccessible::IsWidget() const { return true; }
 
 bool XULListboxAccessible::IsActiveWidget() const {
@@ -377,23 +386,23 @@ bool XULListboxAccessible::AreItemsOpera
       autoCompletePopupElm->GetPopupOpen(&isOpen);
       return isOpen;
     }
   }
   return true;
 }
 
 Accessible* XULListboxAccessible::ContainerWidget() const {
-  if (IsAutoCompletePopup()) {
+  if (IsAutoCompletePopup() && mContent->GetParent()) {
     // This works for XUL autocompletes. It doesn't work for HTML forms
     // autocomplete because of potential crossprocess calls (when autocomplete
     // lives in content process while popup lives in chrome process). If that's
     // a problem then rethink Widgets interface.
     nsCOMPtr<nsIDOMXULMenuListElement> menuListElm =
-        do_QueryInterface(mContent->GetParent());
+        mContent->GetParent()->AsElement()->AsXULMenuList();
     if (menuListElm) {
       RefPtr<mozilla::dom::Element> inputElm;
       menuListElm->GetInputField(getter_AddRefs(inputElm));
       if (inputElm) {
         Accessible* input = mDoc->GetAccessible(inputElm);
         return input ? input->ContainerWidget() : nullptr;
       }
     }
@@ -418,26 +427,24 @@ XULListitemAccessible::XULListitemAccess
 }
 
 XULListitemAccessible::~XULListitemAccessible() {}
 
 Accessible* XULListitemAccessible::GetListAccessible() const {
   if (IsDefunct()) return nullptr;
 
   nsCOMPtr<nsIDOMXULSelectControlItemElement> listItem =
-      do_QueryInterface(mContent);
+      Elm()->AsXULSelectControlItem();
   if (!listItem) return nullptr;
 
-  nsCOMPtr<nsIDOMXULSelectControlElement> list;
-  listItem->GetControl(getter_AddRefs(list));
+  RefPtr<Element> listElement;
+  listItem->GetControl(getter_AddRefs(listElement));
+  if (!listElement) return nullptr;
 
-  nsCOMPtr<nsIContent> listContent(do_QueryInterface(list));
-  if (!listContent) return nullptr;
-
-  return mDoc->GetAccessible(listContent);
+  return mDoc->GetAccessible(listElement);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // XULListitemAccessible Accessible
 
 void XULListitemAccessible::Description(nsString& aDesc) {
   AccessibleWrap::Description(aDesc);
 }
@@ -470,18 +477,17 @@ role XULListitemAccessible::NativeRole()
 }
 
 uint64_t XULListitemAccessible::NativeState() const {
   if (mIsCheckbox) return XULMenuitemAccessible::NativeState();
 
   uint64_t states = NativeInteractiveState();
 
   nsCOMPtr<nsIDOMXULSelectControlItemElement> listItem =
-      do_QueryInterface(mContent);
-
+      Elm()->AsXULSelectControlItem();
   if (listItem) {
     bool isSelected;
     listItem->GetSelected(&isSelected);
     if (isSelected) states |= states::SELECTED;
 
     if (FocusMgr()->IsFocused(this)) states |= states::FOCUSED;
   }
 
--- a/accessible/xul/XULMenuAccessible.cpp
+++ b/accessible/xul/XULMenuAccessible.cpp
@@ -69,18 +69,18 @@ uint64_t XULMenuitemAccessible::NativeSt
       state |= states::CHECKED;
   }
 
   // Combo box listitem
   bool isComboboxOption = (Role() == roles::COMBOBOX_OPTION);
   if (isComboboxOption) {
     // Is selected?
     bool isSelected = false;
-    nsCOMPtr<nsIDOMXULSelectControlItemElement> item(
-        do_QueryInterface(mContent));
+    nsCOMPtr<nsIDOMXULSelectControlItemElement> item =
+        Elm()->AsXULSelectControlItem();
     NS_ENSURE_TRUE(item, state);
     item->GetSelected(&isSelected);
 
     // Is collapsed?
     bool isCollapsed = false;
     Accessible* parent = Parent();
     if (parent && parent->State() & states::INVISIBLE) isCollapsed = true;
 
@@ -215,17 +215,17 @@ KeyBinding XULMenuitemAccessible::Keyboa
   if (modifiersStr.Find("accel") != -1) {
     modifierMask |= KeyBinding::AccelModifier();
   }
 
   return KeyBinding(key, modifierMask);
 }
 
 role XULMenuitemAccessible::NativeRole() const {
-  nsCOMPtr<nsIDOMXULContainerElement> xulContainer(do_QueryInterface(mContent));
+  nsCOMPtr<nsIDOMXULContainerElement> xulContainer = Elm()->AsXULContainer();
   if (xulContainer) return roles::PARENT_MENUITEM;
 
   if (mParent && mParent->Role() == roles::COMBOBOX_LIST)
     return roles::COMBOBOX_OPTION;
 
   if (mContent->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
                                          nsGkAtoms::radio, eCaseMatters))
     return roles::RADIO_MENU_ITEM;
@@ -335,18 +335,26 @@ uint8_t XULMenuSeparatorAccessible::Acti
 
 XULMenupopupAccessible::XULMenupopupAccessible(nsIContent* aContent,
                                                DocAccessible* aDoc)
     : XULSelectControlAccessible(aContent, aDoc) {
   nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(GetFrame());
   if (menuPopupFrame && menuPopupFrame->IsMenu()) mType = eMenuPopupType;
 
   // May be the anonymous <menupopup> inside <menulist> (a combobox)
-  mSelectControl = do_QueryInterface(mContent->GetFlattenedTreeParent());
-  if (!mSelectControl) mGenericTypes &= ~eSelect;
+  nsIContent* parent = mContent->GetFlattenedTreeParent();
+  nsCOMPtr<nsIDOMXULSelectControlElement> selectControl =
+      parent && parent->AsElement() ? parent->AsElement()->AsXULSelectControl()
+                                    : nullptr;
+  if (selectControl) {
+    mSelectControl = parent->AsElement();
+  } else {
+    mSelectControl = nullptr;
+    mGenericTypes &= ~eSelect;
+  }
 
   mStateFlags |= eNoXBLKids;
 }
 
 uint64_t XULMenupopupAccessible::NativeState() const {
   uint64_t state = Accessible::NativeState();
 
 #ifdef DEBUG
--- a/accessible/xul/XULSelectControlAccessible.cpp
+++ b/accessible/xul/XULSelectControlAccessible.cpp
@@ -4,17 +4,16 @@
  * 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 "XULSelectControlAccessible.h"
 
 #include "nsAccessibilityService.h"
 #include "DocAccessible.h"
 
-#include "nsIDOMXULContainerElement.h"
 #include "nsIDOMXULSelectCntrlItemEl.h"
 #include "nsIDOMXULMultSelectCntrlEl.h"
 #include "nsIMutableArray.h"
 #include "nsIServiceManager.h"
 
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/KeyboardEventBinding.h"
 
@@ -24,17 +23,17 @@ using namespace mozilla::a11y;
 ////////////////////////////////////////////////////////////////////////////////
 // XULSelectControlAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 XULSelectControlAccessible::XULSelectControlAccessible(nsIContent* aContent,
                                                        DocAccessible* aDoc)
     : AccessibleWrap(aContent, aDoc) {
   mGenericTypes |= eSelect;
-  mSelectControl = do_QueryInterface(aContent);
+  mSelectControl = aContent->AsElement();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // XULSelectControlAccessible: Accessible
 
 void XULSelectControlAccessible::Shutdown() {
   mSelectControl = nullptr;
   AccessibleWrap::Shutdown();
@@ -46,172 +45,205 @@ void XULSelectControlAccessible::Shutdow
 void XULSelectControlAccessible::SelectedItems(nsTArray<Accessible*>* aItems) {
   // For XUL multi-select control
   nsCOMPtr<nsIDOMXULMultiSelectControlElement> xulMultiSelect =
       do_QueryInterface(mSelectControl);
   if (xulMultiSelect) {
     int32_t length = 0;
     xulMultiSelect->GetSelectedCount(&length);
     for (int32_t index = 0; index < length; index++) {
-      nsCOMPtr<nsIDOMXULSelectControlItemElement> itemElm;
-      xulMultiSelect->MultiGetSelectedItem(index, getter_AddRefs(itemElm));
-      nsCOMPtr<nsINode> itemNode(do_QueryInterface(itemElm));
-      Accessible* item = mDoc->GetAccessible(itemNode);
+      RefPtr<Element> element;
+      xulMultiSelect->MultiGetSelectedItem(index, getter_AddRefs(element));
+      Accessible* item = mDoc->GetAccessible(element);
       if (item) aItems->AppendElement(item);
     }
   } else {  // Single select?
-    nsCOMPtr<nsIDOMXULSelectControlItemElement> itemElm;
-    mSelectControl->GetSelectedItem(getter_AddRefs(itemElm));
-    nsCOMPtr<nsINode> itemNode(do_QueryInterface(itemElm));
-    if (itemNode) {
-      Accessible* item = mDoc->GetAccessible(itemNode);
+    nsCOMPtr<nsIDOMXULSelectControlElement> selectControl =
+        mSelectControl->AsXULSelectControl();
+    RefPtr<Element> element;
+    selectControl->GetSelectedItem(getter_AddRefs(element));
+    if (element) {
+      Accessible* item = mDoc->GetAccessible(element);
       if (item) aItems->AppendElement(item);
     }
   }
 }
 
 Accessible* XULSelectControlAccessible::GetSelectedItem(uint32_t aIndex) {
   nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelectControl =
-      do_QueryInterface(mSelectControl);
+      mSelectControl->AsXULMultiSelectControl();
 
-  nsCOMPtr<nsIDOMXULSelectControlItemElement> itemElm;
-  if (multiSelectControl)
-    multiSelectControl->MultiGetSelectedItem(aIndex, getter_AddRefs(itemElm));
-  else if (aIndex == 0)
-    mSelectControl->GetSelectedItem(getter_AddRefs(itemElm));
+  RefPtr<Element> element;
+  if (multiSelectControl) {
+    multiSelectControl->MultiGetSelectedItem(aIndex, getter_AddRefs(element));
+  } else if (aIndex == 0) {
+    nsCOMPtr<nsIDOMXULSelectControlElement> selectControl =
+        mSelectControl->AsXULSelectControl();
+    if (selectControl) {
+      selectControl->GetSelectedItem(getter_AddRefs(element));
+    }
+  }
 
-  nsCOMPtr<nsINode> itemNode(do_QueryInterface(itemElm));
-  return itemNode && mDoc ? mDoc->GetAccessible(itemNode) : nullptr;
+  return element && mDoc ? mDoc->GetAccessible(element) : nullptr;
 }
 
 uint32_t XULSelectControlAccessible::SelectedItemCount() {
   // For XUL multi-select control
   nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelectControl =
-      do_QueryInterface(mSelectControl);
+      mSelectControl->AsXULMultiSelectControl();
   if (multiSelectControl) {
     int32_t count = 0;
     multiSelectControl->GetSelectedCount(&count);
     return count;
   }
 
   // For XUL single-select control/menulist
-  int32_t index;
-  mSelectControl->GetSelectedIndex(&index);
-  return (index >= 0) ? 1 : 0;
+  nsCOMPtr<nsIDOMXULSelectControlElement> selectControl =
+      mSelectControl->AsXULSelectControl();
+  if (selectControl) {
+    int32_t index = -1;
+    selectControl->GetSelectedIndex(&index);
+    return (index >= 0) ? 1 : 0;
+  }
+
+  return 0;
 }
 
 bool XULSelectControlAccessible::AddItemToSelection(uint32_t aIndex) {
   Accessible* item = GetChildAt(aIndex);
-  if (!item) return false;
+  if (!item || !item->GetContent()) return false;
 
   nsCOMPtr<nsIDOMXULSelectControlItemElement> itemElm =
-      do_QueryInterface(item->GetContent());
+      item->GetContent()->AsElement()->AsXULSelectControlItem();
   if (!itemElm) return false;
 
   bool isItemSelected = false;
   itemElm->GetSelected(&isItemSelected);
   if (isItemSelected) return true;
 
   nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelectControl =
-      do_QueryInterface(mSelectControl);
+      mSelectControl->AsXULMultiSelectControl();
 
-  if (multiSelectControl)
+  if (multiSelectControl) {
     multiSelectControl->AddItemToSelection(itemElm);
-  else
-    mSelectControl->SetSelectedItem(itemElm);
+  } else {
+    nsCOMPtr<nsIDOMXULSelectControlElement> selectControl =
+        mSelectControl->AsXULSelectControl();
+    if (selectControl) {
+      selectControl->SetSelectedItem(item->Elm());
+    }
+  }
 
   return true;
 }
 
 bool XULSelectControlAccessible::RemoveItemFromSelection(uint32_t aIndex) {
   Accessible* item = GetChildAt(aIndex);
-  if (!item) return false;
+  if (!item || !item->GetContent()) return false;
 
   nsCOMPtr<nsIDOMXULSelectControlItemElement> itemElm =
-      do_QueryInterface(item->GetContent());
+      item->GetContent()->AsElement()->AsXULSelectControlItem();
   if (!itemElm) return false;
 
   bool isItemSelected = false;
   itemElm->GetSelected(&isItemSelected);
   if (!isItemSelected) return true;
 
   nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelectControl =
-      do_QueryInterface(mSelectControl);
+      mSelectControl->AsXULMultiSelectControl();
 
-  if (multiSelectControl)
+  if (multiSelectControl) {
     multiSelectControl->RemoveItemFromSelection(itemElm);
-  else
-    mSelectControl->SetSelectedItem(nullptr);
+  } else {
+    nsCOMPtr<nsIDOMXULSelectControlElement> selectControl =
+        mSelectControl->AsXULSelectControl();
+    if (selectControl) {
+      selectControl->SetSelectedItem(nullptr);
+    }
+  }
 
   return true;
 }
 
 bool XULSelectControlAccessible::IsItemSelected(uint32_t aIndex) {
   Accessible* item = GetChildAt(aIndex);
-  if (!item) return false;
+  if (!item || !item->GetContent()) return false;
 
   nsCOMPtr<nsIDOMXULSelectControlItemElement> itemElm =
-      do_QueryInterface(item->GetContent());
+      item->GetContent()->AsElement()->AsXULSelectControlItem();
   if (!itemElm) return false;
 
   bool isItemSelected = false;
   itemElm->GetSelected(&isItemSelected);
   return isItemSelected;
 }
 
 bool XULSelectControlAccessible::UnselectAll() {
   nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelectControl =
-      do_QueryInterface(mSelectControl);
-  multiSelectControl ? multiSelectControl->ClearSelection()
-                     : mSelectControl->SetSelectedIndex(-1);
+      mSelectControl->AsXULMultiSelectControl();
+  if (multiSelectControl) {
+    multiSelectControl->ClearSelection();
+  } else {
+    nsCOMPtr<nsIDOMXULSelectControlElement> selectControl =
+        mSelectControl->AsXULSelectControl();
+    if (selectControl) {
+      selectControl->SetSelectedIndex(-1);
+    }
+  }
 
   return true;
 }
 
 bool XULSelectControlAccessible::SelectAll() {
   nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelectControl =
-      do_QueryInterface(mSelectControl);
+      mSelectControl->AsXULMultiSelectControl();
   if (multiSelectControl) {
     multiSelectControl->SelectAll();
     return true;
   }
 
   // otherwise, don't support this method
   return false;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // XULSelectControlAccessible: Widgets
 
 Accessible* XULSelectControlAccessible::CurrentItem() const {
   if (!mSelectControl) return nullptr;
 
-  nsCOMPtr<nsIDOMXULSelectControlItemElement> currentItemElm;
+  RefPtr<Element> currentItemElm;
   nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelectControl =
-      do_QueryInterface(mSelectControl);
-  if (multiSelectControl)
+      mSelectControl->AsXULMultiSelectControl();
+  if (multiSelectControl) {
     multiSelectControl->GetCurrentItem(getter_AddRefs(currentItemElm));
-  else
-    mSelectControl->GetSelectedItem(getter_AddRefs(currentItemElm));
+  } else {
+    nsCOMPtr<nsIDOMXULSelectControlElement> selectControl =
+        mSelectControl->AsXULSelectControl();
+    if (selectControl) {
+      selectControl->GetSelectedItem(getter_AddRefs(currentItemElm));
+    }
+  }
 
-  nsCOMPtr<nsINode> DOMNode;
-  if (currentItemElm) DOMNode = do_QueryInterface(currentItemElm);
-
-  if (DOMNode) {
+  if (currentItemElm) {
     DocAccessible* document = Document();
-    if (document) return document->GetAccessible(DOMNode);
+    if (document) return document->GetAccessible(currentItemElm);
   }
 
   return nullptr;
 }
 
 void XULSelectControlAccessible::SetCurrentItem(const Accessible* aItem) {
   if (!mSelectControl) return;
 
-  nsCOMPtr<nsIDOMXULSelectControlItemElement> itemElm =
-      do_QueryInterface(aItem->GetContent());
+  nsCOMPtr<Element> itemElm = aItem->Elm();
   nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelectControl =
-      do_QueryInterface(mSelectControl);
-  if (multiSelectControl)
+      itemElm->AsXULMultiSelectControl();
+  if (multiSelectControl) {
     multiSelectControl->SetCurrentItem(itemElm);
-  else
-    mSelectControl->SetSelectedItem(itemElm);
+  } else {
+    nsCOMPtr<nsIDOMXULSelectControlElement> selectControl =
+        mSelectControl->AsXULSelectControl();
+    if (selectControl) {
+      selectControl->SetSelectedItem(itemElm);
+    }
+  }
 }
--- a/accessible/xul/XULSelectControlAccessible.h
+++ b/accessible/xul/XULSelectControlAccessible.h
@@ -34,17 +34,15 @@ class XULSelectControlAccessible : publi
   virtual bool SelectAll() override;
   virtual bool UnselectAll() override;
 
   // Widgets
   virtual Accessible* CurrentItem() const override;
   virtual void SetCurrentItem(const Accessible* aItem) override;
 
  protected:
-  // nsIDOMXULMultiSelectControlElement inherits from this, so we'll always have
-  // one of these if the widget is valid and not defunct
-  nsCOMPtr<nsIDOMXULSelectControlElement> mSelectControl;
+  RefPtr<Element> mSelectControl;
 };
 
 }  // namespace a11y
 }  // namespace mozilla
 
 #endif
--- a/accessible/xul/XULTabAccessible.cpp
+++ b/accessible/xul/XULTabAccessible.cpp
@@ -57,17 +57,18 @@ role XULTabAccessible::NativeRole() cons
 
 uint64_t XULTabAccessible::NativeState() const {
   // Possible states: focused, focusable, unavailable(disabled), offscreen.
 
   // get focus and disable status from base class
   uint64_t state = AccessibleWrap::NativeState();
 
   // Check whether the tab is selected and/or pinned
-  nsCOMPtr<nsIDOMXULSelectControlItemElement> tab(do_QueryInterface(mContent));
+  nsCOMPtr<nsIDOMXULSelectControlItemElement> tab =
+      Elm()->AsXULSelectControlItem();
   if (tab) {
     bool selected = false;
     if (NS_SUCCEEDED(tab->GetSelected(&selected)) && selected)
       state |= states::SELECTED;
 
     if (mContent->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::pinned,
                                            nsGkAtoms::_true, eCaseMatters))
       state |= states::PINNED;
@@ -81,18 +82,21 @@ uint64_t XULTabAccessible::NativeInterac
   return (state & states::UNAVAILABLE) ? state : state | states::SELECTABLE;
 }
 
 Relation XULTabAccessible::RelationByType(RelationType aType) const {
   Relation rel = AccessibleWrap::RelationByType(aType);
   if (aType != RelationType::LABEL_FOR) return rel;
 
   // Expose 'LABEL_FOR' relation on tab accessible for tabpanel accessible.
+  nsIContent* parent = mContent->GetParent();
+  if (!parent) return rel;
+
   nsCOMPtr<nsIDOMXULRelatedElement> tabsElm =
-      do_QueryInterface(mContent->GetParent());
+      parent->AsElement()->AsXULRelated();
   if (!tabsElm) return rel;
 
   RefPtr<mozilla::dom::Element> tabpanelElement;
   tabsElm->GetRelatedElement(GetNode(), getter_AddRefs(tabpanelElement));
   if (!tabpanelElement) return rel;
 
   rel.AppendTarget(mDoc, tabpanelElement);
   return rel;
@@ -192,18 +196,20 @@ XULTabpanelAccessible::XULTabpanelAccess
 
 role XULTabpanelAccessible::NativeRole() const { return roles::PROPERTYPAGE; }
 
 Relation XULTabpanelAccessible::RelationByType(RelationType aType) const {
   Relation rel = AccessibleWrap::RelationByType(aType);
   if (aType != RelationType::LABELLED_BY) return rel;
 
   // Expose 'LABELLED_BY' relation on tabpanel accessible for tab accessible.
+  if (!mContent->GetParent()) return rel;
+
   nsCOMPtr<nsIDOMXULRelatedElement> tabpanelsElm =
-      do_QueryInterface(mContent->GetParent());
+      mContent->GetParent()->AsElement()->AsXULRelated();
   if (!tabpanelsElm) return rel;
 
   RefPtr<mozilla::dom::Element> tabElement;
   tabpanelsElm->GetRelatedElement(GetNode(), getter_AddRefs(tabElement));
   if (!tabElement) return rel;
 
   rel.AppendTarget(mDoc, tabElement);
   return rel;
--- a/accessible/xul/XULTreeAccessible.cpp
+++ b/accessible/xul/XULTreeAccessible.cpp
@@ -405,23 +405,23 @@ bool XULTreeAccessible::AreItemsOperable
       autoCompletePopupElm->GetPopupOpen(&isOpen);
       return isOpen;
     }
   }
   return true;
 }
 
 Accessible* XULTreeAccessible::ContainerWidget() const {
-  if (IsAutoCompletePopup()) {
+  if (IsAutoCompletePopup() && mContent->GetParent()) {
     // This works for XUL autocompletes. It doesn't work for HTML forms
     // autocomplete because of potential crossprocess calls (when autocomplete
     // lives in content process while popup lives in chrome process). If that's
     // a problem then rethink Widgets interface.
     nsCOMPtr<nsIDOMXULMenuListElement> menuListElm =
-        do_QueryInterface(mContent->GetParent());
+        mContent->GetParent()->AsElement()->AsXULMenuList();
     if (menuListElm) {
       RefPtr<mozilla::dom::Element> inputElm;
       menuListElm->GetInputField(getter_AddRefs(inputElm));
       if (inputElm) {
         Accessible* input = mDoc->GetAccessible(inputElm);
         return input ? input->ContainerWidget() : nullptr;
       }
     }
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -6820,17 +6820,17 @@ var CanvasPermissionPromptHelper = {
       return;
     }
 
     let browser;
     if (aSubject instanceof Ci.nsIDOMWindow) {
       let contentWindow = aSubject.QueryInterface(Ci.nsIDOMWindow);
       browser = contentWindow.docShell.chromeEventHandler;
     } else {
-      browser = aSubject.QueryInterface(Ci.nsIBrowser);
+      browser = aSubject;
     }
 
     let uri = Services.io.newURI(aData);
     if (gBrowser.selectedBrowser !== browser) {
       // Must belong to some other window.
       return;
     }
 
--- a/browser/installer/windows/nsis/shared.nsh
+++ b/browser/installer/windows/nsis/shared.nsh
@@ -458,17 +458,16 @@
   ${AddDisabledDDEHandlerValues} "FirefoxURL$5" "$2" "$8,1" "${AppRegName} URL" \
                                  "true"
   ; An empty string is used for the 4th & 5th params because the following
   ; protocol handlers already have a display name and the additional keys
   ; required for a protocol handler.
   ${AddDisabledDDEHandlerValues} "ftp" "$2" "$8,1" "" ""
   ${AddDisabledDDEHandlerValues} "http" "$2" "$8,1" "" ""
   ${AddDisabledDDEHandlerValues} "https" "$2" "$8,1" "" ""
-  ${AddDisabledDDEHandlerValues} "mailto" "$2" "$8,1" "" ""
 !macroend
 !define SetHandlers "!insertmacro SetHandlers"
 
 ; Adds the HKLM\Software\Clients\StartMenuInternet\Firefox-[pathhash] registry
 ; entries (does not use SHCTX).
 ;
 ; The values for StartMenuInternet are only valid under HKLM and there can only
 ; be one installation registerred under StartMenuInternet per application since
@@ -534,17 +533,16 @@
   WriteRegStr ${RegKey} "$0\Capabilities\FileAssociations" ".xht"   "FirefoxHTML$2"
   WriteRegStr ${RegKey} "$0\Capabilities\FileAssociations" ".xhtml" "FirefoxHTML$2"
 
   WriteRegStr ${RegKey} "$0\Capabilities\StartMenu" "StartMenuInternet" "$1"
 
   WriteRegStr ${RegKey} "$0\Capabilities\URLAssociations" "ftp"    "FirefoxURL$2"
   WriteRegStr ${RegKey} "$0\Capabilities\URLAssociations" "http"   "FirefoxURL$2"
   WriteRegStr ${RegKey} "$0\Capabilities\URLAssociations" "https"  "FirefoxURL$2"
-  WriteRegStr ${RegKey} "$0\Capabilities\URLAssociations" "mailto" "FirefoxURL$2"
 
   ; Registered Application
   WriteRegStr ${RegKey} "Software\RegisteredApplications" "$1" "$0\Capabilities"
 !macroend
 !define SetStartMenuInternet "!insertmacro SetStartMenuInternet"
 
 ; Add registry keys to support the Firefox 32 bit to 64 bit migration. These
 ; registry entries are not removed on uninstall at this time. After the Firefox
@@ -881,21 +879,16 @@
   ${If} "$R9" == "true"
     ${AddDisabledDDEHandlerValues} "http" "$2" "$8,1" "" ""
   ${EndIf}
 
   ${IsHandlerForInstallDir} "https" $R9
   ${If} "$R9" == "true"
     ${AddDisabledDDEHandlerValues} "https" "$2" "$8,1" "" ""
   ${EndIf}
-
-  ${IsHandlerForInstallDir} "mailto" $R9
-  ${If} "$R9" == "true"
-    ${AddDisabledDDEHandlerValues} "mailto" "$2" "$8,1" "" ""
-  ${EndIf}
 !macroend
 !define UpdateProtocolHandlers "!insertmacro UpdateProtocolHandlers"
 
 !ifdef MOZ_MAINTENANCE_SERVICE
 ; Adds maintenance service certificate keys for the install dir.
 ; For the cert to work, it must also be signed by a trusted cert for the user.
 !macro AddMaintCertKeys
   Push $R0
--- a/browser/installer/windows/nsis/uninstaller.nsi
+++ b/browser/installer/windows/nsis/uninstaller.nsi
@@ -300,17 +300,16 @@ Section "Uninstall"
     ${un.SetAppLSPCategories}
   ${EndIf}
 
   ${un.RegCleanAppHandler} "FirefoxURL-$AppUserModelID"
   ${un.RegCleanAppHandler} "FirefoxHTML-$AppUserModelID"
   ${un.RegCleanProtocolHandler} "ftp"
   ${un.RegCleanProtocolHandler} "http"
   ${un.RegCleanProtocolHandler} "https"
-  ${un.RegCleanProtocolHandler} "mailto"
   ${un.RegCleanFileHandler}  ".htm"   "FirefoxHTML-$AppUserModelID"
   ${un.RegCleanFileHandler}  ".html"  "FirefoxHTML-$AppUserModelID"
   ${un.RegCleanFileHandler}  ".shtml" "FirefoxHTML-$AppUserModelID"
   ${un.RegCleanFileHandler}  ".xht"   "FirefoxHTML-$AppUserModelID"
   ${un.RegCleanFileHandler}  ".xhtml" "FirefoxHTML-$AppUserModelID"
   ${un.RegCleanFileHandler}  ".oga"  "FirefoxHTML-$AppUserModelID"
   ${un.RegCleanFileHandler}  ".ogg"  "FirefoxHTML-$AppUserModelID"
   ${un.RegCleanFileHandler}  ".ogv"  "FirefoxHTML-$AppUserModelID"
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -143,16 +143,27 @@
 #include "mozilla/dom/WindowBinding.h"
 #include "mozilla/dom/VRDisplay.h"
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/Preferences.h"
 #include "nsComputedDOMStyle.h"
 #include "nsDOMStringMap.h"
 #include "DOMIntersectionObserver.h"
 
+#include "nsIDOMXULButtonElement.h"
+#include "nsIDOMXULContainerElement.h"
+#include "nsIDOMXULControlElement.h"
+#include "nsIDOMXULMenuListElement.h"
+#include "nsIDOMXULMultSelectCntrlEl.h"
+#include "nsIDOMXULRelatedElement.h"
+#include "nsIDOMXULMultSelectCntrlEl.h"
+#include "nsIDOMXULSelectCntrlEl.h"
+#include "nsIDOMXULSelectCntrlItemEl.h"
+#include "nsIBrowser.h"
+
 #include "nsISpeculativeConnect.h"
 #include "nsIIOService.h"
 
 #include "DOMMatrix.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
@@ -3855,18 +3866,25 @@ bool Element::UpdateIntersectionObservat
   return updated;
 }
 
 template <class T>
 void Element::GetCustomInterface(nsGetterAddRefs<T> aResult) {
   nsCOMPtr<nsISupports> iface = CustomElementRegistry::CallGetCustomInterface(
       this, NS_GET_TEMPLATE_IID(T));
   if (iface) {
-    CallQueryInterface(iface, static_cast<T**>(aResult));
-  }
+    if (NS_SUCCEEDED(CallQueryInterface(iface, static_cast<T**>(aResult)))) {
+      return;
+    }
+  }
+
+  // Otherwise, check the binding manager to see if it implements the interface
+  // for this element.
+  OwnerDoc()->BindingManager()->GetBindingImplementation(
+      this, NS_GET_TEMPLATE_IID(T), aResult);
 }
 
 void Element::ClearServoData(nsIDocument* aDoc) {
   MOZ_ASSERT(aDoc);
   if (HasServoData()) {
     Servo_Element_ClearData(this);
   } else {
     UnsetFlags(kAllServoDescendantBits | NODE_NEEDS_FRAME);
@@ -3926,16 +3944,78 @@ CustomElementDefinition* Element::GetCus
 
 void Element::SetCustomElementDefinition(CustomElementDefinition* aDefinition) {
   CustomElementData* data = GetCustomElementData();
   MOZ_ASSERT(data);
 
   data->SetCustomElementDefinition(aDefinition);
 }
 
+already_AddRefed<nsIDOMXULButtonElement> Element::AsXULButton() {
+  nsCOMPtr<nsIDOMXULButtonElement> value;
+  GetCustomInterface(getter_AddRefs(value));
+  return value.forget();
+}
+
+already_AddRefed<nsIDOMXULContainerElement> Element::AsXULContainer() {
+  nsCOMPtr<nsIDOMXULContainerElement> value;
+  GetCustomInterface(getter_AddRefs(value));
+  return value.forget();
+}
+
+already_AddRefed<nsIDOMXULContainerItemElement> Element::AsXULContainerItem() {
+  nsCOMPtr<nsIDOMXULContainerItemElement> value;
+  GetCustomInterface(getter_AddRefs(value));
+  return value.forget();
+}
+
+already_AddRefed<nsIDOMXULControlElement> Element::AsXULControl() {
+  nsCOMPtr<nsIDOMXULControlElement> value;
+  GetCustomInterface(getter_AddRefs(value));
+  return value.forget();
+}
+
+already_AddRefed<nsIDOMXULMenuListElement> Element::AsXULMenuList() {
+  nsCOMPtr<nsIDOMXULMenuListElement> value;
+  GetCustomInterface(getter_AddRefs(value));
+  return value.forget();
+}
+
+already_AddRefed<nsIDOMXULMultiSelectControlElement>
+Element::AsXULMultiSelectControl() {
+  nsCOMPtr<nsIDOMXULMultiSelectControlElement> value;
+  GetCustomInterface(getter_AddRefs(value));
+  return value.forget();
+}
+
+already_AddRefed<nsIDOMXULRelatedElement> Element::AsXULRelated() {
+  nsCOMPtr<nsIDOMXULRelatedElement> value;
+  GetCustomInterface(getter_AddRefs(value));
+  return value.forget();
+}
+
+already_AddRefed<nsIDOMXULSelectControlElement> Element::AsXULSelectControl() {
+  nsCOMPtr<nsIDOMXULSelectControlElement> value;
+  GetCustomInterface(getter_AddRefs(value));
+  return value.forget();
+}
+
+already_AddRefed<nsIDOMXULSelectControlItemElement>
+Element::AsXULSelectControlItem() {
+  nsCOMPtr<nsIDOMXULSelectControlItemElement> value;
+  GetCustomInterface(getter_AddRefs(value));
+  return value.forget();
+}
+
+already_AddRefed<nsIBrowser> Element::AsBrowser() {
+  nsCOMPtr<nsIBrowser> value;
+  GetCustomInterface(getter_AddRefs(value));
+  return value.forget();
+}
+
 MOZ_DEFINE_MALLOC_SIZE_OF(ServoElementMallocSizeOf)
 MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(ServoElementMallocEnclosingSizeOf)
 
 void Element::AddSizeOfExcludingThis(nsWindowSizes& aSizes,
                                      size_t* aNodeSize) const {
   FragmentOrElement::AddSizeOfExcludingThis(aSizes, aNodeSize);
   *aNodeSize += mAttrs.SizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
 
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -58,16 +58,27 @@ class nsFocusManager;
 class nsGlobalWindowInner;
 class nsGlobalWindowOuter;
 class nsDOMCSSAttributeDeclaration;
 class nsISMILAttr;
 class nsDocument;
 class nsDOMStringMap;
 struct ServoNodeData;
 
+class nsIDOMXULButtonElement;
+class nsIDOMXULContainerElement;
+class nsIDOMXULContainerItemElement;
+class nsIDOMXULControlElement;
+class nsIDOMXULMenuListElement;
+class nsIDOMXULMultiSelectControlElement;
+class nsIDOMXULRelatedElement;
+class nsIDOMXULSelectControlElement;
+class nsIDOMXULSelectControlItemElement;
+class nsIBrowser;
+
 namespace mozilla {
 class DeclarationBlock;
 struct MutationClosureData;
 class TextEditor;
 namespace css {
 struct URLValue;
 }  // namespace css
 namespace dom {
@@ -1562,16 +1573,30 @@ class Element : public FragmentOrElement
   void ClearDataset();
 
   void RegisterIntersectionObserver(DOMIntersectionObserver* aObserver);
   void UnregisterIntersectionObserver(DOMIntersectionObserver* aObserver);
   void UnlinkIntersectionObservers();
   bool UpdateIntersectionObservation(DOMIntersectionObserver* aObserver,
                                      int32_t threshold);
 
+  // A number of methods to cast to various XUL interfaces. They return a
+  // pointer only if the element implements that interface.
+  already_AddRefed<nsIDOMXULButtonElement> AsXULButton();
+  already_AddRefed<nsIDOMXULContainerElement> AsXULContainer();
+  already_AddRefed<nsIDOMXULContainerItemElement> AsXULContainerItem();
+  already_AddRefed<nsIDOMXULControlElement> AsXULControl();
+  already_AddRefed<nsIDOMXULMenuListElement> AsXULMenuList();
+  already_AddRefed<nsIDOMXULMultiSelectControlElement>
+  AsXULMultiSelectControl();
+  already_AddRefed<nsIDOMXULRelatedElement> AsXULRelated();
+  already_AddRefed<nsIDOMXULSelectControlElement> AsXULSelectControl();
+  already_AddRefed<nsIDOMXULSelectControlItemElement> AsXULSelectControlItem();
+  already_AddRefed<nsIBrowser> AsBrowser();
+
  protected:
   /*
    * Named-bools for use with SetAttrAndNotify to make call sites easier to
    * read.
    */
   static const bool kFireMutationEvent = true;
   static const bool kDontFireMutationEvent = false;
   static const bool kNotifyDocumentObservers = true;
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -9899,18 +9899,22 @@ static void AppendNativeAnonymousChildre
 
   aValue.setObject(*array);
   return NS_OK;
 }
 
 /* static */
 bool nsContentUtils::ShouldBlockReservedKeys(WidgetKeyboardEvent* aKeyEvent) {
   nsCOMPtr<nsIPrincipal> principal;
-  nsCOMPtr<nsIBrowser> targetBrowser =
+  nsCOMPtr<Element> targetElement =
       do_QueryInterface(aKeyEvent->mOriginalTarget);
+  nsCOMPtr<nsIBrowser> targetBrowser;
+  if (targetElement) {
+    targetBrowser = targetElement->AsBrowser();
+  }
   bool isRemoteBrowser = false;
   if (targetBrowser) {
     targetBrowser->GetIsRemoteBrowser(&isRemoteBrowser);
   }
 
   if (isRemoteBrowser) {
     targetBrowser->GetContentPrincipal(getter_AddRefs(principal));
   } else {
--- a/dom/base/nsFocusManager.cpp
+++ b/dom/base/nsFocusManager.cpp
@@ -332,17 +332,18 @@ Element* nsFocusManager::GetRedirectedFo
   }
 
 #ifdef MOZ_XUL
   if (aContent->IsXULElement()) {
     if (aContent->IsXULElement(nsGkAtoms::textbox)) {
       return aContent->OwnerDoc()->GetAnonymousElementByAttribute(
           aContent, nsGkAtoms::anonid, NS_LITERAL_STRING("input"));
     } else {
-      nsCOMPtr<nsIDOMXULMenuListElement> menulist = do_QueryInterface(aContent);
+      nsCOMPtr<nsIDOMXULMenuListElement> menulist =
+          aContent->AsElement()->AsXULMenuList();
       if (menulist) {
         RefPtr<Element> inputField;
         menulist->GetInputField(getter_AddRefs(inputField));
         return inputField;
       }
     }
   }
 #endif
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -2365,17 +2365,17 @@ void nsFrameLoader::SetClampScrollPositi
       }
     }
   }
 }
 
 static Tuple<ContentParent*, TabParent*> GetContentParent(Element* aBrowser) {
   using ReturnTuple = Tuple<ContentParent*, TabParent*>;
 
-  nsCOMPtr<nsIBrowser> browser = do_QueryInterface(aBrowser);
+  nsCOMPtr<nsIBrowser> browser = aBrowser ? aBrowser->AsBrowser() : nullptr;
   if (!browser) {
     return ReturnTuple(nullptr, nullptr);
   }
 
   RefPtr<nsFrameLoader> otherLoader;
   browser->GetSameProcessAsFrameLoader(getter_AddRefs(otherLoader));
   if (!otherLoader) {
     return ReturnTuple(nullptr, nullptr);
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -6794,18 +6794,17 @@ void nsGlobalWindowInner::SetBrowserDOMW
   FORWARD_TO_OUTER_OR_THROW(SetBrowserDOMWindowOuter, (aBrowserWindow),
                             aError, );
 }
 
 void nsGlobalWindowInner::NotifyDefaultButtonLoaded(Element& aDefaultButton,
                                                     ErrorResult& aError) {
 #ifdef MOZ_XUL
   // Don't snap to a disabled button.
-  nsCOMPtr<nsIDOMXULControlElement> xulControl =
-      do_QueryInterface(&aDefaultButton);
+  nsCOMPtr<nsIDOMXULControlElement> xulControl = aDefaultButton.AsXULControl();
   if (!xulControl) {
     aError.Throw(NS_ERROR_FAILURE);
     return;
   }
   bool disabled;
   aError = xulControl->GetDisabled(&disabled);
   if (aError.Failed() || disabled) {
     return;
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -157,20 +157,16 @@
 #include "nsCDefaultURIFixup.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventStateManager.h"
 #include "nsIObserverService.h"
 #include "nsFocusManager.h"
 #include "nsIXULWindow.h"
 #include "nsITimedChannel.h"
 #include "nsServiceManagerUtils.h"
-#ifdef MOZ_XUL
-#include "nsIDOMXULControlElement.h"
-#include "nsMenuPopupFrame.h"
-#endif
 #include "mozilla/dom/CustomEvent.h"
 #include "nsIJARChannel.h"
 #include "nsIScreenManager.h"
 #include "nsIEffectiveTLDService.h"
 
 #include "xpcprivate.h"
 
 #ifdef NS_PRINTING
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -157,20 +157,21 @@ bool ThrowInvalidThis(JSContext* aCx, co
 }
 
 bool ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs,
                       bool aSecurityError, prototypes::ID aProtoId) {
   return ThrowInvalidThis(aCx, aArgs, aSecurityError,
                           NamesOfInterfacesWithProtos(aProtoId));
 }
 
-bool ThrowNoSetterArg(JSContext* aCx, prototypes::ID aProtoId) {
+bool ThrowNoSetterArg(JSContext* aCx, const JS::CallArgs& aArgs,
+                      prototypes::ID aProtoId) {
   nsPrintfCString errorMessage("%s attribute setter",
                                NamesOfInterfacesWithProtos(aProtoId));
-  return ThrowErrorMessage(aCx, MSG_MISSING_ARGUMENTS, errorMessage.get());
+  return aArgs.requireAtLeast(aCx, errorMessage.get(), 1);
 }
 
 }  // namespace dom
 
 namespace binding_danger {
 
 template <typename CleanupPolicy>
 struct TErrorResult<CleanupPolicy>::Message {
@@ -3003,17 +3004,17 @@ bool GenericSetter(JSContext* cx, unsign
     nsresult rv = binding_detail::UnwrapObjectInternal<void, true>(
         wrapper, self, protoID, info->depth);
     if (NS_FAILED(rv)) {
       return ThisPolicy::HandleInvalidThis(
           cx, args, rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO, protoID);
     }
   }
   if (args.length() == 0) {
-    return ThrowNoSetterArg(cx, protoID);
+    return ThrowNoSetterArg(cx, args, protoID);
   }
   MOZ_ASSERT(info->type() == JSJitInfo::Setter);
   JSJitSetterOp setter = info->setter;
   if (!setter(cx, obj, self, JSJitSetterCallArgs(args))) {
     return false;
   }
   args.rval().setUndefined();
 #ifdef DEBUG
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -8037,18 +8037,18 @@ class CGMethodCall(CGThing):
             requiredArgs = requiredArgCount(signature)
 
             # Skip required arguments check for maplike/setlike interfaces, as
             # they can have arguments which are not passed, and are treated as
             # if undefined had been explicitly passed.
             if requiredArgs > 0 and not method.isMaplikeOrSetlikeOrIterableMethod():
                 code = fill(
                     """
-                    if (MOZ_UNLIKELY(args.length() < ${requiredArgs})) {
-                      return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "${methodName}");
+                    if (!args.requireAtLeast(cx, "${methodName}", ${requiredArgs})) {
+                      return false;
                     }
                     """,
                     requiredArgs=requiredArgs,
                     methodName=methodName)
                 self.cgRoot.prepend(CGGeneric(code))
             return
 
         # Need to find the right overload
@@ -8380,18 +8380,25 @@ class CGMethodCall(CGThing):
 
         overloadCGThings = []
         overloadCGThings.append(
             CGGeneric("unsigned argcount = std::min(args.length(), %du);\n" %
                       maxArgCount))
         overloadCGThings.append(
             CGSwitch("argcount",
                      argCountCases,
-                     CGGeneric('return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "%s");\n' %
-                               methodName)))
+                     CGGeneric(fill(
+                         """
+                         // Using nsPrintfCString here would require including that
+                         // header.  Let's not worry about it.
+                         nsAutoCString argCountStr;
+                         argCountStr.AppendPrintf("%u", args.length());
+                         return ThrowErrorMessage(cx, MSG_INVALID_OVERLOAD_ARGCOUNT, "${methodName}", argCountStr.get());
+                         """,
+                         methodName=methodName))))
         overloadCGThings.append(
             CGGeneric('MOZ_CRASH("We have an always-returning default case");\n'
                       'return false;\n'))
         self.cgRoot = CGList(overloadCGThings)
 
     def define(self):
         return self.cgRoot.define()
 
@@ -9132,18 +9139,18 @@ class CGStaticSetter(CGAbstractStaticBin
         name = 'set_' + IDLToCIdentifier(attr.identifier.name)
         CGAbstractStaticBindingMethod.__init__(self, descriptor, name)
 
     def generate_code(self):
         nativeName = CGSpecializedSetter.makeNativeName(self.descriptor,
                                                         self.attr)
         checkForArg = CGGeneric(fill(
             """
-            if (args.length() == 0) {
-              return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "${name} setter");
+            if (!args.requireAtLeast(cx, "${name} setter", 1)) {
+              return false;
             }
             """,
             name=self.attr.identifier.name))
         call = CGSetterCall(self.attr.type, nativeName, self.descriptor,
                             self.attr)
         return CGList([checkForArg, call])
 
     def auto_profiler_label(self):
@@ -15354,18 +15361,18 @@ class CGJSImplClass(CGBindingImplClass):
         # XXXbz we could try to get parts of this (e.g. the argument
         # conversions) auto-generated by somehow creating an IDLMethod and
         # adding it to our interface, but we'd still need to special-case the
         # implementation slightly to have it not try to forward to the JS
         # object...
         return fill(
             """
             JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
-            if (args.length() < 2) {
-              return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "${ifaceName}._create");
+            if (!args.requireAtLeast(cx, "${ifaceName}._create", 2)) {
+              return false;
             }
             if (!args[0].isObject()) {
               return ThrowErrorMessage(cx, MSG_NOT_OBJECT, "Argument 1 of ${ifaceName}._create");
             }
             if (!args[1].isObject()) {
               return ThrowErrorMessage(cx, MSG_NOT_OBJECT, "Argument 2 of ${ifaceName}._create");
             }
 
--- a/dom/bindings/Errors.msg
+++ b/dom/bindings/Errors.msg
@@ -18,17 +18,17 @@
  * engine should throw.
  *
  * <FORMAT_STRING> is a string literal, containing <ARGUMENT_COUNT> sequences
  * {X} where X  is an integer representing the argument number that will
  * be replaced with a string value when the error is reported.
  */
 
 MSG_DEF(MSG_INVALID_ENUM_VALUE, 3, JSEXN_TYPEERR, "{0} '{1}' is not a valid value for enumeration {2}.")
-MSG_DEF(MSG_MISSING_ARGUMENTS, 1, JSEXN_TYPEERR, "Not enough arguments to {0}.")
+MSG_DEF(MSG_INVALID_OVERLOAD_ARGCOUNT, 2, JSEXN_TYPEERR, "{1} is not a valid argument count for any overload of {0}.")
 MSG_DEF(MSG_NOT_OBJECT, 1, JSEXN_TYPEERR, "{0} is not an object.")
 MSG_DEF(MSG_NOT_CALLABLE, 1, JSEXN_TYPEERR, "{0} is not callable.")
 MSG_DEF(MSG_NOT_CONSTRUCTOR, 1, JSEXN_TYPEERR, "{0} is not a constructor.")
 MSG_DEF(MSG_DOES_NOT_IMPLEMENT_INTERFACE, 2, JSEXN_TYPEERR, "{0} does not implement interface {1}.")
 MSG_DEF(MSG_METHOD_THIS_DOES_NOT_IMPLEMENT_INTERFACE, 2, JSEXN_TYPEERR, "'{0}' called on an object that does not implement interface {1}.")
 MSG_DEF(MSG_METHOD_THIS_UNWRAPPING_DENIED, 1, JSEXN_TYPEERR, "Permission to call '{0}' denied.")
 MSG_DEF(MSG_THIS_DOES_NOT_IMPLEMENT_INTERFACE, 1, JSEXN_TYPEERR, "\"this\" object does not implement interface {0}.")
 MSG_DEF(MSG_NOT_IN_UNION, 2, JSEXN_TYPEERR, "{0} could not be converted to any of: {1}.")
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -940,17 +940,18 @@ static bool IsAccessKeyTarget(nsIContent
   // For XUL we do visibility checks.
   if (!aFrame) return false;
 
   if (aFrame->IsFocusable()) return true;
 
   if (!aFrame->IsVisibleConsideringAncestors()) return false;
 
   // XUL controls can be activated.
-  nsCOMPtr<nsIDOMXULControlElement> control(do_QueryInterface(aContent));
+  nsCOMPtr<nsIDOMXULControlElement> control =
+      aContent->AsElement()->AsXULControl();
   if (control) return true;
 
   // HTML area, label and legend elements are never focusable, so
   // we need to check for them explicitly before giving up.
   if (aContent->IsAnyOfHTMLElements(nsGkAtoms::area, nsGkAtoms::label,
                                     nsGkAtoms::legend)) {
     return true;
   }
@@ -3012,25 +3013,25 @@ nsresult EventStateManager::PostHandleEv
           // We can't use nsIFrame::IsFocusable() because we want to blur when
           // we click on a visibility: none element.
           // We can't use nsIContent::IsFocusable() because we want to blur when
           // we click on a non-focusable element like a <div>.
           // We have to use |aEvent->mTarget| to not make sure we do not check
           // an anonymous node of the targeted element.
           suppressBlur = (ui->mUserFocus == StyleUserFocus::Ignore);
 
+          nsCOMPtr<Element> element = do_QueryInterface(aEvent->mTarget);
           if (!suppressBlur) {
-            nsCOMPtr<Element> element = do_QueryInterface(aEvent->mTarget);
             suppressBlur =
                 element && element->State().HasState(NS_EVENT_STATE_DISABLED);
           }
 
-          if (!suppressBlur) {
+          if (!suppressBlur && element) {
             nsCOMPtr<nsIDOMXULControlElement> xulControl =
-                do_QueryInterface(aEvent->mTarget);
+                element->AsXULControl();
             if (xulControl) {
               bool disabled;
               xulControl->GetDisabled(&disabled);
               suppressBlur = disabled;
             }
           }
         }
 
--- a/dom/interfaces/xul/nsIDOMXULContainerElement.idl
+++ b/dom/interfaces/xul/nsIDOMXULContainerElement.idl
@@ -1,22 +1,24 @@
 /* -*- 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/. */
 
 #include "nsISupports.idl"
 interface nsIDOMXULContainerElement;
 
+webidl Element;
+
 [scriptable, uuid(800a68c7-b854-4597-a436-3055ce5c5c96)]
 interface nsIDOMXULContainerItemElement : nsISupports
 {
   /**
    * Returns the parent container if any.
    */
-  readonly attribute nsIDOMXULContainerElement parentContainer;
+  readonly attribute Element parentContainer;
 };
 
 [scriptable, uuid(b2bc96b8-31fc-42f4-937a-bd27291af40b)]
 interface nsIDOMXULContainerElement : nsIDOMXULContainerItemElement
 {
 };
 
--- a/dom/interfaces/xul/nsIDOMXULMultSelectCntrlEl.idl
+++ b/dom/interfaces/xul/nsIDOMXULMultSelectCntrlEl.idl
@@ -1,23 +1,24 @@
 /* -*- 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/. */
 
 #include "nsIDOMXULSelectCntrlEl.idl"
 
+webidl Element;
 webidl NodeList;
 
 [scriptable, uuid(40654a10-8204-4f06-9f21-7baa31c7b1dd)]
 interface nsIDOMXULMultiSelectControlElement : nsIDOMXULSelectControlElement
 {
   attribute AString selType;
 
-  attribute nsIDOMXULSelectControlItemElement currentItem;
+  attribute Element currentItem;
   attribute long currentIndex;
 
   readonly attribute NodeList selectedItems;
   
   void addItemToSelection(in nsIDOMXULSelectControlItemElement item);
   void removeItemFromSelection(in nsIDOMXULSelectControlItemElement item);
   void toggleItemSelection(in nsIDOMXULSelectControlItemElement item);
 
@@ -27,10 +28,10 @@ interface nsIDOMXULMultiSelectControlEle
   void selectAll();
   void invertSelection();
   void clearSelection();
 
   // XXX - temporary, pending implementation of scriptable, 
   //       mutable NodeList for selectedItems
   readonly attribute long selectedCount;
   [binaryname(MultiGetSelectedItem)]
-  nsIDOMXULSelectControlItemElement getSelectedItem(in long index);
+  Element getSelectedItem(in long index);
 };
--- a/dom/interfaces/xul/nsIDOMXULSelectCntrlEl.idl
+++ b/dom/interfaces/xul/nsIDOMXULSelectCntrlEl.idl
@@ -1,20 +1,22 @@
 /* -*- 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/. */
 
 #include "nsIDOMXULControlElement.idl"
 interface nsIDOMXULSelectControlItemElement;
 
+webidl Element;
+
 [scriptable, uuid(9bf188a7-d6f9-431b-b5c7-118013998e8b)]
 interface nsIDOMXULSelectControlElement : nsIDOMXULControlElement {
-  attribute nsIDOMXULSelectControlItemElement selectedItem;
+  attribute Element selectedItem;
   attribute long selectedIndex;
 
   attribute AString value;
 
   readonly attribute unsigned long itemCount;
   long getIndexOfItem(in nsIDOMXULSelectControlItemElement item);
-  nsIDOMXULSelectControlItemElement getItemAtIndex(in long index);
+  Element getItemAtIndex(in long index);
 };
 
--- a/dom/interfaces/xul/nsIDOMXULSelectCntrlItemEl.idl
+++ b/dom/interfaces/xul/nsIDOMXULSelectCntrlItemEl.idl
@@ -1,28 +1,30 @@
 /* -*- 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/. */
 
 #include "nsISupports.idl"
 interface nsIDOMXULSelectControlElement;
 
+webidl Element;
+
 [scriptable, uuid(5c6be58f-17df-4750-88a5-4a59ac28adc9)]
 interface nsIDOMXULSelectControlItemElement : nsISupports {
   attribute boolean disabled;
   attribute AString crop;
   attribute AString image;
   attribute AString label;
   attribute AString accessKey;
   attribute AString command;
   
   attribute AString value;
   
   readonly attribute boolean selected;
   
-  readonly attribute nsIDOMXULSelectControlElement control;
+  readonly attribute Element control;
   
   // XXX defined in XULElement, but should be defined here
   // void doCommand();
 };
 
 
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -526,17 +526,18 @@ mozilla::ipc::IPCResult TabParent::RecvS
   NS_ENSURE_TRUE(xulWin, IPC_OK());
   xulWin->SizeShellToWithLimit(width, height, aShellItemWidth,
                                aShellItemHeight);
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult TabParent::RecvDropLinks(nsTArray<nsString>&& aLinks) {
-  nsCOMPtr<nsIBrowser> browser = do_QueryInterface(mFrameElement);
+  nsCOMPtr<nsIBrowser> browser =
+      mFrameElement ? mFrameElement->AsBrowser() : nullptr;
   if (browser) {
     // Verify that links have not been modified by the child. If links have
     // not been modified then it's safe to load those links using the
     // SystemPrincipal. If they have been modified by web content, then
     // we use a NullPrincipal which still allows to load web links.
     bool loadUsingSystemPrincipal = true;
     if (aLinks.Length() != mVerifyDropLinks.Length()) {
       loadUsingSystemPrincipal = false;
@@ -1904,17 +1905,18 @@ mozilla::ipc::IPCResult TabParent::RecvR
   RefPtr<Element> element = mFrameElement;
   fm->SetFocus(element, flags);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult TabParent::RecvEnableDisableCommands(
     const nsString& aAction, nsTArray<nsCString>&& aEnabledCommands,
     nsTArray<nsCString>&& aDisabledCommands) {
-  nsCOMPtr<nsIBrowser> browser = do_QueryInterface(mFrameElement);
+  nsCOMPtr<nsIBrowser> browser =
+      mFrameElement ? mFrameElement->AsBrowser() : nullptr;
   bool isRemoteBrowser = false;
   if (browser) {
     browser->GetIsRemoteBrowser(&isRemoteBrowser);
   }
   if (isRemoteBrowser) {
     UniquePtr<const char*[]> enabledCommands, disabledCommands;
 
     if (aEnabledCommands.Length()) {
@@ -3288,17 +3290,18 @@ mozilla::ipc::IPCResult TabParent::RecvL
 
   widget->LookUpDictionary(aText, aFontRangeArray, aIsVertical,
                            aPoint - GetChildProcessOffset());
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult TabParent::RecvShowCanvasPermissionPrompt(
     const nsCString& aFirstPartyURI) {
-  nsCOMPtr<nsIBrowser> browser = do_QueryInterface(mFrameElement);
+  nsCOMPtr<nsIBrowser> browser =
+      mFrameElement ? mFrameElement->AsBrowser() : nullptr;
   if (!browser) {
     // If the tab is being closed, the browser may not be available.
     // In this case we can ignore the request.
     return IPC_OK();
   }
   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
   if (!os) {
     return IPC_FAIL_NO_REASON(this);
--- a/dom/xul/nsXULElement.cpp
+++ b/dom/xul/nsXULElement.cpp
@@ -291,26 +291,16 @@ 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
-
-  nsCOMPtr<nsISupports> iface =
-      CustomElementRegistry::CallGetCustomInterface(this, aIID);
-  if (iface) {
-    iface->QueryInterface(aIID, aInstancePtr);
-    if (*aInstancePtr) {
-      return NS_OK;
-    }
-  }
-
 NS_INTERFACE_MAP_END_INHERITING(nsStyledElement)
 
 //----------------------------------------------------------------------
 // nsINode interface
 
 nsresult nsXULElement::Clone(mozilla::dom::NodeInfo* aNodeInfo,
                              nsINode** aResult) const {
   *aResult = nullptr;
@@ -432,17 +422,17 @@ bool nsXULElement::IsFocusableInternal(i
   // or if it's a remote target, since the remote target must handle
   // the focus.
   if (aWithMouse && IsNonList(mNodeInfo) &&
       !EventStateManager::IsRemoteTarget(this)) {
     return false;
   }
 #endif
 
-  nsCOMPtr<nsIDOMXULControlElement> xulControl = do_QueryObject(this);
+  nsCOMPtr<nsIDOMXULControlElement> xulControl = AsXULControl();
   if (xulControl) {
     // a disabled element cannot be focused and is not part of the tab order
     bool disabled;
     xulControl->GetDisabled(&disabled);
     if (disabled) {
       if (aTabIndex) *aTabIndex = -1;
       return false;
     }
@@ -538,25 +528,23 @@ bool nsXULElement::PerformAccesskey(bool
   if (elm) {
     // Define behavior for each type of XUL element.
     if (!content->IsXULElement(nsGkAtoms::toolbarbutton)) {
       nsIFocusManager* fm = nsFocusManager::GetFocusManager();
       if (fm) {
         nsCOMPtr<Element> elementToFocus;
         // for radio buttons, focus the radiogroup instead
         if (content->IsXULElement(nsGkAtoms::radio)) {
-          nsCOMPtr<nsIDOMXULSelectControlItemElement> controlItem(
-              do_QueryInterface(content));
+          nsCOMPtr<nsIDOMXULSelectControlItemElement> controlItem =
+              content->AsXULSelectControlItem();
           if (controlItem) {
             bool disabled;
             controlItem->GetDisabled(&disabled);
             if (!disabled) {
-              nsCOMPtr<nsIDOMXULSelectControlElement> selectControl;
-              controlItem->GetControl(getter_AddRefs(selectControl));
-              elementToFocus = do_QueryInterface(selectControl);
+              controlItem->GetControl(getter_AddRefs(elementToFocus));
             }
           }
         } else {
           elementToFocus = content;
         }
         if (elementToFocus) {
           fm->SetFocus(elementToFocus, nsIFocusManager::FLAG_BYKEY);
 
--- a/js/public/CallArgs.h
+++ b/js/public/CallArgs.h
@@ -241,16 +241,23 @@ class MOZ_STACK_CLASS CallArgsBase {
    * so eventually.)  You don't need to use or change this if your method
    * fails.
    */
   MutableHandleValue rval() const {
     this->setUsedRval();
     return MutableHandleValue::fromMarkedLocation(&argv_[-2]);
   }
 
+  /*
+   * Returns true if there are at least |required| arguments passed in. If
+   * false, it reports an error message on the context.
+   */
+  JS_PUBLIC_API inline bool requireAtLeast(JSContext* cx, const char* fnname,
+                                           unsigned required) const;
+
  public:
   // These methods are publicly exposed, but they are *not* to be used when
   // implementing a JSNative method and encapsulating access to |vp| within
   // it.  You probably don't want to use these!
 
   void setCallee(const Value& aCalleev) const {
     this->clearUsedRval();
     argv_[-2] = aCalleev;
@@ -306,23 +313,39 @@ class MOZ_STACK_CLASS CallArgs
       MOZ_ASSERT(ValueIsNotGray(argv[i]));
     }
 #endif
     return args;
   }
 
  public:
   /*
-   * Returns true if there are at least |required| arguments passed in. If
-   * false, it reports an error message on the context.
+   * Helper for requireAtLeast to report the actual exception.  Public
+   * so we can call it from CallArgsBase and not need multiple
+   * per-template instantiations of it.
    */
-  JS_PUBLIC_API bool requireAtLeast(JSContext* cx, const char* fnname,
-                                    unsigned required) const;
+  static JS_PUBLIC_API void reportMoreArgsNeeded(JSContext* cx,
+                                                 const char* fnname,
+                                                 unsigned required,
+                                                 unsigned actual);
 };
 
+namespace detail {
+template <class WantUsedRval>
+JS_PUBLIC_API inline bool CallArgsBase<WantUsedRval>::requireAtLeast(
+    JSContext* cx, const char* fnname, unsigned required) const {
+  if (MOZ_LIKELY(required <= length())) {
+    return true;
+  }
+
+  CallArgs::reportMoreArgsNeeded(cx, fnname, required, length());
+  return false;
+}
+}  // namespace detail
+
 MOZ_ALWAYS_INLINE CallArgs CallArgsFromVp(unsigned argc, Value* vp) {
   return CallArgs::create(argc, vp + 2, vp[1].isMagic(JS_IS_CONSTRUCTING));
 }
 
 // This method is only intended for internal use in SpiderMonkey.  We may
 // eventually move it to an internal header.  Embedders should use
 // JS::CallArgsFromVp!
 MOZ_ALWAYS_INLINE CallArgs CallArgsFromSp(unsigned stackSlots, Value* sp,
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -688,20 +688,17 @@ JSString* js::ObjectClassToString(JSCont
   }
 
   return sb.finishAtom();
 }
 
 static bool obj_setPrototypeOf(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 
-  if (args.length() < 2) {
-    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
-                              JSMSG_MORE_ARGS_NEEDED, "Object.setPrototypeOf",
-                              "1", "");
+  if (!args.requireAtLeast(cx, "Object.setPrototypeOf", 2)) {
     return false;
   }
 
   /* Step 1-2. */
   if (args[0].isNullOrUndefined()) {
     JS_ReportErrorNumberASCII(
         cx, GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
         args[0].isNull() ? "null" : "undefined", "object");
@@ -1110,20 +1107,17 @@ static bool ObjectDefineProperties(JSCon
   return true;
 }
 
 // ES6 draft rev34 (2015/02/20) 19.1.2.2 Object.create(O [, Properties])
 bool js::obj_create(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 
   // Step 1.
-  if (args.length() == 0) {
-    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
-                              JSMSG_MORE_ARGS_NEEDED, "Object.create", "0",
-                              "s");
+  if (!args.requireAtLeast(cx, "Object.create", 1)) {
     return false;
   }
 
   if (!args[0].isObjectOrNull()) {
     UniqueChars bytes =
         DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, args[0], nullptr);
     if (!bytes) {
       return false;
@@ -1892,20 +1886,17 @@ static bool obj_defineProperties(JSConte
   /* Steps 1 and 7. */
   RootedObject obj(cx);
   if (!GetFirstArgumentAsObject(cx, args, "Object.defineProperties", &obj)) {
     return false;
   }
   args.rval().setObject(*obj);
 
   /* Step 2. */
-  if (args.length() < 2) {
-    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
-                              JSMSG_MORE_ARGS_NEEDED, "Object.defineProperties",
-                              "0", "s");
+  if (!args.requireAtLeast(cx, "Object.defineProperties", 2)) {
     return false;
   }
 
   /* Steps 3-6. */
   return ObjectDefineProperties(cx, obj, args[1]);
 }
 
 // ES6 20141014 draft 19.1.2.15 Object.preventExtensions(O)
--- a/js/src/builtin/ReflectParse.cpp
+++ b/js/src/builtin/ReflectParse.cpp
@@ -3433,20 +3433,17 @@ bool ASTSerializer::functionBody(ParseNo
   }
 
   return builder.blockStatement(elts, pos, dst);
 }
 
 static bool reflect_parse(JSContext* cx, uint32_t argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 
-  if (args.length() < 1) {
-    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
-                              JSMSG_MORE_ARGS_NEEDED, "Reflect.parse", "0",
-                              "s");
+  if (!args.requireAtLeast(cx, "Reflect.parse", 1)) {
     return false;
   }
 
   RootedString src(cx, ToString<CanGC>(cx, args[0]));
   if (!src) {
     return false;
   }
 
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -3534,19 +3534,17 @@ struct FindPathHandler {
   MutableHandle<GCVector<Value>> nodes;
   Vector<EdgeName>& edges;
 };
 
 }  // namespace heaptools
 
 static bool FindPath(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
-  if (argc < 2) {
-    JS_ReportErrorNumberASCII(cx, GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
-                              "findPath", "1", "");
+  if (!args.requireAtLeast(cx, "findPath", 2)) {
     return false;
   }
 
   // We don't ToString non-objects given as 'start' or 'target', because this
   // test is all about object identity, and ToString doesn't preserve that.
   // Non-GCThing endpoints don't make much sense.
   if (!args[0].isObject() && !args[0].isString() && !args[0].isSymbol()) {
     ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, args[0],
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -219,20 +219,17 @@ uint32_t ScalarTypeDescr::alignment(Type
     case Scalar::MaxTypedArrayViewType:
       break;
   }
   MOZ_CRASH("Invalid type");
 }
 
 bool ScalarTypeDescr::call(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
-  if (args.length() < 1) {
-    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
-                              JSMSG_MORE_ARGS_NEEDED,
-                              args.callee().getClass()->name, "0", "s");
+  if (!args.requireAtLeast(cx, args.callee().getClass()->name, 1)) {
     return false;
   }
 
   Rooted<ScalarTypeDescr*> descr(cx, &args.callee().as<ScalarTypeDescr>());
   ScalarTypeDescr::Type type = descr->type();
 
   double number;
   if (!ToNumber(cx, args[0], &number)) {
@@ -365,20 +362,17 @@ uint32_t ReferenceTypeDescr::alignment(T
 
 bool js::ReferenceTypeDescr::call(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 
   MOZ_ASSERT(args.callee().is<ReferenceTypeDescr>());
   Rooted<ReferenceTypeDescr*> descr(cx,
                                     &args.callee().as<ReferenceTypeDescr>());
 
-  if (args.length() < 1) {
-    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
-                              JSMSG_MORE_ARGS_NEEDED, descr->typeName(), "0",
-                              "s");
+  if (!args.requireAtLeast(cx, descr->typeName(), 1)) {
     return false;
   }
 
   switch (descr->type()) {
     case ReferenceType::TYPE_ANY:
       args.rval().set(args[0]);
       return true;
 
@@ -610,19 +604,17 @@ bool ArrayMetaTypeDescr::construct(JSCon
 
   if (!ThrowIfNotConstructing(cx, args, "ArrayType")) {
     return false;
   }
 
   RootedObject arrayTypeGlobal(cx, &args.callee());
 
   // Expect two arguments. The first is a type object, the second is a length.
-  if (args.length() < 2) {
-    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
-                              JSMSG_MORE_ARGS_NEEDED, "ArrayType", "1", "");
+  if (!args.requireAtLeast(cx, "ArrayType", 2)) {
     return false;
   }
 
   if (!args[0].isObject() || !args[0].toObject().is<TypeDescr>()) {
     ReportCannotConvertTo(cx, args[0], "ArrayType element specifier");
     return false;
   }
 
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -1505,16 +1505,27 @@ void BytecodeEmitter::tellDebuggerAboutC
 
   // Lazy scripts are never top level (despite always being invoked with a
   // nullptr parent), and so the hook should never be fired.
   if (emitterMode != LazyFunction && !parent) {
     Debugger::onNewScript(cx, script);
   }
 }
 
+void BytecodeEmitter::reportNeedMoreArgsError(ParseNode* pn,
+                                              const char* errorName,
+                                              const char* requiredArgs,
+                                              const char* pluralizer,
+                                              const ListNode* argsList) {
+  char actualArgsStr[40];
+  SprintfLiteral(actualArgsStr, "%u", argsList->count());
+  reportError(pn, JSMSG_MORE_ARGS_NEEDED, errorName, requiredArgs, pluralizer,
+              actualArgsStr);
+}
+
 void BytecodeEmitter::reportError(ParseNode* pn, unsigned errorNumber, ...) {
   MOZ_ASSERT_IF(!pn, this->scriptStartOffsetSet);
   uint32_t offset = pn ? pn->pn_pos.begin : this->scriptStartOffset;
 
   va_list args;
   va_start(args, errorNumber);
 
   parser->errorReporter().errorAtVA(offset, errorNumber, &args);
@@ -6814,17 +6825,17 @@ bool BytecodeEmitter::emitSelfHostedCall
   // argc is set to the amount of actually emitted args and the
   // emitting of args below is disabled by setting emitArgs to false.
   NameNode* calleeNode = &callNode->left()->as<NameNode>();
   ListNode* argsList = &callNode->right()->as<ListNode>();
 
   const char* errorName = SelfHostedCallFunctionName(calleeNode->name(), cx);
 
   if (argsList->count() < 2) {
-    reportError(callNode, JSMSG_MORE_ARGS_NEEDED, errorName, "2", "s");
+    reportNeedMoreArgsError(calleeNode, errorName, "2", "s", argsList);
     return false;
   }
 
   JSOp callOp = callNode->getOp();
   if (callOp != JSOP_CALL) {
     reportError(callNode, JSMSG_NOT_CONSTRUCTOR, errorName);
     return false;
   }
@@ -6887,17 +6898,17 @@ bool BytecodeEmitter::emitSelfHostedCall
   return true;
 }
 
 bool BytecodeEmitter::emitSelfHostedResumeGenerator(BinaryNode* callNode) {
   ListNode* argsList = &callNode->right()->as<ListNode>();
 
   // Syntax: resumeGenerator(gen, value, 'next'|'throw'|'return')
   if (argsList->count() != 3) {
-    reportError(callNode, JSMSG_MORE_ARGS_NEEDED, "resumeGenerator", "1", "s");
+    reportNeedMoreArgsError(callNode, "resumeGenerator", "3", "s", argsList);
     return false;
   }
 
   ParseNode* genNode = argsList->head();
   if (!emitTree(genNode)) {
     return false;
   }
 
@@ -6928,17 +6939,17 @@ bool BytecodeEmitter::emitSelfHostedForc
   }
   return true;
 }
 
 bool BytecodeEmitter::emitSelfHostedAllowContentIter(BinaryNode* callNode) {
   ListNode* argsList = &callNode->right()->as<ListNode>();
 
   if (argsList->count() != 1) {
-    reportError(callNode, JSMSG_MORE_ARGS_NEEDED, "allowContentIter", "1", "");
+    reportNeedMoreArgsError(callNode, "allowContentIter", "1", "", argsList);
     return false;
   }
 
   // We're just here as a sentinel. Pass the value through directly.
   return emitTree(argsList->head());
 }
 
 bool BytecodeEmitter::emitSelfHostedDefineDataProperty(BinaryNode* callNode) {
@@ -6967,17 +6978,17 @@ bool BytecodeEmitter::emitSelfHostedDefi
   // value.
   return emit1(JSOP_INITELEM);
 }
 
 bool BytecodeEmitter::emitSelfHostedHasOwn(BinaryNode* callNode) {
   ListNode* argsList = &callNode->right()->as<ListNode>();
 
   if (argsList->count() != 2) {
-    reportError(callNode, JSMSG_MORE_ARGS_NEEDED, "hasOwn", "2", "");
+    reportNeedMoreArgsError(callNode, "hasOwn", "2", "s", argsList);
     return false;
   }
 
   ParseNode* idNode = argsList->head();
   if (!emitTree(idNode)) {
     return false;
   }
 
@@ -6988,17 +6999,17 @@ bool BytecodeEmitter::emitSelfHostedHasO
 
   return emit1(JSOP_HASOWN);
 }
 
 bool BytecodeEmitter::emitSelfHostedGetPropertySuper(BinaryNode* callNode) {
   ListNode* argsList = &callNode->right()->as<ListNode>();
 
   if (argsList->count() != 3) {
-    reportError(callNode, JSMSG_MORE_ARGS_NEEDED, "getPropertySuper", "3", "");
+    reportNeedMoreArgsError(callNode, "getPropertySuper", "3", "s", argsList);
     return false;
   }
 
   ParseNode* objNode = argsList->head();
   ParseNode* idNode = objNode->pn_next;
   ParseNode* receiverNode = idNode->pn_next;
 
   if (!emitTree(receiverNode)) {
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -257,16 +257,25 @@ struct MOZ_STACK_CLASS BytecodeEmitter {
  private:
   // Internal constructor, for delegation use only.
   BytecodeEmitter(BytecodeEmitter* parent, SharedContext* sc,
                   HandleScript script, Handle<LazyScript*> lazyScript,
                   uint32_t lineNum, EmitterMode emitterMode);
 
   void initFromBodyPosition(TokenPos bodyPosition);
 
+  /*
+   * Helper for reporting that we have insufficient args.  pluralizer
+   * should be "s" if requiredArgs is anything other than "1" and ""
+   * if requiredArgs is "1".
+   */
+  void reportNeedMoreArgsError(ParseNode* pn, const char* errorName,
+                               const char* requiredArgs, const char* pluralizer,
+                               const ListNode* argsList);
+
  public:
   BytecodeEmitter(BytecodeEmitter* parent, BCEParserHandle* parser,
                   SharedContext* sc, HandleScript script,
                   Handle<LazyScript*> lazyScript, uint32_t lineNum,
                   EmitterMode emitterMode = Normal);
 
   BytecodeEmitter(BytecodeEmitter* parent, const EitherParser& parser,
                   SharedContext* sc, HandleScript script,
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -1013,17 +1013,19 @@ const char gc::ZealModeHelpText[] =
     "    21: (YieldBeforeSweepingObjects) Incremental GC in two slices that "
     "yields\n"
     "        before sweeping foreground finalized objects\n"
     "    22: (YieldBeforeSweepingNonObjects) Incremental GC in two slices that "
     "yields\n"
     "        before sweeping non-object GC things\n"
     "    23: (YieldBeforeSweepingShapeTrees) Incremental GC in two slices that "
     "yields\n"
-    "        before sweeping shape trees\n";
+    "        before sweeping shape trees\n"
+    "    24: (CheckWeakMapMarking) Check weak map marking invariants after "
+    "every GC\n";
 
 // The set of zeal modes that control incremental slices. These modes are
 // mutually exclusive.
 static const mozilla::EnumSet<ZealMode> IncrementalSliceZealModes = {
     ZealMode::YieldBeforeMarking,
     ZealMode::YieldBeforeSweeping,
     ZealMode::IncrementalMultipleSlices,
     ZealMode::YieldBeforeSweepingAtoms,
@@ -5211,16 +5213,36 @@ void js::NotifyGCPostSwap(JSObject* a, J
   if (removedFlags & JS_GC_SWAP_OBJECT_A_REMOVED) {
     DelayCrossCompartmentGrayMarking(b);
   }
   if (removedFlags & JS_GC_SWAP_OBJECT_B_REMOVED) {
     DelayCrossCompartmentGrayMarking(a);
   }
 }
 
+static inline void MaybeCheckWeakMapMarking(GCRuntime* gc)
+{
+#if defined(JS_GC_ZEAL) || defined(DEBUG)
+
+  bool shouldCheck;
+#if defined(DEBUG)
+  shouldCheck = true;
+#else
+  shouldCheck = gc->hasZealMode(ZealMode::CheckWeakMapMarking);
+#endif
+
+  if (shouldCheck) {
+    for (SweepGroupZonesIter zone(gc->rt); !zone.done(); zone.next()) {
+      MOZ_RELEASE_ASSERT(WeakMapBase::checkMarkingForZone(zone));
+    }
+  }
+
+#endif
+}
+
 IncrementalProgress GCRuntime::endMarkingSweepGroup(FreeOp* fop,
                                                     SliceBudget& budget) {
   gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::SWEEP_MARK);
 
   // Mark any incoming black pointers from previously swept compartments
   // whose referents are not marked. This can occur when gray cells become
   // black by the action of UnmarkGray.
   markIncomingCrossCompartmentPointers(MarkColor::Black);
@@ -5248,16 +5270,18 @@ IncrementalProgress GCRuntime::endMarkin
   for (SweepGroupZonesIter zone(rt); !zone.done(); zone.next()) {
     zone->changeGCState(Zone::MarkGray, Zone::Mark);
   }
   MOZ_ASSERT(marker.isDrained());
 
   // We must not yield after this point before we start sweeping the group.
   safeToYield = false;
 
+  MaybeCheckWeakMapMarking(this);
+
   return Finished;
 }
 
 // Causes the given WeakCache to be swept when run.
 class ImmediateSweepWeakCacheTask
     : public GCParallelTaskHelper<ImmediateSweepWeakCacheTask> {
   JS::detail::WeakCacheBase& cache;
 
--- a/js/src/gc/GCEnum.h
+++ b/js/src/gc/GCEnum.h
@@ -68,17 +68,18 @@ enum class AbortReason {
   D(CheckHeapAfterGC, 15)              \
   D(CheckNursery, 16)                  \
   D(YieldBeforeSweepingAtoms, 17)      \
   D(CheckGrayMarking, 18)              \
   D(YieldBeforeSweepingCaches, 19)     \
   D(YieldBeforeSweepingTypes, 20)      \
   D(YieldBeforeSweepingObjects, 21)    \
   D(YieldBeforeSweepingNonObjects, 22) \
-  D(YieldBeforeSweepingShapeTrees, 23)
+  D(YieldBeforeSweepingShapeTrees, 23) \
+  D(CheckWeakMapMarking, 24)
 
 enum class ZealMode {
 #define ZEAL_MODE(name, value) name = value,
   JS_FOR_EACH_ZEAL_MODE(ZEAL_MODE)
 #undef ZEAL_MODE
       Count,
   Limit = Count - 1
 };
--- a/js/src/gc/Verifier.cpp
+++ b/js/src/gc/Verifier.cpp
@@ -9,28 +9,31 @@
 #include "mozilla/Sprintf.h"
 
 #ifdef MOZ_VALGRIND
 #include <valgrind/memcheck.h>
 #endif
 
 #include "gc/GCInternals.h"
 #include "gc/PublicIterators.h"
+#include "gc/WeakMap.h"
 #include "gc/Zone.h"
 #include "js/HashTable.h"
 #include "vm/JSContext.h"
 
 #include "gc/ArenaList-inl.h"
 #include "gc/GC-inl.h"
 #include "gc/Marking-inl.h"
 #include "vm/JSContext-inl.h"
 
 using namespace js;
 using namespace js::gc;
 
+using mozilla::DebugOnly;
+
 #ifdef JS_GC_ZEAL
 
 /*
  * Write barrier verification
  *
  * The next few functions are for write barrier verification.
  *
  * The VerifyBarriers function is a shorthand. It checks if a verification phase
@@ -442,17 +445,51 @@ void js::gc::GCRuntime::finishVerifier()
   if (verifyPreData) {
     js_delete(verifyPreData.ref());
     verifyPreData = nullptr;
   }
 }
 
 #endif /* JS_GC_ZEAL */
 
-#if defined(JSGC_HASH_TABLE_CHECKS) || defined(DEBUG)
+#if defined(JS_GC_ZEAL) || defined(DEBUG)
+
+// Like gc::MarkColor but allows the possibility of the cell being
+// unmarked. Order is important here, with white being 'least marked'
+// and black being 'most marked'.
+enum class CellColor : uint8_t { White = 0, Gray = 1, Black = 2 };
+
+static CellColor GetCellColor(Cell* cell) {
+  if (cell->isMarkedBlack()) {
+    return CellColor::Black;
+  }
+
+  if (cell->isMarkedGray()) {
+    return CellColor::Gray;
+  }
+
+  return CellColor::White;
+}
+
+static const char* CellColorName(CellColor color) {
+  switch (color) {
+    case CellColor::White:
+      return "white";
+    case CellColor::Black:
+      return "black";
+    case CellColor::Gray:
+      return "gray";
+    default:
+      MOZ_CRASH("Unexpected cell color");
+  }
+}
+
+static const char* GetCellColorName(Cell* cell) {
+  return CellColorName(GetCellColor(cell));
+}
 
 class HeapCheckTracerBase : public JS::CallbackTracer {
  public:
   explicit HeapCheckTracerBase(JSRuntime* rt, WeakMapTraceKind weakTraceKind);
   bool traceHeap(AutoTraceSession& session);
   virtual void checkCell(Cell* cell) = 0;
 
  protected:
@@ -554,26 +591,16 @@ bool HeapCheckTracerBase::traceHeap(Auto
       stack.back().processed = true;
       TraceChildren(this, item.thing);
     }
   }
 
   return !oom;
 }
 
-static const char* GetCellColorName(Cell* cell) {
-  if (cell->isMarkedBlack()) {
-    return "black";
-  }
-  if (cell->isMarkedGray()) {
-    return "gray";
-  }
-  return "white";
-}
-
 void HeapCheckTracerBase::dumpCellInfo(Cell* cell) {
   auto kind = cell->getTraceKind();
   JSObject* obj =
       kind == JS::TraceKind::Object ? static_cast<JSObject*>(cell) : nullptr;
 
   fprintf(stderr, "%s %s", GetCellColorName(cell), GCTraceKindToAscii(kind));
   if (obj) {
     fprintf(stderr, " %s", obj->getClass()->name);
@@ -592,20 +619,16 @@ void HeapCheckTracerBase::dumpCellPath()
     fprintf(stderr, "  from ");
     dumpCellInfo(cell);
     fprintf(stderr, " %s edge\n", name);
     name = parent.name;
   }
   fprintf(stderr, "  from root %s\n", name);
 }
 
-#endif  // defined(JSGC_HASH_TABLE_CHECKS) || defined(DEBUG)
-
-#ifdef JSGC_HASH_TABLE_CHECKS
-
 class CheckHeapTracer final : public HeapCheckTracerBase {
  public:
   enum GCType { Moving, NonMoving };
 
   explicit CheckHeapTracer(JSRuntime* rt, GCType type);
   void check(AutoTraceSession& session);
 
  private:
@@ -651,20 +674,16 @@ void js::gc::CheckHeapAfterGC(JSRuntime*
   } else {
     gcType = CheckHeapTracer::GCType::NonMoving;
   }
 
   CheckHeapTracer tracer(rt, gcType);
   tracer.check(session);
 }
 
-#endif /* JSGC_HASH_TABLE_CHECKS */
-
-#if defined(JS_GC_ZEAL) || defined(DEBUG)
-
 class CheckGrayMarkingTracer final : public HeapCheckTracerBase {
  public:
   explicit CheckGrayMarkingTracer(JSRuntime* rt);
   bool check(AutoTraceSession& session);
 
  private:
   void checkCell(Cell* cell) override;
 };
@@ -715,9 +734,94 @@ JS_FRIEND_API bool js::CheckGrayMarkingS
 
   gcstats::AutoPhase ap(rt->gc.stats(), gcstats::PhaseKind::TRACE_HEAP);
   AutoTraceSession session(rt);
   CheckGrayMarkingTracer tracer(rt);
 
   return tracer.check(session);
 }
 
+static Zone* GetCellZone(Cell* cell) {
+  if (cell->is<JSObject>()) {
+    return cell->as<JSObject>()->zone();
+  }
+
+  return cell->asTenured().zone();
+}
+
+static JSObject* MaybeGetDelegate(Cell* cell) {
+  if (!cell->is<JSObject>()) {
+    return nullptr;
+  }
+
+  JSObject* object = cell->as<JSObject>();
+  JSWeakmapKeyDelegateOp op = object->getClass()->extWeakmapKeyDelegateOp();
+  if (!op) {
+    return nullptr;
+  }
+
+  JS::AutoSuppressGCAnalysis nogc; // Calling the delegate op cannot GC.
+  return op(object);
+}
+
+bool js::gc::CheckWeakMapEntryMarking(const WeakMapBase* map, Cell* key,
+                                      Cell* value) {
+  Zone* zone = map->zone();
+
+  JSObject* object = map->memberOf;
+  MOZ_ASSERT_IF(object, object->zone() == zone);
+
+  // Debugger weak maps can have keys in different zones.
+  DebugOnly<Zone*> keyZone = GetCellZone(key);
+  MOZ_ASSERT_IF(!map->allowKeysInOtherZones(),
+                keyZone == zone || keyZone->isAtomsZone());
+
+  DebugOnly<Zone*> valueZone = GetCellZone(value);
+  MOZ_ASSERT(valueZone == zone || valueZone->isAtomsZone());
+
+  // We may not know the color of the map, but we know that it's
+  // alive so it must at least be marked gray.
+  CellColor mapColor = object ? GetCellColor(object) : CellColor::Gray;
+
+  CellColor keyColor = GetCellColor(key);
+  CellColor valueColor = GetCellColor(value);
+
+  if (valueColor < Min(mapColor, keyColor)) {
+    fprintf(stderr, "WeakMap value is less marked than map and key\n");
+    fprintf(stderr, "(map %p is %s, key %p is %s, value %p is %s)\n", map,
+            CellColorName(mapColor), key, CellColorName(keyColor), value,
+            CellColorName(valueColor));
+    return false;
+  }
+
+  // Debugger weak maps map have keys in zones that are not or are
+  // no longer collecting. We can't make guarantees about the mark
+  // state of these keys.
+  if (map->allowKeysInOtherZones() &&
+      !(keyZone->isGCMarking() || keyZone->isGCSweeping())) {
+    return true;
+  }
+
+  JSObject* delegate = MaybeGetDelegate(key);
+  if (!delegate) {
+    return true;
+  }
+
+  CellColor delegateColor;
+  if (delegate->zone()->isGCMarking() || delegate->zone()->isGCSweeping()) {
+    delegateColor = GetCellColor(delegate);
+  } else {
+    // IsMarked() assumes cells in uncollected zones are marked.
+    delegateColor = CellColor::Black;
+  }
+
+  if (keyColor < Min(mapColor, delegateColor)) {
+    fprintf(stderr, "WeakMap key is less marked than map and delegate\n");
+    fprintf(stderr, "(map %p is %s, delegate %p is %s, key %p is %s)\n", map,
+            CellColorName(mapColor), delegate, CellColorName(delegateColor),
+            key, CellColorName(keyColor));
+    return false;
+  }
+
+  return true;
+}
+
 #endif  // defined(JS_GC_ZEAL) || defined(DEBUG)
--- a/js/src/gc/WeakMap-inl.h
+++ b/js/src/gc/WeakMap-inl.h
@@ -242,11 +242,28 @@ void WeakMap<K, V>::assertEntriesNotAbou
     K k(r.front().key());
     MOZ_ASSERT(!gc::IsAboutToBeFinalized(&k));
     MOZ_ASSERT(!gc::IsAboutToBeFinalized(&r.front().value()));
     MOZ_ASSERT(k == r.front().key());
   }
 }
 #endif
 
+#ifdef JS_GC_ZEAL
+template <class K, class V>
+bool WeakMap<K, V>::checkMarking() const {
+  bool ok = true;
+  for (Range r = Base::all(); !r.empty(); r.popFront()) {
+    gc::Cell* key = gc::ToMarkable(r.front().key());
+    gc::Cell* value = gc::ToMarkable(r.front().value());
+    if (key && value) {
+      if (!gc::CheckWeakMapEntryMarking(this, key, value)) {
+        ok = false;
+      }
+    }
+  }
+  return ok;
+}
+#endif
+
 } /* namespace js */
 
 #endif /* gc_WeakMap_inl_h */
--- a/js/src/gc/WeakMap.cpp
+++ b/js/src/gc/WeakMap.cpp
@@ -40,16 +40,32 @@ void WeakMapBase::unmarkZone(JS::Zone* z
 void WeakMapBase::traceZone(JS::Zone* zone, JSTracer* tracer) {
   MOZ_ASSERT(tracer->weakMapAction() != DoNotTraceWeakMaps);
   for (WeakMapBase* m : zone->gcWeakMapList()) {
     m->trace(tracer);
     TraceNullableEdge(tracer, &m->memberOf, "memberOf");
   }
 }
 
+#if defined(JS_GC_ZEAL) || defined(DEBUG)
+bool WeakMapBase::checkMarkingForZone(JS::Zone* zone) {
+  // This is called at the end of marking.
+  MOZ_ASSERT(zone->isGCMarking());
+
+  bool ok = true;
+  for (WeakMapBase* m : zone->gcWeakMapList()) {
+    if (m->marked && !m->checkMarking()) {
+      ok = false;
+    }
+  }
+
+  return ok;
+}
+#endif
+
 bool WeakMapBase::markZoneIteratively(JS::Zone* zone, GCMarker* marker) {
   bool markedAny = false;
   for (WeakMapBase* m : zone->gcWeakMapList()) {
     if (m->marked && m->markIteratively(marker)) {
       markedAny = true;
     }
   }
   return markedAny;
--- a/js/src/gc/WeakMap.h
+++ b/js/src/gc/WeakMap.h
@@ -20,17 +20,24 @@ class Zone;
 
 namespace js {
 
 class GCMarker;
 class WeakMapBase;
 struct WeakMapTracer;
 
 namespace gc {
+
 struct WeakMarkable;
+
+#if defined(JS_GC_ZEAL) || defined(DEBUG)
+// Check whether a weak map entry is marked correctly.
+bool CheckWeakMapEntryMarking(const WeakMapBase* map, Cell* key, Cell* value);
+#endif
+
 }  // namespace gc
 
 // A subclass template of js::HashMap whose keys and values may be
 // garbage-collected. When a key is collected, the table entry disappears,
 // dropping its reference to the value.
 //
 // More precisely:
 //
@@ -57,60 +64,70 @@ class WeakMapBase : public mozilla::Link
 
   JS::Zone* zone() const { return zone_; }
 
   // Garbage collector entry points.
 
   // Unmark all weak maps in a zone.
   static void unmarkZone(JS::Zone* zone);
 
-  // Mark all the weakmaps in a zone.
+  // Trace all the weakmaps in a zone.
   static void traceZone(JS::Zone* zone, JSTracer* tracer);
 
   // Check all weak maps in a zone that have been marked as live in this garbage
   // collection, and mark the values of all entries that have become strong
   // references to them. Return true if we marked any new values, indicating
   // that we need to make another pass. In other words, mark my marked maps'
   // marked members' mid-collection.
   static bool markZoneIteratively(JS::Zone* zone, GCMarker* marker);
 
   // Add zone edges for weakmaps with key delegates in a different zone.
   static bool findInterZoneEdges(JS::Zone* zone);
 
   // Sweep the weak maps in a zone, removing dead weak maps and removing
   // entries of live weak maps whose keys are dead.
   static void sweepZone(JS::Zone* zone);
 
-  // Trace all delayed weak map bindings. Used by the cycle collector.
+  // Trace all weak map bindings. Used by the cycle collector.
   static void traceAllMappings(WeakMapTracer* tracer);
 
   // Save information about which weak maps are marked for a zone.
   static bool saveZoneMarkedWeakMaps(JS::Zone* zone,
                                      WeakMapSet& markedWeakMaps);
 
   // Restore information about which weak maps are marked for many zones.
   static void restoreMarkedWeakMaps(WeakMapSet& markedWeakMaps);
 
+#if defined(JS_GC_ZEAL) || defined(DEBUG)
+  static bool checkMarkingForZone(JS::Zone* zone);
+#endif
+
  protected:
   // Instance member functions called by the above. Instantiations of WeakMap
   // override these with definitions appropriate for their Key and Value types.
   virtual void trace(JSTracer* tracer) = 0;
   virtual bool findZoneEdges() = 0;
   virtual void sweep() = 0;
   virtual void traceMappings(WeakMapTracer* tracer) = 0;
   virtual void clearAndCompact() = 0;
 
   // Any weakmap key types that want to participate in the non-iterative
   // ephemeron marking must override this method.
   virtual void markEntry(GCMarker* marker, gc::Cell* markedCell,
                          JS::GCCellPtr l) = 0;
 
   virtual bool markIteratively(GCMarker* marker) = 0;
 
- protected:
+#ifdef JS_GC_ZEAL
+  virtual bool checkMarking() const = 0;
+  virtual bool allowKeysInOtherZones() const { return false; }
+  friend bool gc::CheckWeakMapEntryMarking(const WeakMapBase*, gc::Cell*,
+                                           gc::Cell*);
+#endif
+
   // Object that this weak map is part of, if any.
   GCPtrObject memberOf;
 
   // Zone containing this weak map.
   JS::Zone* zone_;
 
   // Whether this object has been traced during garbage collection.
   bool marked;
@@ -195,16 +212,20 @@ class WeakMap
   // memberOf can be nullptr, which means that the map is not part of a
   // JSObject.
   void traceMappings(WeakMapTracer* tracer) override;
 
  protected:
 #if DEBUG
   void assertEntriesNotAboutToBeFinalized();
 #endif
+
+#ifdef JS_GC_ZEAL
+  bool checkMarking() const override;
+#endif
 };
 
 class ObjectValueMap : public WeakMap<HeapPtr<JSObject*>, HeapPtr<Value>> {
  public:
   ObjectValueMap(JSContext* cx, JSObject* obj) : WeakMap(cx, obj) {}
 
   bool findZoneEdges() override;
 };
--- a/js/src/jit-test/tests/wasm/globals.js
+++ b/js/src/jit-test/tests/wasm/globals.js
@@ -315,17 +315,17 @@ wasmAssert(`(module
     assertEq(new Global({value: "i32"}) instanceof Global, true);
     assertEq(new Global({value: "f32"}) instanceof Global, true);
     assertEq(new Global({value: "f64"}) instanceof Global, true);
     assertEq(new Global({value: "i64"}) instanceof Global, true); // No initial value works
 
     // These types should not work:
     assertErrorMessage(() => new Global({}),                TypeError, /bad type for a WebAssembly.Global/);
     assertErrorMessage(() => new Global({value: "fnord"}),  TypeError, /bad type for a WebAssembly.Global/);
-    assertErrorMessage(() => new Global(),                  TypeError, /Global requires more than 0 arguments/);
+    assertErrorMessage(() => new Global(),                  TypeError, /Global requires at least 1 argument/);
     assertErrorMessage(() => new Global({value: "i64"}, 0), TypeError, /bad type for a WebAssembly.Global/); // Initial value does not work
 
     // Coercion of init value; ".value" accessor
     assertEq((new Global({value: "i32"}, 3.14)).value, 3);
     assertEq((new Global({value: "f32"}, { valueOf: () => 33.5 })).value, 33.5);
     assertEq((new Global({value: "f64"}, "3.25")).value, 3.25);
 
     // Nothing special about NaN, it coerces just fine
--- a/js/src/jit-test/tests/wasm/validate.js
+++ b/js/src/jit-test/tests/wasm/validate.js
@@ -1,11 +1,11 @@
 const { validate } = WebAssembly;
 
-assertErrorMessage(() => validate(), Error, /requires more than 0 arguments/);
+assertErrorMessage(() => validate(), Error, /requires at least 1 argument/);
 
 const argError = /first argument must be an ArrayBuffer or typed array object/;
 assertErrorMessage(() => validate(null), Error, argError);
 assertErrorMessage(() => validate(true), Error, argError);
 assertErrorMessage(() => validate(42), Error, argError);
 assertErrorMessage(() => validate(NaN), Error, argError);
 assertErrorMessage(() => validate('yo'), Error, argError);
 assertErrorMessage(() => validate([]), Error, argError);
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -37,17 +37,17 @@
  *
  * to report:
  *
  * "TypeError: Rhino is not a member of the Monkey family"
  */
 
 MSG_DEF(JSMSG_NOT_AN_ERROR,            0, JSEXN_ERR, "<Error #0 is reserved>")
 MSG_DEF(JSMSG_NOT_DEFINED,             1, JSEXN_REFERENCEERR, "{0} is not defined")
-MSG_DEF(JSMSG_MORE_ARGS_NEEDED,        3, JSEXN_TYPEERR, "{0} requires more than {1} argument{2}")
+MSG_DEF(JSMSG_MORE_ARGS_NEEDED,        4, JSEXN_TYPEERR, "{0} requires at least {1} argument{2}, but only {3} were passed")
 MSG_DEF(JSMSG_INCOMPATIBLE_PROTO,      3, JSEXN_TYPEERR, "{0}.prototype.{1} called on incompatible {2}")
 MSG_DEF(JSMSG_NO_CONSTRUCTOR,          1, JSEXN_TYPEERR, "{0} has no constructor")
 MSG_DEF(JSMSG_BAD_SORT_ARG,            0, JSEXN_TYPEERR, "invalid Array.prototype.sort argument")
 MSG_DEF(JSMSG_READ_ONLY,               1, JSEXN_TYPEERR, "{0} is read-only")
 MSG_DEF(JSMSG_CANT_DELETE,             1, JSEXN_TYPEERR, "property {0} is non-configurable and can't be deleted")
 MSG_DEF(JSMSG_CANT_TRUNCATE_ARRAY,     0, JSEXN_TYPEERR, "can't delete non-configurable array element")
 MSG_DEF(JSMSG_NOT_FUNCTION,            1, JSEXN_TYPEERR, "{0} is not a function")
 MSG_DEF(JSMSG_NOT_CONSTRUCTOR,         1, JSEXN_TYPEERR, "{0} is not a constructor")
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -119,29 +119,27 @@ using JS::ReadOnlyCompileOptions;
 using JS::SourceText;
 
 #ifdef HAVE_VA_LIST_AS_ARRAY
 #define JS_ADDRESSOF_VA_LIST(ap) ((va_list*)(ap))
 #else
 #define JS_ADDRESSOF_VA_LIST(ap) (&(ap))
 #endif
 
-JS_PUBLIC_API bool JS::CallArgs::requireAtLeast(JSContext* cx,
-                                                const char* fnname,
-                                                unsigned required) const {
-  if (length() < required) {
-    char numArgsStr[40];
-    SprintfLiteral(numArgsStr, "%u", required - 1);
-    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
-                              JSMSG_MORE_ARGS_NEEDED, fnname, numArgsStr,
-                              required == 2 ? "" : "s");
-    return false;
-  }
-
-  return true;
+JS_PUBLIC_API void JS::CallArgs::reportMoreArgsNeeded(JSContext* cx,
+                                                      const char* fnname,
+                                                      unsigned required,
+                                                      unsigned actual) {
+  char requiredArgsStr[40];
+  SprintfLiteral(requiredArgsStr, "%u", required);
+  char actualArgsStr[40];
+  SprintfLiteral(actualArgsStr, "%u", actual);
+  JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+                            JSMSG_MORE_ARGS_NEEDED, fnname, requiredArgsStr,
+                            required == 1 ? "" : "s", actualArgsStr);
 }
 
 static bool ErrorTakesArguments(unsigned msg) {
   MOZ_ASSERT(msg < JSErr_Limit);
   unsigned argCount = js_ErrorFormatString[msg].argCount;
   MOZ_ASSERT(argCount <= 2);
   return argCount == 1 || argCount == 2;
 }
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -2051,16 +2051,23 @@ class JSJitMethodCallArgs
 
   JSObject& callee() const {
     // We can't use Base::callee() because that will try to poke at
     // this->usedRval_, which we don't have.
     return argv_[-2].toObject();
   }
 
   JS::HandleValue get(unsigned i) const { return Base::get(i); }
+
+  bool requireAtLeast(JSContext* cx, const char* fnname,
+                      unsigned required) const {
+    // Can just forward to Base, since it only needs the length and we
+    // forward that already.
+    return Base::requireAtLeast(cx, fnname, required);
+  }
 };
 
 struct JSJitMethodCallArgsTraits {
   static const size_t offsetOfArgv = offsetof(JSJitMethodCallArgs, argv_);
   static const size_t offsetOfArgc = offsetof(JSJitMethodCallArgs, argc_);
 };
 
 typedef bool (*JSJitGetterOp)(JSContext* cx, JS::HandleObject thisObj,
--- a/js/src/proxy/ScriptedProxyHandler.cpp
+++ b/js/src/proxy/ScriptedProxyHandler.cpp
@@ -1440,19 +1440,17 @@ const ScriptedProxyHandler ScriptedProxy
 bool IsRevokedScriptedProxy(JSObject* obj) {
   obj = CheckedUnwrap(obj);
   return obj && IsScriptedProxy(obj) && !obj->as<ProxyObject>().target();
 }
 
 // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93
 // 9.5.14 ProxyCreate.
 static bool ProxyCreate(JSContext* cx, CallArgs& args, const char* callerName) {
-  if (args.length() < 2) {
-    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
-                              JSMSG_MORE_ARGS_NEEDED, callerName, "1", "s");
+  if (!args.requireAtLeast(cx, callerName, 2)) {
     return false;
   }
 
   // Step 1.
   RootedObject target(cx,
                       NonNullObjectArg(cx, "`target`", callerName, args[0]));
   if (!target) {
     return false;
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -4503,19 +4503,17 @@ static bool Elapsed(JSContext* cx, unsig
     return true;
   }
   JS_ReportErrorASCII(cx, "Wrong number of arguments");
   return false;
 }
 
 static bool Compile(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
-  if (args.length() < 1) {
-    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
-                              JSMSG_MORE_ARGS_NEEDED, "compile", "0", "s");
+  if (!args.requireAtLeast(cx, "compile", 1)) {
     return false;
   }
   if (!args[0].isString()) {
     const char* typeName = InformalValueTypeName(args[0]);
     JS_ReportErrorASCII(cx, "expected string to compile, got %s", typeName);
     return false;
   }
 
@@ -4559,19 +4557,17 @@ static ShellCompartmentPrivate* EnsureSh
     priv = cx->new_<ShellCompartmentPrivate>();
     JS_SetCompartmentPrivate(cx->compartment(), priv);
   }
   return priv;
 }
 
 static bool ParseModule(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
-  if (args.length() == 0) {
-    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
-                              JSMSG_MORE_ARGS_NEEDED, "parseModule", "0", "s");
+  if (!args.requireAtLeast(cx, "parseModule", 1)) {
     return false;
   }
 
   if (!args[0].isString()) {
     const char* typeName = InformalValueTypeName(args[0]);
     JS_ReportErrorASCII(cx, "expected string to compile, got %s", typeName);
     return false;
   }
@@ -4619,20 +4615,17 @@ static bool ParseModule(JSContext* cx, u
   }
 
   args.rval().setObject(*module);
   return true;
 }
 
 static bool SetModuleLoadHook(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
-  if (args.length() != 1) {
-    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
-                              JSMSG_MORE_ARGS_NEEDED, "setModuleLoadHook", "0",
-                              "s");
+  if (!args.requireAtLeast(cx, "setModuleLoadHook", 1)) {
     return false;
   }
 
   if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
     const char* typeName = InformalValueTypeName(args[0]);
     JS_ReportErrorASCII(cx, "expected hook function, got %s", typeName);
     return false;
   }
@@ -4641,20 +4634,17 @@ static bool SetModuleLoadHook(JSContext*
   global->setReservedSlot(GlobalAppSlotModuleLoadHook, args[0]);
 
   args.rval().setUndefined();
   return true;
 }
 
 static bool SetModuleResolveHook(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
-  if (args.length() != 1) {
-    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
-                              JSMSG_MORE_ARGS_NEEDED, "setModuleResolveHook",
-                              "0", "s");
+  if (!args.requireAtLeast(cx, "setModuleResolveHook", 1)) {
     return false;
   }
 
   if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
     const char* typeName = InformalValueTypeName(args[0]);
     JS_ReportErrorASCII(cx, "expected hook function, got %s", typeName);
     return false;
   }
@@ -4692,20 +4682,17 @@ static JSObject* ShellModuleResolveHook(
     return nullptr;
   }
 
   return &result.toObject();
 }
 
 static bool SetModuleMetadataHook(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
-  if (args.length() != 1) {
-    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
-                              JSMSG_MORE_ARGS_NEEDED, "setModuleMetadataHook",
-                              "0", "s");
+  if (!args.requireAtLeast(cx, "setModuleMetadataHook", 1)) {
     return false;
   }
 
   if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
     const char* typeName = InformalValueTypeName(args[0]);
     JS_ReportErrorASCII(cx, "expected hook function, got %s", typeName);
     return false;
   }
@@ -4741,57 +4728,48 @@ static bool ReportArgumentTypeError(JSCo
   const char* typeName = InformalValueTypeName(value);
   JS_ReportErrorASCII(cx, "Expected %s, got %s", expected, typeName);
   return false;
 }
 
 static bool ShellSetModulePrivate(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 
-  if (args.length() != 2) {
-    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
-                              JSMSG_MORE_ARGS_NEEDED, "setModulePrivate", "0",
-                              "s");
+  if (!args.requireAtLeast(cx, "setModulePrivate", 2)) {
     return false;
   }
 
   if (!args[0].isObject() || !args[0].toObject().is<ModuleObject>()) {
     return ReportArgumentTypeError(cx, args[0], "module object");
   }
 
   JS::SetModulePrivate(&args[0].toObject(), args[1]);
   args.rval().setUndefined();
   return true;
 }
 
 static bool ShellGetModulePrivate(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 
-  if (args.length() != 1) {
-    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
-                              JSMSG_MORE_ARGS_NEEDED, "getModulePrivate", "0",
-                              "s");
+  if (!args.requireAtLeast(cx, "getModulePrivate", 1)) {
     return false;
   }
 
   if (!args[0].isObject() || !args[0].toObject().is<ModuleObject>()) {
     return ReportArgumentTypeError(cx, args[0], "module object");
   }
 
   args.rval().set(JS::GetModulePrivate(&args[0].toObject()));
   return true;
 }
 
 static bool SetModuleDynamicImportHook(JSContext* cx, unsigned argc,
                                        Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
-  if (args.length() != 1) {
-    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
-                              JSMSG_MORE_ARGS_NEEDED,
-                              "setModuleDynamicImportHook", "0", "s");
+  if (!args.requireAtLeast(cx, "setModuleDynamicImportHook", 1)) {
     return false;
   }
 
   if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
     const char* typeName = InformalValueTypeName(args[0]);
     JS_ReportErrorASCII(cx, "expected hook function, got %s", typeName);
     return false;
   }
@@ -4800,20 +4778,17 @@ static bool SetModuleDynamicImportHook(J
   global->setReservedSlot(GlobalAppSlotModuleDynamicImportHook, args[0]);
 
   args.rval().setUndefined();
   return true;
 }
 
 static bool FinishDynamicModuleImport(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
-  if (args.length() != 3) {
-    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
-                              JSMSG_MORE_ARGS_NEEDED,
-                              "finishDynamicModuleImport", "0", "s");
+  if (!args.requireAtLeast(cx, "finishDynamicModuleImport", 3)) {
     return false;
   }
 
   if (!args[1].isString()) {
     return ReportArgumentTypeError(cx, args[1], "String");
   }
 
   if (!args[2].isObject() || !args[2].toObject().is<PromiseObject>()) {
@@ -4823,20 +4798,17 @@ static bool FinishDynamicModuleImport(JS
   RootedString specifier(cx, args[1].toString());
   Rooted<PromiseObject*> promise(cx, &args[2].toObject().as<PromiseObject>());
 
   return js::FinishDynamicModuleImport(cx, args[0], specifier, promise);
 }
 
 static bool AbortDynamicModuleImport(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
-  if (args.length() != 4) {
-    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
-                              JSMSG_MORE_ARGS_NEEDED,
-                              "abortDynamicModuleImport", "0", "s");
+  if (!args.requireAtLeast(cx, "abortDynamicModuleImport", 4)) {
     return false;
   }
 
   if (!args[1].isString()) {
     return ReportArgumentTypeError(cx, args[1], "String");
   }
 
   if (!args[2].isObject() || !args[2].toObject().is<PromiseObject>()) {
@@ -4921,19 +4893,17 @@ static bool ParseBinASTData(JSContext* c
 #endif
 
   return true;
 }
 
 static bool BinParse(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 
-  if (args.length() < 1) {
-    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
-                              JSMSG_MORE_ARGS_NEEDED, "parse", "0", "s");
+  if (!args.requireAtLeast(cx, "parseBin", 1)) {
     return false;
   }
 
   // Extract argument 1: ArrayBuffer.
 
   if (!args[0].isObject()) {
     const char* typeName = InformalValueTypeName(args[0]);
     JS_ReportErrorASCII(cx, "expected object (ArrayBuffer) to parse, got %s",
@@ -5036,19 +5006,17 @@ static bool BinParse(JSContext* cx, unsi
 
 #endif  // defined(JS_BUILD_BINAST)
 
 static bool Parse(JSContext* cx, unsigned argc, Value* vp) {
   using namespace js::frontend;
 
   CallArgs args = CallArgsFromVp(argc, vp);
 
-  if (args.length() < 1) {
-    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
-                              JSMSG_MORE_ARGS_NEEDED, "parse", "0", "s");
+  if (!args.requireAtLeast(cx, "parse", 1)) {
     return false;
   }
   if (!args[0].isString()) {
     const char* typeName = InformalValueTypeName(args[0]);
     JS_ReportErrorASCII(cx, "expected string to parse, got %s", typeName);
     return false;
   }
 
@@ -5165,19 +5133,17 @@ static bool Parse(JSContext* cx, unsigne
   return true;
 }
 
 static bool SyntaxParse(JSContext* cx, unsigned argc, Value* vp) {
   using namespace js::frontend;
 
   CallArgs args = CallArgsFromVp(argc, vp);
 
-  if (args.length() < 1) {
-    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
-                              JSMSG_MORE_ARGS_NEEDED, "parse", "0", "s");
+  if (!args.requireAtLeast(cx, "syntaxParse", 1)) {
     return false;
   }
   if (!args[0].isString()) {
     const char* typeName = InformalValueTypeName(args[0]);
     JS_ReportErrorASCII(cx, "expected string to parse, got %s", typeName);
     return false;
   }
 
@@ -5237,20 +5203,17 @@ static bool OffThreadCompileScript(JSCon
   if (!CanUseExtraThreads()) {
     JS_ReportErrorASCII(cx,
                         "Can't use offThreadCompileScript with --no-threads");
     return false;
   }
 
   CallArgs args = CallArgsFromVp(argc, vp);
 
-  if (args.length() < 1) {
-    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
-                              JSMSG_MORE_ARGS_NEEDED, "offThreadCompileScript",
-                              "0", "s");
+  if (!args.requireAtLeast(cx, "offThreadCompileScript", 1)) {
     return false;
   }
   if (!args[0].isString()) {
     const char* typeName = InformalValueTypeName(args[0]);
     JS_ReportErrorASCII(cx, "expected string to parse, got %s", typeName);
     return false;
   }
 
@@ -5448,20 +5411,17 @@ static bool OffThreadDecodeScript(JSCont
   if (!CanUseExtraThreads()) {
     JS_ReportErrorASCII(cx,
                         "Can't use offThreadDecodeScript with --no-threads");
     return false;
   }
 
   CallArgs args = CallArgsFromVp(argc, vp);
 
-  if (args.length() < 1) {
-    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
-                              JSMSG_MORE_ARGS_NEEDED, "offThreadDecodeScript",
-                              "0", "s");
+  if (!args.requireAtLeast(cx, "offThreadDecodeScript", 1)) {
     return false;
   }
   if (!args[0].isObject() || !CacheEntry_isCacheEntry(&args[0].toObject())) {
     const char* typeName = InformalValueTypeName(args[0]);
     JS_ReportErrorASCII(cx, "expected cache entry, got %s", typeName);
     return false;
   }
   RootedObject cacheEntry(cx, &args[0].toObject());
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -250,16 +250,22 @@ class DebuggerWeakMap
     CountMap::Ptr p = zoneCounts.lookup(zone);
     MOZ_ASSERT(p);
     MOZ_ASSERT(p->value() > 0);
     --p->value();
     if (p->value() == 0) {
       zoneCounts.remove(zone);
     }
   }
+
+#ifdef JS_GC_ZEAL
+  // Let the weak map marking verifier know that this map can
+  // contain keys in other zones.
+  virtual bool allowKeysInOtherZones() const override { return true; }
+#endif
 };
 
 class LeaveDebuggeeNoExecute;
 
 // Suppresses all debuggee NX checks, i.e., allow all execution. Used to allow
 // certain whitelisted operations to execute code.
 //
 // WARNING
--- a/js/src/vm/JSObject.cpp
+++ b/js/src/vm/JSObject.cpp
@@ -222,19 +222,17 @@ bool js::FromPropertyDescriptorToObject(
 
   vp.setObject(*obj);
   return true;
 }
 
 bool js::GetFirstArgumentAsObject(JSContext* cx, const CallArgs& args,
                                   const char* method,
                                   MutableHandleObject objp) {
-  if (args.length() == 0) {
-    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
-                              JSMSG_MORE_ARGS_NEEDED, method, "0", "s");
+  if (!args.requireAtLeast(cx, method, 1)) {
     return false;
   }
 
   HandleValue v = args[0];
   if (!v.isObject()) {
     UniqueChars bytes =
         DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, nullptr);
     if (!bytes) {
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -7892,19 +7892,19 @@ void PresShell::GetCurrentItemAndPositio
   bool istree = false, checkLineHeight = true;
   nscoord extraTreeY = 0;
 
 #ifdef MOZ_XUL
   // Set the position to just underneath the current item for multi-select
   // lists or just underneath the selected item for single-select lists. If
   // the element is not a list, or there is no selection, leave the position
   // as is.
-  nsCOMPtr<nsIDOMXULSelectControlItemElement> item;
+  nsCOMPtr<Element> item;
   nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelect =
-      do_QueryInterface(aFocusedElement);
+      aFocusedElement->AsXULMultiSelectControl();
   if (multiSelect) {
     checkLineHeight = false;
 
     int32_t currentIndex;
     multiSelect->GetCurrentIndex(&currentIndex);
     if (currentIndex >= 0) {
       RefPtr<nsXULElement> xulElement = nsXULElement::FromNode(focusedContent);
       if (xulElement) {
@@ -7941,28 +7941,30 @@ void PresShell::GetCurrentItemAndPositio
         } else {
           multiSelect->GetCurrentItem(getter_AddRefs(item));
         }
       }
     }
   } else {
     // don't check menulists as the selected item will be inside a popup.
     nsCOMPtr<nsIDOMXULMenuListElement> menulist =
-        do_QueryInterface(aFocusedElement);
+        aFocusedElement->AsXULMenuList();
     if (!menulist) {
       nsCOMPtr<nsIDOMXULSelectControlElement> select =
-          do_QueryInterface(aFocusedElement);
+          aFocusedElement->AsXULSelectControl();
       if (select) {
         checkLineHeight = false;
         select->GetSelectedItem(getter_AddRefs(item));
       }
     }
   }
 
-  if (item) focusedContent = do_QueryInterface(item);
+  if (item) {
+    focusedContent = item;
+  }
 #endif
 
   nsIFrame* frame = focusedContent->GetPrimaryFrame();
   if (frame) {
     NS_ASSERTION(
         frame->PresContext() == GetPresContext(),
         "handling event for focused content that is not in our document?");
 
--- a/layout/xul/nsButtonBoxFrame.cpp
+++ b/layout/xul/nsButtonBoxFrame.cpp
@@ -118,18 +118,19 @@ nsresult nsButtonBoxFrame::HandleEvent(n
 // On mac, Return fires the default button, not the focused one.
 #ifndef XP_MACOSX
     case eKeyPress: {
       WidgetKeyboardEvent* keyEvent = aEvent->AsKeyboardEvent();
       if (!keyEvent) {
         break;
       }
       if (NS_VK_RETURN == keyEvent->mKeyCode) {
-        nsCOMPtr<nsIDOMXULButtonElement> buttonEl(do_QueryInterface(mContent));
-        if (buttonEl) {
+        RefPtr<nsIDOMXULButtonElement> button =
+            mContent->AsElement()->AsXULButton();
+        if (button) {
           MouseClicked(aEvent);
           *aEventStatus = nsEventStatus_eConsumeNoDefault;
         }
       }
       break;
     }
 #endif
 
--- a/layout/xul/nsMenuPopupFrame.cpp
+++ b/layout/xul/nsMenuPopupFrame.cpp
@@ -1102,30 +1102,31 @@ nsPoint nsMenuPopupFrame::AdjustPosition
   }
 
   return pnt;
 }
 
 nsIFrame* nsMenuPopupFrame::GetSelectedItemForAlignment() {
   // This method adjusts a menulist's popup such that the selected item is under
   // the cursor, aligned with the menulist label.
-  nsCOMPtr<nsIDOMXULSelectControlElement> select =
-      do_QueryInterface(mAnchorContent);
+  nsCOMPtr<nsIDOMXULSelectControlElement> select;
+  if (mAnchorContent) {
+    select = mAnchorContent->AsElement()->AsXULSelectControl();
+  }
+
   if (!select) {
     // If there isn't an anchor, then try just getting the parent of the popup.
-    select = do_QueryInterface(mContent->GetParent());
+    select = mContent->GetParent()->AsElement()->AsXULSelectControl();
     if (!select) {
       return nullptr;
     }
   }
 
-  nsCOMPtr<nsIDOMXULSelectControlItemElement> item;
-  select->GetSelectedItem(getter_AddRefs(item));
-
-  nsCOMPtr<nsIContent> selectedElement = do_QueryInterface(item);
+  nsCOMPtr<Element> selectedElement;
+  select->GetSelectedItem(getter_AddRefs(selectedElement));
   return selectedElement ? selectedElement->GetPrimaryFrame() : nullptr;
 }
 
 nscoord nsMenuPopupFrame::SlideOrResize(nscoord& aScreenPoint, nscoord aSize,
                                         nscoord aScreenBegin,
                                         nscoord aScreenEnd, nscoord* aOffset) {
   // The popup may be positioned such that either the left/top or bottom/right
   // is outside the screen - but never both.
--- a/mobile/android/modules/SelectHelper.jsm
+++ b/mobile/android/modules/SelectHelper.jsm
@@ -41,17 +41,17 @@ var SelectHelper = {
 
   // This is a callback function to be provided to prompt.show(callBack).
   // It will update which Option elements in a Select have been selected
   // or unselected and fire the onChange event.
   _promptCallBack: function(data, element) {
     let win = element.ownerGlobal;
     let selected = data.list;
 
-    if (element instanceof Ci.nsIDOMXULMenuListElement) {
+    if (this._isXULElement(element, "menulist")) {
       if (element.selectedIndex != selected[0]) {
         element.selectedIndex = selected[0];
         this.fireOnCommand(element);
       }
     } else if (element instanceof win.HTMLSelectElement) {
       let changed = false;
       let i = 0; // The index for the element from `data.list` that we are currently examining.
       this.forVisibleOptions(element, function(node) {
@@ -85,19 +85,24 @@ var SelectHelper = {
       p.setSingleChoiceItems(list);
     }
 
     p.show((data) => {
       this._promptCallBack(data, element);
     });
   },
 
+  _isXULElement: function(element, tag) {
+    return (!tag || element.localName == tag) &&
+           element.namespaceURI == "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+  },
+
   _isMenu: function(element) {
     let win = element.ownerGlobal;
-    return (element instanceof win.HTMLSelectElement || element instanceof Ci.nsIDOMXULMenuListElement);
+    return (element instanceof win.HTMLSelectElement || this._isXULElement(element, "menulist"));
   },
 
   // Return a list of Option elements within a Select excluding
   // any that were not visible.
   getListForElement: function(element) {
     let index = 0;
     let items = [];
     this.forVisibleOptions(element, function(node, options, parent) {
@@ -117,34 +122,34 @@ var SelectHelper = {
       index++;
     });
     return items;
   },
 
   // Apply a function to all visible Option elements in a Select
   forVisibleOptions: function(element, aFunction, parent = null) {
     let win = element.ownerGlobal;
-    if (element instanceof Ci.nsIDOMXULMenuListElement) {
+    if (this._isXULElement(element, "menulist")) {
       element = element.menupopup;
     }
     let children = element.children;
     let numChildren = children.length;
 
 
     // if there are no children in this select, we add a dummy row so that at least something appears
     if (numChildren == 0) {
       aFunction.call(this, {label: ""}, {isGroup: false}, parent);
     }
 
     for (let i = 0; i < numChildren; i++) {
       let child = children[i];
       let style = win.getComputedStyle(child);
       if (style.display !== "none") {
         if (child instanceof win.HTMLOptionElement ||
-            child instanceof Ci.nsIDOMXULSelectControlItemElement) {
+            this._isXULElement(child)) {
           aFunction.call(this, child, {isGroup: false}, parent);
         } else if (child instanceof win.HTMLOptGroupElement) {
           aFunction.call(this, child, {isGroup: true});
           this.forVisibleOptions(child, aFunction, child);
         }
       }
     }
   },
--- a/netwerk/streamconv/converters/mozTXTToHTMLConv.cpp
+++ b/netwerk/streamconv/converters/mozTXTToHTMLConv.cpp
@@ -532,47 +532,65 @@ bool mozTXTToHTMLConv::ItMatchesDelimite
        textLen < aRepLen) ||
       ((before != LT_IGNORE || (after != LT_IGNORE && after != LT_DELIMITER)) &&
        textLen < aRepLen + 1) ||
       (before != LT_IGNORE && after != LT_IGNORE && after != LT_DELIMITER &&
        textLen < aRepLen + 2))
     return false;
 
   uint32_t text0 = aInString[0];
-  uint32_t textAfterPos = aInString[aRepLen + (before == LT_IGNORE ? 0 : 1)];
+  if (NS_IS_HIGH_SURROGATE(text0) && aInLength > 1 &&
+      NS_IS_LOW_SURROGATE(aInString[1])) {
+    text0 = SURROGATE_TO_UCS4(text0, aInString[1]);
+  }
+  // find length of the char/cluster to be ignored
+  int32_t ignoreLen = before == LT_IGNORE ? 0 : 1;
+  if (ignoreLen) {
+    mozilla::unicode::ClusterIterator ci(aInString, aInLength);
+    ci.Next();
+    ignoreLen = ci - aInString;
+  }
+
+  int32_t afterIndex = aRepLen + ignoreLen;
+  uint32_t textAfterPos = aInString[afterIndex];
+  if (NS_IS_HIGH_SURROGATE(textAfterPos) && aInLength > afterIndex + 1 &&
+      NS_IS_LOW_SURROGATE(aInString[afterIndex + 1])) {
+    textAfterPos = SURROGATE_TO_UCS4(textAfterPos, aInString[afterIndex + 1]);
+  }
 
   if ((before == LT_ALPHA && !IsAlpha(text0)) ||
       (before == LT_DIGIT && !IsDigit(text0)) ||
       (before == LT_DELIMITER &&
        (IsAlpha(text0) || IsDigit(text0) || text0 == *rep)) ||
       (after == LT_ALPHA && !IsAlpha(textAfterPos)) ||
       (after == LT_DIGIT && !IsDigit(textAfterPos)) ||
       (after == LT_DELIMITER &&
        (IsAlpha(textAfterPos) || IsDigit(textAfterPos) ||
         textAfterPos == *rep)) ||
-      !Substring(Substring(aInString, aInString + aInLength),
-                 (before == LT_IGNORE ? 0 : 1), aRepLen)
+      !Substring(Substring(aInString, aInString + aInLength), ignoreLen,
+                 aRepLen)
            .Equals(Substring(rep, rep + aRepLen),
                    nsCaseInsensitiveStringComparator()))
     return false;
 
   return true;
 }
 
 uint32_t mozTXTToHTMLConv::NumberOfMatches(const char16_t* aInString,
                                            int32_t aInStringLength,
                                            const char16_t* rep, int32_t aRepLen,
                                            LIMTYPE before, LIMTYPE after) {
   uint32_t result = 0;
 
-  for (int32_t i = 0; i < aInStringLength; i++) {
-    const char16_t* indexIntoString = &aInString[i];
-    if (ItMatchesDelimited(indexIntoString, aInStringLength - i, rep, aRepLen,
-                           before, after))
+  const char16_t* end = aInString + aInStringLength;
+  for (mozilla::unicode::ClusterIterator ci(aInString, aInStringLength);
+       !ci.AtEnd(); ci.Next()) {
+    if (ItMatchesDelimited(ci, end - ci, rep, aRepLen, before, after)) {
       result++;
+    }
   }
   return result;
 }
 
 // NOTE: the converted html for the phrase is appended to aOutString
 // tagHTML and attributeHTML are plain ASCII (literal strings, in fact)
 bool mozTXTToHTMLConv::StructPhraseHit(
     const char16_t* aInString, int32_t aInStringLength, bool col0,
@@ -949,68 +967,76 @@ mozTXTToHTMLConv::ScanTXT(const nsAStrin
   uint32_t structPhrase_italic = 0;
   uint32_t structPhrase_code = 0;
 
   uint32_t endOfLastURLOutput = 0;
 
   nsAutoString outputHTML;  // moved here for performance increase
 
   const char16_t* rawInputString = aInString.BeginReading();
+  uint32_t inLength = aInString.Length();
 
-  for (uint32_t i = 0; i < aInString.Length();) {
+  for (mozilla::unicode::ClusterIterator ci(rawInputString, inLength);
+       !ci.AtEnd();) {
+    uint32_t i = ci - rawInputString;
     if (doGlyphSubstitution) {
       int32_t glyphTextLen;
-      if (GlyphHit(&rawInputString[i], aInString.Length() - i, i == 0,
-                   aOutString, glyphTextLen)) {
+      if (GlyphHit(&rawInputString[i], inLength - i, i == 0, aOutString,
+                   glyphTextLen)) {
         i += glyphTextLen;
+        while (ci < rawInputString + i) {
+          ci.Next();
+        }
         continue;
       }
     }
 
     if (doStructPhrase) {
       const char16_t* newOffset = rawInputString;
       int32_t newLength = aInString.Length();
       if (i > 0)  // skip the first element?
       {
-        newOffset = &rawInputString[i - 1];
-        newLength = aInString.Length() - i + 1;
+        mozilla::unicode::ClusterReverseIterator ri(rawInputString, i);
+        ri.Next();
+        newOffset = ri;
+        newLength = aInString.Length() - (ri - rawInputString);
       }
 
       switch (aInString[i])  // Performance increase
       {
         case '*':
           if (StructPhraseHit(newOffset, newLength, i == 0, u"*", 1, "b",
                               "class=\"moz-txt-star\"", aOutString,
                               structPhrase_strong)) {
-            i++;
+            ci.Next();
             continue;
           }
           break;
         case '/':
           if (StructPhraseHit(newOffset, newLength, i == 0, u"/", 1, "i",
                               "class=\"moz-txt-slash\"", aOutString,
                               structPhrase_italic)) {
-            i++;
+            ci.Next();
             continue;
           }
           break;
         case '_':
           if (StructPhraseHit(newOffset, newLength, i == 0, u"_", 1,
                               "span" /* <u> is deprecated */,
                               "class=\"moz-txt-underscore\"", aOutString,
                               structPhrase_underline)) {
-            i++;
+            ci.Next();
             continue;
           }
           break;
         case '|':
           if (StructPhraseHit(newOffset, newLength, i == 0, u"|", 1, "code",
                               "class=\"moz-txt-verticalline\"", aOutString,
                               structPhrase_code)) {
-            i++;
+            ci.Next();
             continue;
           }
           break;
       }
     }
 
     if (doURLs) {
       switch (aInString[i]) {
@@ -1033,36 +1059,41 @@ mozTXTToHTMLConv::ScanTXT(const nsAStrin
               if (aOutString.Length() - replaceBefore < endOfLastURLOutput) {
                 break;
               }
               aOutString.Cut(aOutString.Length() - replaceBefore,
                              replaceBefore);
               aOutString += outputHTML;
               endOfLastURLOutput = aOutString.Length();
               i += replaceAfter + 1;
+              while (ci < rawInputString + i) {
+                ci.Next();
+              }
               continue;
             }
           }
           break;
       }  // switch
     }
 
     switch (aInString[i]) {
       // Special symbols
       case '<':
       case '>':
       case '&':
         EscapeChar(aInString[i], aOutString, false);
-        i++;
+        ci.Next();
         break;
       // Normal characters
-      default:
-        aOutString += aInString[i];
-        i++;
+      default: {
+        const char16_t* start = ci;
+        ci.Next();
+        aOutString += Substring(start, (const char16_t*)ci);
         break;
+      }
     }
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 mozTXTToHTMLConv::ScanHTML(const nsAString& input, uint32_t whattodo,
                            nsAString& aOutString) {
--- a/netwerk/test/unit/test_mozTXTToHTMLConv.js
+++ b/netwerk/test/unit/test_mozTXTToHTMLConv.js
@@ -184,24 +184,30 @@ function run_test() {
       input: "this is a smiley :-(",
       results: ["moz-smiley-s2"]
     },
   ];
 
   const scanTXTstrings = [
     "underline",                                  // ASCII
     "äöüßáéíóúî",                                 // Latin-1
+    "a\u0301c\u0327c\u030Ce\u0309n\u0303t\u0326e\u0308d\u0323",
+                                                  // áçčẻñțëḍ Latin
     "\u016B\u00F1\u0257\u0119\u0211\u0142\u00ED\u00F1\u0119",
                                                   // Pseudo-ese ūñɗęȑłíñę
     "\u01DDu\u0131\u0283\u0279\u01DDpun",         // Upside down ǝuıʃɹǝpun
     "\u03C5\u03C0\u03BF\u03B3\u03C1\u03AC\u03BC\u03BC\u03B9\u03C3\u03B7",
                                                   // Greek υπογράμμιση
     "\u0441\u0438\u043B\u044C\u043D\u0443\u044E", // Russian сильную
     "\u0C2C\u0C32\u0C2E\u0C46\u0C56\u0C28",       // Telugu బలమైన
-    "\u508D\u7DDA\u3059\u308B"                    // Japanese 傍線する
+    "\u508D\u7DDA\u3059\u308B",                   // Japanese 傍線する
+    "\uD841\uDF0E\uD841\uDF31\uD841\uDF79\uD843\uDC53\uD843\uDC78",
+                                                  // Chinese (supplementary plane)
+    "\uD801\uDC14\uD801\uDC2F\uD801\uDC45\uD801\uDC28\uD801\uDC49\uD801\uDC2F\uD801\uDC3B"
+                                                  // Deseret 𐐔𐐯𐑅𐐨𐑉𐐯𐐻
   ];
 
   const scanTXTstructs = [
       {
         delimiter: "/",
         tag: "i",
         class: "moz-txt-slash"
       },
--- a/toolkit/components/prompts/content/tabprompts.xml
+++ b/toolkit/components/prompts/content/tabprompts.xml
@@ -330,17 +330,18 @@
               // the entire prompt blurs).
               defaultButton.setAttribute("default", true);
             } else {
               // On other platforms, the default button is only marked as such
               // when no other button has focus. XUL buttons on not-OSX will
               // react to pressing enter as a command, so you can't trigger the
               // default without tabbing to it or something that isn't a button.
               let focusedDefault = (event.originalTarget == defaultButton);
-              let someButtonFocused = event.originalTarget instanceof Ci.nsIDOMXULButtonElement;
+              let someButtonFocused = event.originalTarget.localName == "button" ||
+                                      event.originalTarget.localName == "toolbarbutton";
               defaultButton.setAttribute("default", focusedDefault || !someButtonFocused);
             }
         </handler>
         <handler event="blur">
             // If focus shifted to somewhere else in the browser, don't make
             // the default button look active.
             let bnum = this.args.defaultButtonNum || 0;
             let button = this.ui["button" + bnum];
--- a/toolkit/content/customElements.js
+++ b/toolkit/content/customElements.js
@@ -195,36 +195,36 @@ const MozElementMixin = Base => class Mo
     link.setAttribute("rel", "localization");
     link.setAttribute("href", path);
 
     container.appendChild(link);
   }
 
   /**
    * Indicate that a class defining a XUL element implements one or more
-   * XPCOM interfaces by adding a getCustomInterface implementation to it.
+   * XPCOM interfaces by adding a getCustomInterface implementation to it,
+   * as well as an implementation of QueryInterface.
    *
    * 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.
    */
   static implementCustomInterface(cls, ifaces) {
-    const numbers = new Set(ifaces.map(i => i.number));
-    if (cls.prototype.customInterfaceNumbers) {
-      // Base class already implemented some interfaces. Inherit:
-      cls.prototype.customInterfaceNumbers.forEach(number => numbers.add(number));
+    if (cls.prototype.customInterfaces) {
+      ifaces.push(...cls.prototype.customInterfaces);
     }
+    cls.prototype.customInterfaces = ifaces;
 
-    cls.prototype.customInterfaceNumbers = numbers;
-    cls.prototype.getCustomInterfaceCallback = function getCustomInterfaceCallback(iface) {
-      if (numbers.has(iface.number)) {
+    cls.prototype.QueryInterface = ChromeUtils.generateQI(ifaces);
+    cls.prototype.getCustomInterfaceCallback = function getCustomInterfaceCallback(ifaceToCheck) {
+      if (cls.prototype.customInterfaces.some(iface => iface.equals(ifaceToCheck))) {
         return getInterfaceProxy(this);
       }
       return null;
     };
   }
 };
 
 const MozXULElement = MozElementMixin(XULElement);
--- a/toolkit/content/tests/chrome/test_custom_element_base.xul
+++ b/toolkit/content/tests/chrome/test_custom_element_base.xul
@@ -188,15 +188,15 @@
     synthesizeKey("VK_TAB", { shiftKey: true });
     is(document.activeElement.id, "one", "Tab 1");
 
     info("Checking that interfaces get inherited automatically with implementCustomInterface");
     class ExtendedElement extends SimpleElement { }
     MozXULElement.implementCustomInterface(ExtendedElement, [Ci.nsIDOMXULSelectControlElement]);
     customElements.define("extendedelement", ExtendedElement);
     const extendedInstance = document.createXULElement("extendedelement");
-    ok(extendedInstance instanceof Ci.nsIDOMXULSelectControlElement, "interface applied");
-    ok(extendedInstance instanceof Ci.nsIDOMXULControlElement, "inherited interface applied");
+    ok(extendedInstance.QueryInterface(Ci.nsIDOMXULSelectControlElement), "interface applied");
+    ok(extendedInstance.QueryInterface(Ci.nsIDOMXULControlElement), "inherited interface applied");
   }
   ]]>
   </script>
 </window>
 
--- a/toolkit/content/widgets/dialog.xml
+++ b/toolkit/content/widgets/dialog.xml
@@ -453,16 +453,18 @@
           this.cancelDialog();
       </handler>
 #ifdef XP_MACOSX
       <handler event="keypress" key="." modifiers="meta" phase="capturing" action="this.cancelDialog();"/>
 #else
       <handler event="focus" phase="capturing">
         var btn = this.getButton(this.defaultButton);
         if (btn)
-          btn.setAttribute("default", event.originalTarget == btn || !(event.originalTarget instanceof Ci.nsIDOMXULButtonElement));
+          btn.setAttribute("default", event.originalTarget == btn ||
+                           !(event.originalTarget.localName == "button" ||
+                             event.originalTarget.localName == "toolbarbutton"));
       </handler>
 #endif
     </handlers>
 
   </binding>
 
 </bindings>
--- a/toolkit/content/widgets/menu.xml
+++ b/toolkit/content/widgets/menu.xml
@@ -1,13 +1,13 @@
 <?xml version="1.0"?>
 <!-- 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/. -->
-
+<!-- globals XULMenuElement -->
 
 <bindings id="menuitemBindings"
    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="menuitem-base"
            extends="chrome://global/content/bindings/general.xml#basetext">
@@ -16,30 +16,32 @@
                              onget="return this.getAttribute('value');"/>
       <!-- nsIDOMXULSelectControlItemElement -->
       <property name="selected" readonly="true"
                 onget="return this.getAttribute('selected') == 'true';"/>
       <property name="control" readonly="true">
         <getter>
           <![CDATA[
             var parent = this.parentNode;
-            if (parent &&
-                parent.parentNode instanceof Ci.nsIDOMXULSelectControlElement)
+            // Return the parent if it is a menu or menulist.
+            if (parent && parent.parentNode instanceof XULMenuElement) {
               return parent.parentNode;
+            }
             return null;
           ]]>
         </getter>
       </property>
 
       <!-- nsIDOMXULContainerItemElement -->
       <property name="parentContainer" readonly="true">
         <getter>
           for (var parent = this.parentNode; parent; parent = parent.parentNode) {
-            if (parent instanceof Ci.nsIDOMXULContainerElement)
+            if (parent instanceof XULMenuElement) {
               return parent;
+            }
           }
           return null;
         </getter>
       </property>
     </implementation>
   </binding>
 
   <binding id="menu-base"
--- a/toolkit/content/widgets/richlistbox.xml
+++ b/toolkit/content/widgets/richlistbox.xml
@@ -72,18 +72,17 @@
         <parameter name="aDelta"/>
         <body>
         <![CDATA[
           var prop = this.dir == "reverse" && this._mayReverse ?
                                                 "previousSibling" :
                                                 "nextSibling";
           while (aStartItem) {
             aStartItem = aStartItem[prop];
-            if (aStartItem && aStartItem instanceof
-                Ci.nsIDOMXULSelectControlItemElement &&
+            if (aStartItem && aStartItem.localName == "richlistitem" &&
                 (!this._userSelecting || this._canUserSelect(aStartItem))) {
               --aDelta;
               if (aDelta == 0)
                 return aStartItem;
             }
           }
           return null;
         ]]>
@@ -95,18 +94,17 @@
         <parameter name="aDelta"/>
         <body>
         <![CDATA[
           var prop = this.dir == "reverse" && this._mayReverse ?
                                                 "nextSibling" :
                                                 "previousSibling";
           while (aStartItem) {
             aStartItem = aStartItem[prop];
-            if (aStartItem && aStartItem instanceof
-                Ci.nsIDOMXULSelectControlItemElement &&
+            if (aStartItem && aStartItem.localName == "richlistitem" &&
                 (!this._userSelecting || this._canUserSelect(aStartItem))) {
               --aDelta;
               if (aDelta == 0)
                 return aStartItem;
             }
           }
           return null;
         ]]>
@@ -576,19 +574,18 @@
             return index != this.currentIndex ? index - this.currentIndex : aDirection;
           ]]>
         </body>
       </method>
 
       <property name="itemChildren" readonly="true">
         <getter>
           <![CDATA[
-            let iface = Ci.nsIDOMXULSelectControlItemElement;
             let children = Array.from(this.children)
-                                .filter(node => node instanceof iface);
+                                .filter(node => node.localName == "richlistitem");
             if (this.dir == "reverse" && this._mayReverse) {
               children.reverse();
             }
             return children;
           ]]>
         </getter>
       </property>
 
@@ -1045,17 +1042,17 @@
         ]]></setter>
       </property>
 
       <!-- nsIDOMXULSelectControlItemElement -->
       <property name="control">
         <getter><![CDATA[
           var parent = this.parentNode;
           while (parent) {
-            if (parent instanceof Ci.nsIDOMXULSelectControlElement)
+            if (parent.localName == "richlistbox")
               return parent;
             parent = parent.parentNode;
           }
           return null;
         ]]></getter>
       </property>
 
       <property name="current" onget="return this.getAttribute('current') == 'true';">
--- a/toolkit/content/widgets/tabbox.xml
+++ b/toolkit/content/widgets/tabbox.xml
@@ -386,19 +386,17 @@
 
     <implementation implements="nsIDOMXULSelectControlItemElement">
       <property name="value" onset="this.setAttribute('value', val); return val;"
                              onget="return this.getAttribute('value');"/>
       <property name="control" readonly="true">
         <getter>
           <![CDATA[
             var parent = this.parentNode;
-            if (parent instanceof Ci.nsIDOMXULSelectControlElement)
-              return parent;
-            return null;
+            return (parent.localName == "tabs") ? parent : null;
           ]]>
         </getter>
       </property>
 
       <property name="selected" readonly="true"
                 onget="return this.getAttribute('selected') == 'true';"/>
 
       <property name="_selected">