Merge mozilla-central to autoland. a=merge CLOSED TREE
authorBrindusan Cristian <cbrindusan@mozilla.com>
Tue, 11 Dec 2018 00:10:08 +0200
changeset 509736 e67df0b7834b623caf7f571373d50ba036ca871d
parent 509735 e694a8efd3827fc04030009d9c5d214a4e3dcfec (current diff)
parent 509715 13f891b92db19ea8ec85ef329eff7a793c8b368f (diff)
child 509737 b58ea9ea4468244fdad1b7f5cb1626e9f0ad01e3
push id1953
push userffxbld-merge
push dateMon, 11 Mar 2019 12:10:20 +0000
treeherdermozilla-release@9c35dcbaa899 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone66.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to autoland. a=merge CLOSED TREE
.hgignore
dom/base/Element.h
dom/xul/nsXULElement.cpp
layout/base/PresShell.cpp
--- 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
@@ -188,8 +188,11 @@ subinclude:servo/.hgignore
 \.orig$
 \.rej$
 
 # 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/.hgtags
+++ b/.hgtags
@@ -150,8 +150,9 @@ 034c5ef24e98b0ce85fa849face079f568eb397c
 4a230b07f0cbf48e87dcb4265ea2d00893bb1b62 FIREFOX_BETA_64_BASE
 4a230b07f0cbf48e87dcb4265ea2d00893bb1b62 FIREFOX_BETA_64_BASE
 224715760a637bc37c14794839468a954f1f2695 FIREFOX_BETA_64_BASE
 224715760a637bc37c14794839468a954f1f2695 FIREFOX_BETA_64_BASE
 ad179a6fc14cbd41d10a018ac4a3822db119de3b FIREFOX_BETA_64_BASE
 c44fbdd5173548c9035256dda8fd3512f67118a8 FIREFOX_NIGHTLY_64_END
 58a0412e15574f063cd380517a0369bfb48b22e0 PRE_TREEWIDE_CLANG_FORMAT
 9ad82455dcee2bc1d438e46016b8db00e88758a8 FIREFOX_BETA_65_BASE
+3386ff76878d83496bb822d09115c77472808b53 FIREFOX_NIGHTLY_65_END
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Bug 1499026 - Update to ICU 63 requires clobber
+Merge day clobber
\ No newline at end of file
--- 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/config/version.txt
+++ b/browser/config/version.txt
@@ -1,1 +1,1 @@
-65.0a1
+66.0a1
--- a/browser/config/version_display.txt
+++ b/browser/config/version_display.txt
@@ -1,1 +1,1 @@
-65.0a1
+66.0a1
--- 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/config/milestone.txt
+++ b/config/milestone.txt
@@ -5,9 +5,9 @@
 #    x.x.x.x
 #    x.x.x+
 #
 # Referenced by build/moz.configure/init.configure.
 # Hopefully I'll be able to automate replacement of *all*
 # hardcoded milestones in the tree from these two files.
 #--------------------------------------------------------
 
-65.0a1
+66.0a1
--- 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
@@ -7885,19 +7885,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) {
@@ -7934,28 +7934,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/services/sync/modules/constants.js
+++ b/services/sync/modules/constants.js
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // Process each item in the "constants hash" to add to "global" and give a name
 var EXPORTED_SYMBOLS = [];
 for (let [key, val] of Object.entries({
 
 // Don't manually modify this line, as it is automatically replaced on merge day
 // by the gecko_migration.py script.
-WEAVE_VERSION: "1.67.0",
+WEAVE_VERSION: "1.68.0",
 
 // Sync Server API version that the client supports.
 SYNC_API_VERSION:                      "1.5",
 
 // Version of the data format this client supports. The data format describes
 // how records are packaged; this is separate from the Server API version and
 // the per-engine cleartext formats.
 STORAGE_VERSION:                       5,
--- 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">
--- a/xpcom/components/Module.h
+++ b/xpcom/components/Module.h
@@ -17,17 +17,17 @@ namespace mozilla {
 
 /**
  * A module implements one or more XPCOM components. This structure is used
  * for both binary and script modules, but the registration members
  * (cids/contractids/categoryentries) are unused for modules which are loaded
  * via a module loader.
  */
 struct Module {
-  static const unsigned int kVersion = 65;
+  static const unsigned int kVersion = 66;
 
   struct CIDEntry;
 
   typedef already_AddRefed<nsIFactory> (*GetFactoryProcPtr)(
       const Module& module, const CIDEntry& entry);
 
   typedef nsresult (*ConstructorProcPtr)(nsISupports* aOuter, const nsIID& aIID,
                                          void** aResult);