Merge m-i to m-c, a=merge
authorPhil Ringnalda <philringnalda@gmail.com>
Thu, 15 Dec 2016 17:47:41 -0800
changeset 371049 63b447888a6469b9f6ae8f76ac5f0d7c6ea239da
parent 370990 fe17931bfd5f0658a94f40a71b0463cfa1e03c6c (current diff)
parent 371048 e1b7a57826e0e9523616df493907f7613f9d8dc1 (diff)
child 371050 6e3fa915a1dfd4e516376dc997685d975fdbaf08
child 371068 b67e4dcee6aaa7768c0309b2c0708ae83f104eae
child 371134 7c828bc4d59ee94d181687fab56957bb8074c5ac
push id6996
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 20:48:21 +0000
treeherdermozilla-beta@d89512dab048 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone53.0a1
first release with
nightly linux32
63b447888a64 / 53.0a1 / 20161216030207 / files
nightly linux64
63b447888a64 / 53.0a1 / 20161216030207 / files
nightly mac
63b447888a64 / 53.0a1 / 20161216030207 / files
nightly win32
63b447888a64 / 53.0a1 / 20161216030207 / files
nightly win64
63b447888a64 / 53.0a1 / 20161216030207 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-i to m-c, a=merge MozReview-Commit-ID: 4iKvEwhyJQJ
testing/web-platform/meta/custom-elements/HTMLElement-constructor.html.ini
testing/web-platform/meta/custom-elements/custom-element-registry/define.html.ini
testing/web-platform/meta/custom-elements/htmlconstructor/newtarget.html.ini
--- a/accessible/aom/AccessibleNode.cpp
+++ b/accessible/aom/AccessibleNode.cpp
@@ -71,13 +71,40 @@ AccessibleNode::GetStates(nsTArray<nsStr
     }
     aStates = mStates->StringArray();
     return;
   }
 
   mStates->Add(NS_LITERAL_STRING("defunct"));
 }
 
+bool
+AccessibleNode::Is(const Sequence<nsString>& aFlavors)
+{
+  if (!mIntl) {
+    for (const auto& flavor : aFlavors) {
+      if (!flavor.EqualsLiteral("unknown") && !flavor.EqualsLiteral("defunct")) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  nsAutoString role;
+  GetOrCreateAccService()->GetStringRole(mIntl->Role(), role);
+
+  if (!mStates) {
+    mStates = GetOrCreateAccService()->GetStringStates(mIntl->State());
+  }
+
+  for (const auto& flavor : aFlavors) {
+    if (!flavor.Equals(role) && !mStates->Contains(flavor)) {
+      return false;
+    }
+  }
+  return true;
+}
+
 nsINode*
 AccessibleNode::GetDOMNode()
 {
   return mDOMNode;
 }
--- a/accessible/aom/AccessibleNode.h
+++ b/accessible/aom/AccessibleNode.h
@@ -3,19 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef A11Y_AOM_ACCESSIBLENODE_H
 #define A11Y_AOM_ACCESSIBLENODE_H
 
 #include "nsWrapperCache.h"
-#include "mozilla/RefPtr.h"
-#include "nsTArray.h"
-#include "nsString.h"
+#include "mozilla/dom/BindingDeclarations.h"
 
 class nsINode;
 
 namespace mozilla {
 
 namespace a11y {
   class Accessible;
 }
@@ -36,16 +34,18 @@ public:
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override final;
   virtual dom::ParentObject GetParentObject() const final;
 
   void GetRole(nsAString& aRole);
   void GetStates(nsTArray<nsString>& aStates);
   nsINode* GetDOMNode();
 
+  bool Is(const Sequence<nsString>& aFlavors);
+
   a11y::Accessible* Internal() const { return mIntl; }
 
 protected:
   AccessibleNode(const AccessibleNode& aCopy) = delete;
   AccessibleNode& operator=(const AccessibleNode& aCopy) = delete;
   virtual ~AccessibleNode();
 
   RefPtr<a11y::Accessible> mIntl;
--- a/accessible/aom/moz.build
+++ b/accessible/aom/moz.build
@@ -1,20 +1,20 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 EXPORTS.mozilla.dom += [
-    'AccessibleNode.h'
+    'AccessibleNode.h',
 ]
 
 UNIFIED_SOURCES += [
-    'AccessibleNode.cpp'
+    'AccessibleNode.cpp',
 ]
 
 LOCAL_INCLUDES += [
     '/accessible/base',
     '/accessible/generic',
 ]
 
 if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
--- a/accessible/tests/mochitest/aom/test_general.html
+++ b/accessible/tests/mochitest/aom/test_general.html
@@ -72,12 +72,15 @@
         ok(false, 'Unexpected amount of states');
     }
     if (states) {
       for (var i = 0; i < states.length; i++) {
         is(anode.states[i], states[i], `${states[i]} state is expected at ${i}th index`);
       }
     }
 
+    ok(anode.is('document', 'focusable'),
+       'correct role and state on an accessible node');
+
     finish();
   }
   </script>
 </head>
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -19,17 +19,18 @@
       <xul:tabbox anonid="tabbox" class="tabbrowser-tabbox"
                   flex="1" eventnode="document" xbl:inherits="handleCtrlPageUpDown"
                   onselect="if (event.target.localName == 'tabpanels') this.parentNode.updateCurrentBrowser();">
         <xul:tabpanels flex="1" class="plain" selectedIndex="0" anonid="panelcontainer">
           <xul:notificationbox flex="1" notificationside="top">
             <xul:hbox flex="1" class="browserSidebarContainer">
               <xul:vbox flex="1" class="browserContainer">
                 <xul:stack flex="1" class="browserStack" anonid="browserStack">
-                  <xul:browser anonid="initialBrowser" type="content-primary" message="true" messagemanagergroup="browsers"
+                  <xul:browser anonid="initialBrowser" type="content" message="true" messagemanagergroup="browsers"
+                               primary="true"
                                xbl:inherits="tooltip=contenttooltip,contextmenu=contentcontextmenu,autocompletepopup,selectmenulist,datetimepicker"/>
                 </xul:stack>
               </xul:vbox>
             </xul:hbox>
           </xul:notificationbox>
         </xul:tabpanels>
       </xul:tabbox>
       <children/>
@@ -1087,19 +1088,19 @@
               if (!this._lastRelatedTab.selected)
                 this._lastRelatedTab.owner = null;
               this._lastRelatedTab = null;
             }
 
             var oldBrowser = this.mCurrentBrowser;
 
             if (!gMultiProcessBrowser) {
-              oldBrowser.setAttribute("type", "content-targetable");
+              oldBrowser.removeAttribute("primary");
               oldBrowser.docShellIsActive = false;
-              newBrowser.setAttribute("type", "content-primary");
+              newBrowser.setAttribute("primary", "true");
               newBrowser.docShellIsActive =
                 (window.windowState != window.STATE_MINIMIZED);
             }
 
             var updateBlockedPopups = false;
             if ((oldBrowser.blockedPopups && !newBrowser.blockedPopups) ||
                 (!oldBrowser.blockedPopups && newBrowser.blockedPopups))
               updateBlockedPopups = true;
@@ -1935,17 +1936,17 @@
             // Supported parameters:
             // userContextId, remote, remoteType, isPreloadBrowser,
             // uriIsAboutBlank, permanentKey
 
             const NS_XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
             let b = document.createElementNS(NS_XUL, "browser");
             b.permanentKey = aParams.permanentKey || {};
-            b.setAttribute("type", "content-targetable");
+            b.setAttribute("type", "content");
             b.setAttribute("message", "true");
             b.setAttribute("messagemanagergroup", "browsers");
             b.setAttribute("contextmenu", this.getAttribute("contentcontextmenu"));
             b.setAttribute("tooltip", this.getAttribute("contenttooltip"));
 
             if (aParams.userContextId) {
               b.setAttribute("usercontextid", aParams.userContextId);
             }
@@ -2521,23 +2522,16 @@
         <body>
           <![CDATA[
             if (aTab.closing ||
                 this._windowIsClosing)
               return false;
 
             var browser = this.getBrowserForTab(aTab);
 
-            // Start closing the FrameLoaderOwners which are inactive, so that
-            // they are cleaned up as well.
-            let frameLoader = browser.frameLoader;
-            if (frameLoader && frameLoader.groupedSessionHistory) {
-              frameLoader.groupedSessionHistory.closeInactiveFrameLoaderOwners();
-            }
-
             if (!aTab._pendingPermitUnload && !aAdoptedByTab && !aSkipPermitUnload) {
               // We need to block while calling permitUnload() because it
               // processes the event queue and may lead to another removeTab()
               // call before permitUnload() returns.
               aTab._pendingPermitUnload = true;
               let {permitUnload, timedOut} = browser.permitUnload();
               delete aTab._pendingPermitUnload;
               // If we were closed during onbeforeunload, we return false now
@@ -2617,17 +2611,17 @@
 
             if (browser.registeredOpenURI && !aAdoptedByTab) {
               this._unifiedComplete.unregisterOpenPage(browser.registeredOpenURI,
                                                        browser.getAttribute("usercontextid") || 0);
               delete browser.registeredOpenURI;
             }
 
             // We are no longer the primary content area.
-            browser.setAttribute("type", "content-targetable");
+            browser.removeAttribute("primary");
 
             // Remove this tab as the owner of any other tabs, since it's going away.
             for (let tab of this.tabs) {
               if ("owner" in tab && tab.owner == aTab)
                 // |tab| is a child of the tab we're removing, make it an orphan
                 tab.owner = null;
             }
 
@@ -3752,26 +3746,26 @@
               this.assert(!this.loadTimer);
               this.assert(!this.loadingTab);
               this.assert(this.lastVisibleTab === this.requestedTab);
               this.assert(this.minimized || this.getTabState(this.requestedTab) == this.STATE_LOADED);
 
               this.destroy();
 
               let toBrowser = this.requestedTab.linkedBrowser;
-              toBrowser.setAttribute("type", "content-primary");
+              toBrowser.setAttribute("primary", "true");
 
               this.tabbrowser._adjustFocusAfterTabSwitch(this.requestedTab);
 
               let fromBrowser = this.originalTab.linkedBrowser;
               // It's possible that the tab we're switching from closed
               // before we were able to finalize, in which case, fromBrowser
               // doesn't exist.
               if (fromBrowser) {
-                fromBrowser.setAttribute("type", "content-targetable");
+                fromBrowser.removeAttribute("primary");
               }
 
               let event = new CustomEvent("TabSwitchDone", {
                 bubbles: true,
                 cancelable: true
               });
               this.tabbrowser.dispatchEvent(event);
             },
--- a/browser/themes/shared/autocomplete.inc.css
+++ b/browser/themes/shared/autocomplete.inc.css
@@ -35,14 +35,30 @@
 
 #PopupAutoComplete > richlistbox > richlistitem[originaltype="login"] > .ac-site-icon[selected] {
   list-style-image: url(chrome://browser/skin/notification-icons.svg#login-highlighted);
 }
 
 
 /* Insecure field warning */
 #PopupAutoComplete > richlistbox > richlistitem[originaltype="insecureWarning"] {
-  background-color: #F6F6F6; /* Bug 1319176 */
+  background-color: var(--arrowpanel-dimmed);
+  border-bottom: 1px solid var(--panel-separator-color);
+  padding-bottom: 4px;
+  padding-top: 4px;
+}
+
+#PopupAutoComplete > richlistbox > richlistitem[originaltype="insecureWarning"][selected] {
+  background-color: var(--arrowpanel-dimmed-further);
+}
+
+#PopupAutoComplete > richlistbox > richlistitem[originaltype="insecureWarning"] > .ac-title {
+  color: GrayText;
+  font-size: 1em;
+}
+
+#PopupAutoComplete > richlistbox > richlistitem[originaltype="insecureWarning"][selected] > .ac-title {
+  color: inherit;
 }
 
 #PopupAutoComplete > richlistbox > richlistitem[originaltype="insecureWarning"] > .ac-site-icon {
   list-style-image: url(chrome://browser/skin/connection-mixed-active-loaded.svg#icon);
 }
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -1703,39 +1703,52 @@ nsDocShell::FirePageHideNotification(boo
     // Now make sure our editor, if any, is detached before we go
     // any farther.
     DetachEditorFromWindow();
   }
 
   return NS_OK;
 }
 
-void
+bool
 nsDocShell::MaybeInitTiming()
 {
   if (mTiming && !mBlankTiming) {
-    return;
-  }
+    return false;
+  }
+
+  bool canBeReset = false;
 
   if (mScriptGlobal && mBlankTiming) {
     nsPIDOMWindowInner* innerWin =
       mScriptGlobal->AsOuter()->GetCurrentInnerWindow();
     if (innerWin && innerWin->GetPerformance()) {
       mTiming = innerWin->GetPerformance()->GetDOMTiming();
       mBlankTiming = false;
     }
   }
 
   if (!mTiming) {
     mTiming = new nsDOMNavigationTiming();
+    canBeReset = true;
   }
 
   mTiming->NotifyNavigationStart(
     mIsActive ? nsDOMNavigationTiming::DocShellState::eActive
               : nsDOMNavigationTiming::DocShellState::eInactive);
+
+  return canBeReset;
+}
+
+void
+nsDocShell::MaybeResetInitTiming(bool aReset)
+{
+  if (aReset) {
+    mTiming = nullptr;
+  }
 }
 
 //
 // Bug 13871: Prevent frameset spoofing
 //
 // This routine answers: 'Is origin's document from same domain as
 // target's document?'
 //
@@ -7281,17 +7294,17 @@ nsDocShell::OnStateChange(nsIWebProgress
     uri->GetAsciiSpec(aURI);
 
     nsCOMPtr<nsIWyciwygChannel> wcwgChannel(do_QueryInterface(aRequest));
     nsCOMPtr<nsIWebProgress> webProgress =
       do_QueryInterface(GetAsSupports(this));
 
     // We don't update navigation timing for wyciwyg channels
     if (this == aProgress && !wcwgChannel) {
-      MaybeInitTiming();
+      mozilla::Unused << MaybeInitTiming();
       mTiming->NotifyFetchStart(uri,
                                 ConvertLoadTypeToNavigationType(mLoadType));
     }
 
     // Was the wyciwyg document loaded on this docshell?
     if (wcwgChannel && !mLSHE && (mItemType == typeContent) &&
         aProgress == webProgress.get()) {
       bool equalUri = true;
@@ -7963,32 +7976,33 @@ nsDocShell::CreateAboutBlankContentViewe
     MOZ_ASSERT(ChromeUtils::IsOriginAttributesEqualIgnoringAddonId(
       BasePrincipal::Cast(aPrincipal)->OriginAttributesRef(),
       mOriginAttributes));
   }
 
   // Make sure timing is created.  But first record whether we had it
   // already, so we don't clobber the timing for an in-progress load.
   bool hadTiming = mTiming;
-  MaybeInitTiming();
+  bool toBeReset = MaybeInitTiming();
   if (mContentViewer) {
     // We've got a content viewer already. Make sure the user
     // permits us to discard the current document and replace it
     // with about:blank. And also ensure we fire the unload events
     // in the current document.
 
     // Unload gets fired first for
     // document loaded from the session history.
     mTiming->NotifyBeforeUnload();
 
     bool okToUnload;
     rv = mContentViewer->PermitUnload(&okToUnload);
 
     if (NS_SUCCEEDED(rv) && !okToUnload) {
       // The user chose not to unload the page, interrupt the load.
+      MaybeResetInitTiming(toBeReset);
       return NS_ERROR_FAILURE;
     }
 
     mSavingOldViewer = aTryToSaveOldPresentation &&
                        CanSavePresentation(LOAD_NORMAL, nullptr, nullptr);
 
     if (mTiming) {
       mTiming->NotifyUnloadAccepted(mCurrentURI);
@@ -10476,36 +10490,35 @@ nsDocShell::InternalLoad(nsIURI* aURI,
   // (bug#331040)
   nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
 
   // Don't init timing for javascript:, since it generally doesn't
   // actually start a load or anything.  If it does, we'll init
   // timing then, from OnStateChange.
 
   // XXXbz mTiming should know what channel it's for, so we don't
-  // need this hackery.  Note that this is still broken in cases
-  // when we're loading something that's not javascript: and the
-  // beforeunload handler denies the load.  That will screw up
-  // timing for the next load!
+  // need this hackery.
+  bool toBeReset = false;
   if (!isJavaScript) {
-    MaybeInitTiming();
+    toBeReset = MaybeInitTiming();
   }
   bool timeBeforeUnload = aFileName.IsVoid();
   if (mTiming && timeBeforeUnload) {
     mTiming->NotifyBeforeUnload();
   }
   // Check if the page doesn't want to be unloaded. The javascript:
   // protocol handler deals with this for javascript: URLs.
   if (!isJavaScript && aFileName.IsVoid() && mContentViewer) {
     bool okToUnload;
     rv = mContentViewer->PermitUnload(&okToUnload);
 
     if (NS_SUCCEEDED(rv) && !okToUnload) {
       // The user chose not to unload the page, interrupt the
       // load.
+      MaybeResetInitTiming(toBeReset);
       return NS_OK;
     }
   }
 
   if (mTiming && timeBeforeUnload) {
     mTiming->NotifyUnloadAccepted(mCurrentURI);
   }
 
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -742,19 +742,25 @@ protected:
 
   // Override the parent setter from nsDocLoader
   virtual nsresult SetDocLoaderParent(nsDocLoader* aLoader) override;
 
   void ClearFrameHistory(nsISHEntry* aEntry);
 
   /**
    * Initializes mTiming if it isn't yet.
-   * After calling this, mTiming is non-null.
+   * After calling this, mTiming is non-null. This method returns true if the
+   * initialization of the Timing can be reset (basically this is true if a new
+   * Timing object is created).
+   * In case the loading is aborted, MaybeResetInitTiming() can be called
+   * passing the return value of MaybeInitTiming(): if it's possible to reset
+   * the Timing, this method will do it.
    */
-  void MaybeInitTiming();
+  MOZ_MUST_USE bool MaybeInitTiming();
+  void MaybeResetInitTiming(bool aReset);
 
   bool DisplayLoadError(nsresult aError, nsIURI* aURI, const char16_t* aURL,
                         nsIChannel* aFailedChannel)
   {
     bool didDisplayLoadError = false;
     DisplayLoadError(aError, aURI, aURL, aFailedChannel, &didDisplayLoadError);
     return didDisplayLoadError;
   }
--- a/docshell/base/nsIDocShellTreeOwner.idl
+++ b/docshell/base/nsIDocShellTreeOwner.idl
@@ -18,21 +18,19 @@ interface nsIDocShellTreeOwner : nsISupp
 {
 	/**
 	 * Called when a content shell is added to the docshell tree.  This is
 	 * _only_ called for "root" content shells (that is, ones whose parent is a
 	 * chrome shell).
 	 *
 	 * @param aContentShell the shell being added.
 	 * @param aPrimary whether the shell is primary.
-	 * @param aID the "id" of the shell.  What this actually means is
-	 *			undefined. Don't rely on this for anything.
 	 */
 	void contentShellAdded(in nsIDocShellTreeItem aContentShell,
-	                       in boolean aPrimary, in AString aID);
+	                       in boolean aPrimary);
 
 	/**
 	 * Called when a content shell is removed from the docshell tree.  This is
 	 * _only_ called for "root" content shells (that is, ones whose parent is a
 	 * chrome shell).  Note that if aContentShell was never added,
 	 * contentShellRemoved should just do nothing.
 	 *
 	 * @param aContentShell the shell being removed.
--- a/docshell/shistory/nsIGroupedSHistory.idl
+++ b/docshell/shistory/nsIGroupedSHistory.idl
@@ -24,20 +24,23 @@ interface nsIGroupedSHistory : nsISuppor
   /**
    * Remove all partial histories after currently active one (if any) and then
    * append the given partial session history to the end of the list.
    */
   void appendPartialSessionHistory(in nsIPartialSHistory aPartialHistory);
 
   /**
    * Notify the grouped session history that the active partial session history
-   * has been modified. All partial session histories after the active one
-   * will be removed and destroy.
+   * has been modified.
+   *
+   * @param aPartialHistory The partial history which was updated
+   * @param aTruncate If this parameter is true, all partial session histories
+   *                  after this one will be removed.
    */
-  void onPartialSessionHistoryChange(in nsIPartialSHistory aPartialHistory);
+  void handleSHistoryUpdate(in nsIPartialSHistory aPartialHistory, in boolean aTruncate);
 
   /**
    * Find the proper partial session history and navigate to the entry
    * corresponding to the given global index. Note it doesn't swap frameloaders,
    * but rather return the target loader for the caller to swap.
    *
    * This function may throw NS_ERROR_NOT_AVAILABLE if the frameloader to swap
    * to is dead.
--- a/docshell/shistory/nsIPartialSHistory.idl
+++ b/docshell/shistory/nsIPartialSHistory.idl
@@ -12,16 +12,19 @@ interface nsIFrameLoader;
  * a "partial" nsISHistory in either local or remote process.
  */
 [scriptable, builtinclass, uuid(5cd75e28-838c-4a0a-972e-6005f736ef7a)]
 interface nsIPartialSHistory : nsISupports
 {
   // The number of entries of its corresponding nsISHistory.
   [infallible] readonly attribute unsigned long count;
 
+  // The current global index of the active shentry in this partialSHistory.
+  [infallible] readonly attribute long globalIndex;
+
   // If it's part of a grouped session history, globalIndexOffset denotes the
   // number of entries ahead.
   [infallible] readonly attribute unsigned long globalIndexOffset;
 
   // The frameloader which owns this partial session history.
   readonly attribute nsIFrameLoader ownerFrameLoader;
 
   /**
@@ -29,25 +32,25 @@ interface nsIPartialSHistory : nsISuppor
    * it's becoming the active partial history of the group.
    *
    * @param aOffset                The number of entries in preceding partial
    *                               session histories.
    */
   void onAttachGroupedSessionHistory(in unsigned long aOffset);
 
   /**
-   * Notify that one or more entries in its associated nsISHistory object
-   * have been changed (i.e. add / remove / replace). It's mainly used for
-   * cross-process case, since in the in-process case we can just register an
-   * nsISHistoryListener instead.
+   * This method is used by the TabParent to notify the PartialSHistory
+   * that the state of its corresponding nsISHistory in the content process
+   * has been updated. It is unused in the in-process case.
    *
-   * @param aCount The number of entries in the associated session history.
-   *               It can be the same as the old value if entries were replaced.
+   * @param aCount      The number of entries in the associated session history.
+   * @param aLocalIndex The local index of the currently active entry in the
+   *                    associated session history
    */
-  void onSessionHistoryChange(in unsigned long aCount);
+  void handleSHistoryUpdate(in unsigned long aCount, in unsigned long aLocalIndex, in boolean aTruncate);
 
   /**
    * Notify that the partial session history has been swapped in as the active
    * session history. Only an active session history can possibly add / remove /
    * replace its history entries.
    *
    * @param aGlobalLength      The up-to-date global length.
    * @param aTargetLocalIndex  The local index to navigate to.
--- a/docshell/shistory/nsISHistory.idl
+++ b/docshell/shistory/nsISHistory.idl
@@ -28,16 +28,25 @@ interface nsIPartialSHistoryListener;
 
 #define NS_SHISTORY_CONTRACTID "@mozilla.org/browser/shistory;1"
 %}
 
 [scriptable, uuid(7b807041-e60a-4384-935f-af3061d8b815)]
 interface nsISHistory: nsISupports
 {
   /**
+   * The size of the window of SHEntries which can have alive viewers in the
+   * bfcache around the currently active SHEntry.
+   *
+   * We try to keep viewers for SHEntries between index - VIEWER_WINDOW and
+   * index + VIEWER_WINDOW alive.
+   */
+  const long VIEWER_WINDOW = 3;
+
+  /**
    * An attribute denoting whether the nsISHistory is associated to a grouped
    * session history.
    *
    * The abstraction of grouped session history is implemented at
    * nsIWebNavigation level, so those canGoBack / canGoForward / gotoIndex
    * functions work transparently;
    *
    * On the other hand, nsISHistory works on partial session history directly.
--- a/docshell/shistory/nsISHistoryListener.idl
+++ b/docshell/shistory/nsISHistoryListener.idl
@@ -108,10 +108,17 @@ interface nsISHistoryListener : nsISuppo
    */
    void OnHistoryReplaceEntry(in long aIndex);
 
    /**
     * Called when nsISHistory::count has been updated. Unlike OnHistoryNewEntry
     * and OnHistoryPurge which happen before the modifications are actually done
     * and maybe cancellable, this function is called after these modifications.
     */
-   void OnLengthChange(in long aCount);
+   void OnLengthChanged(in long aCount);
+
+  /**
+   * Called when nsISHistory::index has been updated. Unlike the other methods
+   * on this interface, which happen before the modifications are actually done
+   * and maybe cancellable, this function is called after these modifications.
+   */
+   void OnIndexChanged(in long aIndex);
 };
--- a/docshell/shistory/nsSHistory.cpp
+++ b/docshell/shistory/nsSHistory.cpp
@@ -43,18 +43,16 @@ using namespace mozilla;
 
 static const char* kObservedPrefs[] = {
   PREF_SHISTORY_SIZE,
   PREF_SHISTORY_MAX_TOTAL_VIEWERS,
   nullptr
 };
 
 static int32_t gHistoryMaxSize = 50;
-// Max viewers allowed per SHistory objects
-static const int32_t gHistoryMaxViewers = 3;
 // List of all SHistory objects, used for content viewer cache eviction
 static PRCList gSHistoryList;
 // Max viewers allowed total, across all SHistory objects - negative default
 // means we will calculate how many viewers to cache based on total memory
 int32_t nsSHistory::sHistoryMaxTotalViewers = -1;
 
 // A counter that is used to be able to know the order in which
 // entries were touched, so that we can evict older entries first.
@@ -425,17 +423,18 @@ nsSHistory::AddEntry(nsISHEntry* aSHEntr
   // parent will properly set the parent child relationship
   txn->SetPersist(aPersist);
   NS_ENSURE_SUCCESS(txn->Create(aSHEntry, currentTxn), NS_ERROR_FAILURE);
 
   // A little tricky math here...  Basically when adding an object regardless of
   // what the length was before, it should always be set back to the current and
   // lop off the forward.
   mLength = (++mIndex + 1);
-  NOTIFY_LISTENERS(OnLengthChange, (mLength));
+  NOTIFY_LISTENERS(OnLengthChanged, (mLength));
+  NOTIFY_LISTENERS(OnIndexChanged, (mIndex));
 
   // Much like how mLength works above, when changing our entries, all following
   // partial histories should be purged, so we just reset the number to zero.
   mEntriesInFollowingPartialHistories = 0;
 
   // If this is the very first transaction, initialize the list
   if (!mListRoot) {
     mListRoot = txn;
@@ -556,16 +555,17 @@ nsSHistory::GetEntryAtIndex(int32_t aInd
   rv = GetTransactionAtIndex(aIndex, getter_AddRefs(txn));
   if (NS_SUCCEEDED(rv) && txn) {
     // Get the Entry from the transaction
     rv = txn->GetSHEntry(aResult);
     if (NS_SUCCEEDED(rv) && (*aResult)) {
       // Set mIndex to the requested index, if asked to do so..
       if (aModifyIndex) {
         mIndex = aIndex;
+        NOTIFY_LISTENERS(OnIndexChanged, (mIndex))
       }
     }
   }
   return rv;
 }
 
 /* Get the transaction at a given index */
 NS_IMETHODIMP
@@ -779,27 +779,29 @@ nsSHistory::PurgeHistory(int32_t aEntrie
     mListRoot = nextTxn;
     if (mListRoot) {
       mListRoot->SetPrev(nullptr);
     }
     cnt++;
   }
   mLength -= cnt;
   mIndex -= cnt;
-  NOTIFY_LISTENERS(OnLengthChange, (mLength));
 
   // All following partial histories will be deleted in this case.
   mEntriesInFollowingPartialHistories = 0;
 
   // Now if we were not at the end of the history, mIndex could have
   // become far too negative.  If so, just set it to -1.
   if (mIndex < -1) {
     mIndex = -1;
   }
 
+  NOTIFY_LISTENERS(OnLengthChanged, (mLength));
+  NOTIFY_LISTENERS(OnIndexChanged, (mIndex))
+
   if (mRootDocShell) {
     mRootDocShell->HistoryPurged(cnt);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -1036,61 +1038,61 @@ nsSHistory::RestoreToEntryAtIndex(int32_
 
 void
 nsSHistory::EvictOutOfRangeWindowContentViewers(int32_t aIndex)
 {
   // XXX rename method to EvictContentViewersExceptAroundIndex, or something.
 
   // We need to release all content viewers that are no longer in the range
   //
-  //  aIndex - gHistoryMaxViewers to aIndex + gHistoryMaxViewers
+  //  aIndex - VIEWER_WINDOW to aIndex + VIEWER_WINDOW
   //
   // to ensure that this SHistory object isn't responsible for more than
-  // gHistoryMaxViewers content viewers.  But our job is complicated by the
+  // VIEWER_WINDOW content viewers.  But our job is complicated by the
   // fact that two transactions which are related by either hash navigations or
   // history.pushState will have the same content viewer.
   //
-  // To illustrate the issue, suppose gHistoryMaxViewers = 3 and we have four
+  // To illustrate the issue, suppose VIEWER_WINDOW = 3 and we have four
   // linked transactions in our history.  Suppose we then add a new content
   // viewer and call into this function.  So the history looks like:
   //
   //   A A A A B
   //     +     *
   //
   // where the letters are content viewers and + and * denote the beginning and
-  // end of the range aIndex +/- gHistoryMaxViewers.
+  // end of the range aIndex +/- VIEWER_WINDOW.
   //
   // Although one copy of the content viewer A exists outside the range, we
   // don't want to evict A, because it has other copies in range!
   //
   // We therefore adjust our eviction strategy to read:
   //
   //   Evict each content viewer outside the range aIndex -/+
-  //   gHistoryMaxViewers, unless that content viewer also appears within the
+  //   VIEWER_WINDOW, unless that content viewer also appears within the
   //   range.
   //
   // (Note that it's entirely legal to have two copies of one content viewer
   // separated by a different content viewer -- call pushState twice, go back
   // once, and refresh -- so we can't rely on identical viewers only appearing
   // adjacent to one another.)
 
   if (aIndex < 0) {
     return;
   }
   NS_ENSURE_TRUE_VOID(aIndex < mLength);
 
   // Calculate the range that's safe from eviction.
-  int32_t startSafeIndex = std::max(0, aIndex - gHistoryMaxViewers);
-  int32_t endSafeIndex = std::min(mLength, aIndex + gHistoryMaxViewers);
+  int32_t startSafeIndex = std::max(0, aIndex - nsISHistory::VIEWER_WINDOW);
+  int32_t endSafeIndex = std::min(mLength, aIndex + nsISHistory::VIEWER_WINDOW);
 
   LOG(("EvictOutOfRangeWindowContentViewers(index=%d), "
        "mLength=%d. Safe range [%d, %d]",
        aIndex, mLength, startSafeIndex, endSafeIndex));
 
-  // The content viewers in range aIndex -/+ gHistoryMaxViewers will not be
+  // The content viewers in range aIndex -/+ VIEWER_WINDOW will not be
   // evicted.  Collect a set of them so we don't accidentally evict one of them
   // if it appears outside this range.
   nsCOMArray<nsIContentViewer> safeViewers;
   nsCOMPtr<nsISHTransaction> trans;
   GetTransactionAtIndex(startSafeIndex, getter_AddRefs(trans));
   for (int32_t i = startSafeIndex; trans && i <= endSafeIndex; i++) {
     nsCOMPtr<nsIContentViewer> viewer = GetContentViewerForTransaction(trans);
     safeViewers.AppendObject(viewer);
@@ -1177,31 +1179,31 @@ nsSHistory::GloballyEvictContentViewers(
     nsSHistory* shist = static_cast<nsSHistory*>(listEntry);
 
     // Maintain a list of the transactions which have viewers and belong to
     // this particular shist object.  We'll add this list to the global list,
     // |transactions|, eventually.
     nsTArray<TransactionAndDistance> shTransactions;
 
     // Content viewers are likely to exist only within shist->mIndex -/+
-    // gHistoryMaxViewers, so only search within that range.
+    // VIEWER_WINDOW, so only search within that range.
     //
     // A content viewer might exist outside that range due to either:
     //
     //   * history.pushState or hash navigations, in which case a copy of the
     //     content viewer should exist within the range, or
     //
     //   * bugs which cause us not to call nsSHistory::EvictContentViewers()
     //     often enough.  Once we do call EvictContentViewers() for the
     //     SHistory object in question, we'll do a full search of its history
     //     and evict the out-of-range content viewers, so we don't bother here.
     //
-    int32_t startIndex = std::max(0, shist->mIndex - gHistoryMaxViewers);
+    int32_t startIndex = std::max(0, shist->mIndex - nsISHistory::VIEWER_WINDOW);
     int32_t endIndex = std::min(shist->mLength - 1,
-                                shist->mIndex + gHistoryMaxViewers);
+                                shist->mIndex + nsISHistory::VIEWER_WINDOW);
     nsCOMPtr<nsISHTransaction> trans;
     shist->GetTransactionAtIndex(startIndex, getter_AddRefs(trans));
     for (int32_t i = startIndex; trans && i <= endIndex; i++) {
       nsCOMPtr<nsIContentViewer> contentViewer =
         GetContentViewerForTransaction(trans);
 
       if (contentViewer) {
         // Because one content viewer might belong to multiple SHEntries, we
@@ -1254,18 +1256,18 @@ nsSHistory::GloballyEvictContentViewers(
        --i) {
     EvictContentViewerForTransaction(transactions[i].mTransaction);
   }
 }
 
 nsresult
 nsSHistory::EvictExpiredContentViewerForEntry(nsIBFCacheEntry* aEntry)
 {
-  int32_t startIndex = std::max(0, mIndex - gHistoryMaxViewers);
-  int32_t endIndex = std::min(mLength - 1, mIndex + gHistoryMaxViewers);
+  int32_t startIndex = std::max(0, mIndex - nsISHistory::VIEWER_WINDOW);
+  int32_t endIndex = std::min(mLength - 1, mIndex + nsISHistory::VIEWER_WINDOW);
   nsCOMPtr<nsISHTransaction> trans;
   GetTransactionAtIndex(startIndex, getter_AddRefs(trans));
 
   int32_t i;
   for (i = startIndex; trans && i <= endIndex; ++i) {
     nsCOMPtr<nsISHEntry> entry;
     trans->GetSHEntry(getter_AddRefs(entry));
 
@@ -1453,16 +1455,17 @@ nsSHistory::RemoveDuplicate(int32_t aInd
     }
     if (mRootDocShell) {
       static_cast<nsDocShell*>(mRootDocShell)->HistoryTransactionRemoved(aIndex);
     }
 
     // Adjust our indices to reflect the removed transaction
     if (mIndex > aIndex) {
       mIndex = mIndex - 1;
+      NOTIFY_LISTENERS(OnIndexChanged, (mIndex));
     }
 
     // NB: If the transaction we are removing is the transaction currently
     // being navigated to (mRequestedIndex) then we adjust the index
     // only if we're not keeping the next entry (because if we are keeping
     // the next entry (because the current is a duplicate of the next), then
     // that entry slides into the spot that we're currently pointing to.
     // We don't do this adjustment for mIndex because mIndex cannot equal
@@ -1472,17 +1475,17 @@ nsSHistory::RemoveDuplicate(int32_t aInd
     // because either they're strictly greater than aIndex which is at least
     // zero, or they are equal to aIndex in which case aKeepNext must be true
     // if aIndex is zero.
     if (mRequestedIndex > aIndex || (mRequestedIndex == aIndex && !aKeepNext)) {
       mRequestedIndex = mRequestedIndex - 1;
     }
     --mLength;
     mEntriesInFollowingPartialHistories = 0;
-    NOTIFY_LISTENERS(OnLengthChange, (mLength));
+    NOTIFY_LISTENERS(OnLengthChanged, (mLength));
     return true;
   }
   return false;
 }
 
 NS_IMETHODIMP_(void)
 nsSHistory::RemoveEntries(nsTArray<nsID>& aIDs, int32_t aStartIndex)
 {
@@ -1542,16 +1545,17 @@ nsSHistory::RemoveDynEntries(int32_t aOl
 
 NS_IMETHODIMP
 nsSHistory::UpdateIndex()
 {
   // Update the actual index with the right value.
   if (mIndex != mRequestedIndex && mRequestedIndex != -1) {
     RemoveDynEntries(mIndex, mRequestedIndex);
     mIndex = mRequestedIndex;
+    NOTIFY_LISTENERS(OnIndexChanged, (mIndex))
   }
 
   mRequestedIndex = -1;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSHistory::Stop(uint32_t aStopFlags)
--- a/docshell/test/browser/browser.ini
+++ b/docshell/test/browser/browser.ini
@@ -88,8 +88,12 @@ support-files =
 [browser_timelineMarkers-02.js]
 skip-if = true # Bug 1220415
 [browser_timelineMarkers-03.js]
 [browser_timelineMarkers-04.js]
 [browser_timelineMarkers-05.js]
 [browser_ua_emulation.js]
 [browser_grouped_shistory_dead_navigate.js]
 skip-if = !e10s
+[browser_grouped_shistory_crossproc.js]
+skip-if = !e10s
+[browser_grouped_shistory_bfcache_cleaning.js]
+skip-if = !e10s
new file mode 100644
--- /dev/null
+++ b/docshell/test/browser/browser_grouped_shistory_bfcache_cleaning.js
@@ -0,0 +1,61 @@
+add_task(function* () {
+  yield SpecialPowers.pushPrefEnv({
+    set: [["browser.groupedhistory.enabled", true]]
+  });
+
+  // Wait for a process change and then fulfil the promise.
+  function awaitProcessChange(browser) {
+    return new Promise(resolve => {
+      browser.addEventListener("BrowserChangedProcess", function bcp(e) {
+        browser.removeEventListener("BrowserChangedProcess", bcp);
+        ok(true, "The browser changed process!");
+        resolve();
+      });
+    });
+  }
+
+  function isAlive(tab) {
+    return tab.linkedBrowser &&
+      tab.linkedBrowser.frameLoader &&
+      !tab.linkedBrowser.frameLoader.isDead;
+  }
+
+  yield BrowserTestUtils.withNewTab({ gBrowser, url: "data:text/html,a" }, function* (browser1) {
+    // Set up the grouped SHEntry setup
+    let tab2 = gBrowser.loadOneTab("data:text/html,b", {
+      referrerPolicy: Ci.nsIHttpChannel.REFERRER_POLICY_DEFAULT,
+      allowThirdPartyFixup: true,
+      relatedToCurrent: true,
+      isPrerendered: true,
+    });
+    yield BrowserTestUtils.browserLoaded(tab2.linkedBrowser);
+    browser1.frameLoader.appendPartialSessionHistoryAndSwap(
+      tab2.linkedBrowser.frameLoader);
+    yield awaitProcessChange(browser1);
+    ok(isAlive(tab2));
+
+    // Load some URIs and make sure that we lose the old process once we are 3 history entries away.
+    browser1.loadURI("data:text/html,c", null, null);
+    yield BrowserTestUtils.browserLoaded(browser1);
+    ok(isAlive(tab2), "frameloader should still be alive");
+    browser1.loadURI("data:text/html,d", null, null);
+    yield BrowserTestUtils.browserLoaded(browser1);
+    ok(isAlive(tab2), "frameloader should still be alive");
+    browser1.loadURI("data:text/html,e", null, null);
+    yield BrowserTestUtils.browserLoaded(browser1);
+    ok(isAlive(tab2), "frameloader should still be alive");
+
+    // The 4th navigation should kill the frameloader
+    browser1.loadURI("data:text/html,f", null, null);
+    yield new Promise(resolve => {
+      tab2.addEventListener("TabClose", function f() {
+        tab2.removeEventListener("TabClose", f);
+        ok(true, "The tab is being closed!\n");
+        resolve();
+      });
+    });
+    // We don't check for !isAlive() as TabClose is called during
+    // _beginRemoveTab, which means that the frameloader may not be dead yet. We
+    // avoid races by not checking.
+  });
+});
new file mode 100644
--- /dev/null
+++ b/docshell/test/browser/browser_grouped_shistory_crossproc.js
@@ -0,0 +1,41 @@
+add_task(function* () {
+  yield SpecialPowers.pushPrefEnv({
+    set: [["browser.groupedhistory.enabled", true]]
+  });
+
+  // Wait for a process change and then fulfil the promise.
+  function awaitProcessChange(browser) {
+    return new Promise(resolve => {
+      browser.addEventListener("BrowserChangedProcess", function bcp(e) {
+        browser.removeEventListener("BrowserChangedProcess", bcp);
+        ok(true, "The browser changed process!");
+        resolve();
+      });
+    });
+  }
+
+  yield BrowserTestUtils.withNewTab({ gBrowser, url: "data:text/html,a" }, function* (browser1) {
+    // Set up the grouped SHEntry setup
+    let tab2 = gBrowser.loadOneTab("data:text/html,b", {
+      referrerPolicy: Ci.nsIHttpChannel.REFERRER_POLICY_DEFAULT,
+      allowThirdPartyFixup: true,
+      relatedToCurrent: true,
+      isPrerendered: true,
+    });
+    yield BrowserTestUtils.browserLoaded(tab2.linkedBrowser);
+    browser1.frameLoader.appendPartialSessionHistoryAndSwap(
+      tab2.linkedBrowser.frameLoader);
+    yield awaitProcessChange(browser1);
+
+    // Load a URI which will involve loading in the parent process
+    browser1.loadURI("about:config", Ci.nsIWebNavigation.LOAD_FLAGS_NONE, null, null, null);
+    yield BrowserTestUtils.browserLoaded(browser1);
+    let docshell = browser1.frameLoader.docShell.QueryInterface(Ci.nsIWebNavigation);
+    ok(docshell, "The browser should be loaded in the chrome process");
+    is(docshell.canGoForward, false, "canGoForward is correct");
+    is(docshell.canGoBack, true, "canGoBack is correct");
+    is(docshell.sessionHistory.count, 3, "Count is correct");
+    is(browser1.frameLoader.groupedSessionHistory, null,
+       "browser1's session history is now complete");
+  });
+});
--- a/docshell/test/chrome/bug112564_window.xul
+++ b/docshell/test/chrome/bug112564_window.xul
@@ -106,10 +106,10 @@
                    {type: "pageshow", title: "test1", persisted: true}];
       gBrowser.goBack();
       yield undefined;
 
       finish();
     }
   ]]></script>
 
-  <browser type="content-primary" flex="1" id="content" src="about:blank"/>
+  <browser type="content" primary="true" flex="1" id="content" src="about:blank"/>
 </window>
--- a/docshell/test/chrome/bug215405_window.xul
+++ b/docshell/test/chrome/bug215405_window.xul
@@ -151,10 +151,10 @@
          " vertical axis scroll position not correctly restored");
       var formValue = gBrowser.contentDocument.getElementById("inp").value;
       is(formValue, text, testName + " form value not correctly restored");
       
       finish();
     }
   ]]></script>
 
-  <browser type="content-primary" flex="1" id="content" src="about:blank"/>
+  <browser type="content" primary="true" flex="1" id="content" src="about:blank"/>
 </window>
--- a/docshell/test/chrome/bug293235_window.xul
+++ b/docshell/test/chrome/bug293235_window.xul
@@ -151,10 +151,10 @@
          "visited link should be purple");
 
       // Tell the framework the test is finished.
       finish();
     }
 
   ]]></script>
 
-  <browser type="content-primary" flex="1" id="content" src="about:blank"/>
+  <browser type="content" primary="true" flex="1" id="content" src="about:blank"/>
 </window>
--- a/docshell/test/chrome/bug294258_window.xul
+++ b/docshell/test/chrome/bug294258_window.xul
@@ -65,10 +65,10 @@
       is($("select").selectedIndex, 2, "Select value changed");
 
       // Tell the framework the test is finished.
       finish();
     }
     
   ]]></script>
 
-  <browser type="content-primary" flex="1" id="content" src="about:blank"/>
+  <browser type="content" primary="true" flex="1" id="content" src="about:blank"/>
 </window>
--- a/docshell/test/chrome/bug298622_window.xul
+++ b/docshell/test/chrome/bug298622_window.xul
@@ -136,11 +136,11 @@
     }
     
   ]]></script>
 
   <commandset>
     <command id="cmd_find"
      oncommand="document.getElementById('FindToolbar').onFindCommand();"/>
   </commandset>
-  <browser type="content-primary" flex="1" id="content" src="about:blank"/>
+  <browser type="content" primary="true" flex="1" id="content" src="about:blank"/>
   <findbar id="FindToolbar" browserid="content"/>
 </window>
--- a/docshell/test/chrome/bug301397_window.xul
+++ b/docshell/test/chrome/bug301397_window.xul
@@ -239,10 +239,10 @@
       verifyIframeInnerHtml("You made it");
       
       // Tell the framework the test is finished.
       finish();
     }
     
   ]]></script>
 
-  <browser type="content-primary" flex="1" id="content" src="about:blank"/>
+  <browser type="content" primary="true" flex="1" id="content" src="about:blank"/>
 </window>
--- a/docshell/test/chrome/bug303267_window.xul
+++ b/docshell/test/chrome/bug303267_window.xul
@@ -90,10 +90,10 @@
     // Return the innerHTML of a particular element in the content document.
     //
     function getInnerHTMLById(id) {
       return TestWindow.getDocument().getElementById(id).innerHTML;
     }
     
   ]]></script>
 
-  <browser type="content-primary" flex="1" id="content" src="about:blank"/>
+  <browser type="content" primary="true" flex="1" id="content" src="about:blank"/>
 </window>
--- a/docshell/test/chrome/bug311007_window.xul
+++ b/docshell/test/chrome/bug311007_window.xul
@@ -190,10 +190,10 @@ function step4B(aWebProgress, aRequest, 
 
   /* End. */
   aWebProgress.removeProgressListener(gListener);
   delete(gListener);
   finish();
 }
   ]]></script>
 
-  <browser type="content-primary" flex="1" id="content" src="about:blank"/>
+  <browser type="content" primary="true" flex="1" id="content" src="about:blank"/>
 </window>
--- a/docshell/test/chrome/bug321671_window.xul
+++ b/docshell/test/chrome/bug321671_window.xul
@@ -122,10 +122,10 @@
       }
       
       // Tell the framework the test is finished.
       finish();
     }
     
   ]]></script>
 
-  <browser type="content-primary" flex="1" id="content" src="about:blank"/>
+  <browser type="content" primary="true" flex="1" id="content" src="about:blank"/>
 </window>
--- a/docshell/test/chrome/bug360511_window.xul
+++ b/docshell/test/chrome/bug360511_window.xul
@@ -123,10 +123,10 @@
       }
                       
       // Tell the framework the test is finished.
       finish();
     }
     
   ]]></script>
 
-  <browser type="content-primary" flex="1" id="content" src="about:blank"/>
+  <browser type="content" primary="true" flex="1" id="content" src="about:blank"/>
 </window>
--- a/docshell/test/chrome/bug364461_window.xul
+++ b/docshell/test/chrome/bug364461_window.xul
@@ -261,10 +261,10 @@
       
       is(gBrowser.currentURI.spec, "data:text/plain,aaa",
          "Navigation is blocked when clicking link");
 
       finish();
     }
   ]]></script>
 
-  <browser type="content-primary" flex="1" id="content" src="about:blank"/>
+  <browser type="content" primary="true" flex="1" id="content" src="about:blank"/>
 </window>
--- a/docshell/test/chrome/bug396519_window.xul
+++ b/docshell/test/chrome/bug396519_window.xul
@@ -158,10 +158,10 @@
       gExpected = [false, false, true, true, false, false];
       gBrowser.gotoIndex(5);
       yield undefined;
 
       finish();
     }
   ]]></script>
 
-  <browser type="content-primary" flex="1" id="content" src="about:blank"/>
+  <browser type="content" primary="true" flex="1" id="content" src="about:blank"/>
 </window>
--- a/docshell/test/chrome/bug396649_window.xul
+++ b/docshell/test/chrome/bug396649_window.xul
@@ -111,10 +111,10 @@
       }
       
       // Tell the framework the test is finished.
       finish();
     }
     
   ]]></script>
 
-  <browser type="content-primary" flex="1" id="content" src="about:blank"/>
+  <browser type="content" primary="true" flex="1" id="content" src="about:blank"/>
 </window>
--- a/docshell/test/chrome/bug582176_window.xul
+++ b/docshell/test/chrome/bug582176_window.xul
@@ -77,10 +77,10 @@
 
       os.removeObserver(observer, "content-document-global-created")
 
       // Tell the framework the test is finished.
       finish();
     }
     
   ]]></script>
-  <browser type="content-primary" flex="1" id="content" src="about:blank"/>
+  <browser type="content" primary="true" flex="1" id="content" src="about:blank"/>
 </window>
--- a/docshell/test/chrome/bug662200_window.xul
+++ b/docshell/test/chrome/bug662200_window.xul
@@ -118,10 +118,10 @@
       is(shistory.requestedIndex, -1, "Requested index should be cleared!");
 
       // Tell the framework the test is finished.
       finish();
     }
     
   ]]></script>
 
-  <browser type="content-primary" flex="1" id="content" src="about:blank"/>
+  <browser type="content" primary="true" flex="1" id="content" src="about:blank"/>
 </window>
--- a/docshell/test/chrome/bug690056_window.xul
+++ b/docshell/test/chrome/bug690056_window.xul
@@ -165,10 +165,10 @@
       });
       yield undefined;
 
       // Tell the framework the test is finished.
       finish();
     }
   ]]></script>
 
-  <browser type="content-primary" flex="1" id="content" src="about:blank"/>
+  <browser type="content" primary="true" flex="1" id="content" src="about:blank"/>
 </window>
--- a/docshell/test/chrome/bug89419_window.xul
+++ b/docshell/test/chrome/bug89419_window.xul
@@ -71,10 +71,10 @@
       ok(snapshotsEqual(first, third), "going back should be the same as the initial load");
 
       // Tell the framework the test is finished.
       finish();
     }
 
   ]]></script>
 
-  <browser type="content-primary" flex="1" id="content" src="about:blank"/>
+  <browser type="content" primary="true" flex="1" id="content" src="about:blank"/>
 </window>
--- a/docshell/test/chrome/bug92598_window.xul
+++ b/docshell/test/chrome/bug92598_window.xul
@@ -107,10 +107,10 @@
                    {type: "pageshow", title: "test1", persisted: false}];
       gBrowser.goBack();
       yield undefined;
 
       finish();
     }
   ]]></script>
 
-  <browser type="content-primary" flex="1" id="content" src="about:blank"/>
+  <browser type="content" primary="true" flex="1" id="content" src="about:blank"/>
 </window>
--- a/docshell/test/chrome/window.template.txt
+++ b/docshell/test/chrome/window.template.txt
@@ -35,10 +35,10 @@
       // Tell the framework the test is finished.  Include the final 'yield' 
       // statement to prevent a StopIteration exception from being thrown.
       finish();
       yield undefined;
     }
     
   ]]></script>
 
-  <browser type="content-primary" flex="1" id="content" src="about:blank"/>
+  <browser type="content" primary="true" flex="1" id="content" src="about:blank"/>
 </window>
--- a/dom/base/CustomElementRegistry.cpp
+++ b/dom/base/CustomElementRegistry.cpp
@@ -98,16 +98,17 @@ CustomElementData::RunCallbackQueue()
   mCurrentCallback = -1;
 }
 
 // Only needed for refcounted objects.
 NS_IMPL_CYCLE_COLLECTION_CLASS(CustomElementRegistry)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CustomElementRegistry)
   tmp->mCustomDefinitions.Clear();
+  tmp->mConstructors.clear();
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mWhenDefinedPromiseMap)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CustomElementRegistry)
   for (auto iter = tmp->mCustomDefinitions.Iter(); !iter.Done(); iter.Next()) {
     nsAutoPtr<LifecycleCallbacks>& callbacks = iter.UserData()->mCallbacks;
@@ -145,16 +146,22 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Cus
   for (auto iter = tmp->mCustomDefinitions.Iter(); !iter.Done(); iter.Next()) {
     aCallbacks.Trace(&iter.UserData()->mConstructor,
                      "mCustomDefinitions constructor",
                      aClosure);
     aCallbacks.Trace(&iter.UserData()->mPrototype,
                      "mCustomDefinitions prototype",
                      aClosure);
   }
+
+  for (ConstructorMap::Enum iter(tmp->mConstructors); !iter.empty(); iter.popFront()) {
+    aCallbacks.Trace(&iter.front().mutableKey(),
+                     "mConstructors key",
+                     aClosure);
+  }
   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(CustomElementRegistry)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(CustomElementRegistry)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CustomElementRegistry)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
@@ -179,16 +186,21 @@ CustomElementRegistry::Create(nsPIDOMWin
   }
 
   if (!IsCustomElementEnabled()) {
     return nullptr;
   }
 
   RefPtr<CustomElementRegistry> customElementRegistry =
     new CustomElementRegistry(aWindow);
+
+  if (!customElementRegistry->Init()) {
+    return nullptr;
+  }
+
   return customElementRegistry.forget();
 }
 
 /* static */ void
 CustomElementRegistry::ProcessTopElementQueue()
 {
   MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
 
@@ -237,31 +249,54 @@ CustomElementRegistry::CustomElementRegi
   }
 }
 
 CustomElementRegistry::~CustomElementRegistry()
 {
   mozilla::DropJSObjects(this);
 }
 
+bool
+CustomElementRegistry::Init()
+{
+  return mConstructors.init();
+}
+
 CustomElementDefinition*
 CustomElementRegistry::LookupCustomElementDefinition(const nsAString& aLocalName,
                                                      const nsAString* aIs) const
 {
   nsCOMPtr<nsIAtom> localNameAtom = NS_Atomize(aLocalName);
   nsCOMPtr<nsIAtom> typeAtom = aIs ? NS_Atomize(*aIs) : localNameAtom;
 
   CustomElementDefinition* data = mCustomDefinitions.Get(typeAtom);
   if (data && data->mLocalName == localNameAtom) {
     return data;
   }
 
   return nullptr;
 }
 
+CustomElementDefinition*
+CustomElementRegistry::LookupCustomElementDefinition(JSContext* aCx,
+                                                     JSObject* aConstructor) const
+{
+  JS::Rooted<JSObject*> constructor(aCx, js::CheckedUnwrap(aConstructor));
+
+  const auto& ptr = mConstructors.lookup(constructor);
+  if (!ptr) {
+    return nullptr;
+  }
+
+  CustomElementDefinition* definition = mCustomDefinitions.Get(ptr->value());
+  MOZ_ASSERT(definition, "Definition must be found in mCustomDefinitions");
+
+  return definition;
+}
+
 void
 CustomElementRegistry::RegisterUnresolvedElement(Element* aElement, nsIAtom* aTypeName)
 {
   mozilla::dom::NodeInfo* info = aElement->NodeInfo();
 
   // Candidate may be a custom element through extension,
   // in which case the custom element type name will not
   // match the element tag name. e.g. <button is="x-button">.
@@ -605,19 +640,23 @@ CustomElementRegistry::Define(const nsAS
     aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
     return;
   }
 
   /**
    * 4. If this CustomElementRegistry contains an entry with constructor constructor,
    *    then throw a "NotSupportedError" DOMException and abort these steps.
    */
-  // TODO: Step 3 of HTMLConstructor also needs a way to look up definition by
-  // using constructor. So I plans to figure out a solution to support both of
-  // them in bug 1274159.
+  const auto& ptr = mConstructors.lookup(constructorUnwrapped);
+  if (ptr) {
+    MOZ_ASSERT(mCustomDefinitions.Get(ptr->value()),
+               "Definition must be found in mCustomDefinitions");
+    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return;
+  }
 
   /**
    * 5. Let localName be name.
    * 6. Let extends be the value of the extends member of options, or null if
    *    no such member exists.
    * 7. If extends is not null, then:
    *    1. If extends is a valid custom element name, then throw a
    *       "NotSupportedError" DOMException.
@@ -763,18 +802,26 @@ CustomElementRegistry::Define(const nsAS
                                 constructor,
                                 constructorPrototype,
                                 callbacks,
                                 0 /* TODO dependent on HTML imports. Bug 877072 */);
 
   /**
    * 12. Add definition to this CustomElementRegistry.
    */
+  if (!mConstructors.put(constructorUnwrapped, nameAtom)) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return;
+  }
+
   mCustomDefinitions.Put(nameAtom, definition);
 
+  MOZ_ASSERT(mCustomDefinitions.Count() == mConstructors.count(),
+             "Number of entries should be the same");
+
   /**
    * 13. 14. 15. Upgrade candidates
    */
   // TODO: Bug 1299363 - Implement custom element v1 upgrade algorithm
   UpgradeCandidates(cx, nameAtom, definition);
 
   /**
    * 16. If this CustomElementRegistry's when-defined promise map contains an
@@ -849,9 +896,9 @@ CustomElementDefinition::CustomElementDe
     mConstructor(aConstructor),
     mPrototype(aPrototype),
     mCallbacks(aCallbacks),
     mDocOrder(aDocOrder)
 {
 }
 
 } // namespace dom
-} // namespace mozilla
\ No newline at end of file
+} // namespace mozilla
--- a/dom/base/CustomElementRegistry.h
+++ b/dom/base/CustomElementRegistry.h
@@ -2,23 +2,24 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_CustomElementRegistry_h
 #define mozilla_dom_CustomElementRegistry_h
 
+#include "js/GCHashTable.h"
 #include "js/TypeDecls.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/dom/FunctionBinding.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsWrapperCache.h"
-#include "mozilla/dom/FunctionBinding.h"
 
 class nsDocument;
 
 namespace mozilla {
 namespace dom {
 
 struct CustomElementData;
 struct ElementDefinitionOptions;
@@ -122,16 +123,20 @@ struct CustomElementDefinition
   // The lifecycle callbacks to call for this custom element.
   nsAutoPtr<mozilla::dom::LifecycleCallbacks> mCallbacks;
 
   // A construction stack.
   // TODO: Bug 1287348 - Implement construction stack for upgrading an element
 
   // The document custom element order.
   uint32_t mDocOrder;
+
+  bool IsCustomBuiltIn() {
+    return mType != mLocalName;
+  }
 };
 
 class CustomElementRegistry final : public nsISupports,
                                     public nsWrapperCache
 {
   // Allow nsDocument to access mCustomDefinitions and mCandidatesMap.
   friend class ::nsDocument;
 
@@ -149,16 +154,19 @@ public:
 
   /**
    * Looking up a custom element definition.
    * https://html.spec.whatwg.org/#look-up-a-custom-element-definition
    */
   CustomElementDefinition* LookupCustomElementDefinition(
     const nsAString& aLocalName, const nsAString* aIs = nullptr) const;
 
+  CustomElementDefinition* LookupCustomElementDefinition(
+    JSContext* aCx, JSObject *aConstructor) const;
+
   /**
    * Enqueue created callback or register upgrade candidate for
    * newly created custom elements, possibly extending an existing type.
    * ex. <x-button>, <button is="x-button> (type extension)
    */
   void SetupCustomElement(Element* aElement, const nsAString* aTypeExtension);
 
   void EnqueueLifecycleCallback(nsIDocument::ElementCallbackType aType,
@@ -168,16 +176,18 @@ public:
 
   void GetCustomPrototype(nsIAtom* aAtom,
                           JS::MutableHandle<JSObject*> aPrototype);
 
 private:
   explicit CustomElementRegistry(nsPIDOMWindowInner* aWindow);
   ~CustomElementRegistry();
 
+  bool Init();
+
   /**
    * Registers an unresolved custom element that is a candidate for
    * upgrade when the definition is registered via registerElement.
    * |aTypeName| is the name of the custom element type, if it is not
    * provided, then element name is used. |aTypeName| should be provided
    * when registering a custom element that extends an existing
    * element. e.g. <button is="x-button">.
    */
@@ -187,25 +197,35 @@ private:
   void UpgradeCandidates(JSContext* aCx,
                          nsIAtom* aKey,
                          CustomElementDefinition* aDefinition);
 
   typedef nsClassHashtable<nsISupportsHashKey, CustomElementDefinition>
     DefinitionMap;
   typedef nsClassHashtable<nsISupportsHashKey, nsTArray<nsWeakPtr>>
     CandidateMap;
+  typedef JS::GCHashMap<JS::Heap<JSObject*>,
+                        nsCOMPtr<nsIAtom>,
+                        js::MovableCellHasher<JS::Heap<JSObject*>>,
+                        js::SystemAllocPolicy> ConstructorMap;
 
   // Hashtable for custom element definitions in web components.
   // Custom prototypes are stored in the compartment where
   // registerElement was called.
   DefinitionMap mCustomDefinitions;
 
+  // Hashtable for looking up definitions by using constructor as key.
+  // Custom elements' name are stored here and we need to lookup
+  // mCustomDefinitions again to get definitions.
+  ConstructorMap mConstructors;
+
   typedef nsRefPtrHashtable<nsISupportsHashKey, Promise>
     WhenDefinedPromiseMap;
   WhenDefinedPromiseMap mWhenDefinedPromiseMap;
+
   // The "upgrade candidates map" from the web components spec. Maps from a
   // namespace id and local name to a list of elements to upgrade if that
   // element is registered as a custom element.
   CandidateMap mCandidatesMap;
 
   nsCOMPtr<nsPIDOMWindowInner> mWindow;
 
   // Array representing the processing stack in the custom elements
--- a/dom/base/GroupedSHistory.cpp
+++ b/dom/base/GroupedSHistory.cpp
@@ -65,38 +65,65 @@ GroupedSHistory::AppendPartialSessionHis
   mPartialHistories.AppendElement(partialHistory);
   partialHistory->OnAttachGroupedSessionHistory(offset);
   mIndexOfActivePartialHistory = mPartialHistories.Count() - 1;
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-GroupedSHistory::OnPartialSessionHistoryChange(
-  nsIPartialSHistory* aPartialSessionHistory)
+GroupedSHistory::HandleSHistoryUpdate(nsIPartialSHistory* aPartial, bool aTruncate)
 {
-  if (!aPartialSessionHistory) {
+  if (!aPartial) {
     return NS_ERROR_INVALID_POINTER;
   }
+  nsCOMPtr<nsIPartialSHistory> partialHistory = aPartial;
+
+  int32_t index = partialHistory->GetGlobalIndex();
+  // Get the lower and upper bounds for the viewer window
+  int32_t lower = index - nsISHistory::VIEWER_WINDOW;
+  int32_t upper = index + nsISHistory::VIEWER_WINDOW;
+  for (uint32_t i = 0; i < mPartialHistories.Length(); ++i) {
+    nsIPartialSHistory* pHistory = mPartialHistories[i];
+    // Skip the active partial history.
+    if (pHistory == partialHistory) {
+      continue;
+    }
+
+    // Check if the given partialshistory entry is too far away in history, and
+    // if it is, close it.
+    int32_t thisCount = pHistory->GetCount();
+    int32_t thisOffset = pHistory->GetGlobalIndexOffset();
+    if ((thisOffset > upper) || ((thisCount + thisOffset) < lower)) {
+      nsCOMPtr<nsIFrameLoader> loader;
+      pHistory->GetOwnerFrameLoader(getter_AddRefs(loader));
+      if (loader && !loader->GetIsDead()) {
+        loader->RequestFrameLoaderClose();
+      }
+    }
+  }
 
-  nsCOMPtr<nsIPartialSHistory> partialHistory(aPartialSessionHistory);
-  int32_t index = mPartialHistories.IndexOf(partialHistory);
-  if (NS_WARN_IF(index != mIndexOfActivePartialHistory) ||
-      NS_WARN_IF(index < 0)) {
-    // Non-active or not attached partialHistory
-    return NS_ERROR_UNEXPECTED;
+  // If we should be truncating, make sure to purge any partialSHistories which
+  // follow the one being updated.
+  if (aTruncate) {
+    int32_t index = mPartialHistories.IndexOf(partialHistory);
+    if (NS_WARN_IF(index != mIndexOfActivePartialHistory) ||
+        NS_WARN_IF(index < 0)) {
+      // Non-active or not attached partialHistory
+      return NS_ERROR_UNEXPECTED;
+    }
+
+    PurgePartialHistories(index);
+
+    // Update global count.
+    uint32_t count = partialHistory->GetCount();
+    uint32_t offset = partialHistory->GetGlobalIndexOffset();
+    mCount = count + offset;
   }
 
-  PurgePartialHistories(index);
-
-  // Update global count.
-  uint32_t count = partialHistory->GetCount();
-  uint32_t offset = partialHistory->GetGlobalIndexOffset();
-  mCount = count + offset;
-
   return NS_OK;
 }
 
 NS_IMETHODIMP
 GroupedSHistory::GotoIndex(uint32_t aGlobalIndex,
                            nsIFrameLoader** aTargetLoaderToSwap)
 {
   MOZ_ASSERT(aTargetLoaderToSwap);
--- a/dom/base/PartialSHistory.cpp
+++ b/dom/base/PartialSHistory.cpp
@@ -85,16 +85,37 @@ PartialSHistory::GetCount(uint32_t* aRes
   }
 
   // Otherwise use the cached value.
   *aResult = mCount;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+PartialSHistory::GetGlobalIndex(int32_t* aResult)
+{
+  if (!aResult) {
+    return NS_ERROR_INVALID_POINTER;
+  }
+
+  nsCOMPtr<nsISHistory> shistory = GetSessionHistory();
+  if (shistory) {
+    int32_t idx;
+    nsresult rv = shistory->GetIndex(&idx);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    *aResult = idx + GetGlobalIndexOffset();
+    return NS_OK;
+  }
+
+  *aResult = mIndex + GetGlobalIndexOffset();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 PartialSHistory::GetGlobalIndexOffset(uint32_t* aResult)
 {
   if (!aResult) {
     return NS_ERROR_INVALID_POINTER;
   }
 
   // If we have direct reference to nsISHistory, simply pass through.
   nsCOMPtr<nsISHistory> shistory(GetSessionHistory());
@@ -145,20 +166,42 @@ PartialSHistory::OnAttachGroupedSessionH
     return NS_ERROR_UNEXPECTED;
   }
   Unused << tabParent->SendNotifyAttachGroupedSessionHistory(aOffset);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-PartialSHistory::OnSessionHistoryChange(uint32_t aCount)
+PartialSHistory::HandleSHistoryUpdate(uint32_t aCount, uint32_t aIndex, bool aTruncate)
+{
+  // Update our local cache of mCount and mIndex
+  mCount = aCount;
+  mIndex = aIndex;
+  return SHistoryDidUpdate(aTruncate);
+}
+
+nsresult
+PartialSHistory::SHistoryDidUpdate(bool aTruncate /* = false */)
 {
-  mCount = aCount;
-  return OnLengthChange(aCount);
+  if (!mOwnerFrameLoader) {
+    // Cycle collected?
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  nsCOMPtr<nsIGroupedSHistory> groupedHistory;
+  mOwnerFrameLoader->GetGroupedSessionHistory(getter_AddRefs(groupedHistory));
+  if (NS_WARN_IF(!groupedHistory)) {
+    // Maybe we're not the active partial history, but in this case we shouldn't
+    // receive any update from session history object either.
+    return NS_ERROR_FAILURE;
+  }
+
+  groupedHistory->HandleSHistoryUpdate(this, aTruncate);
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 PartialSHistory::OnActive(uint32_t aGlobalLength, uint32_t aTargetLocalIndex)
 {
   // In-process case.
   nsCOMPtr<nsISHistory> shistory(GetSessionHistory());
   if (shistory) {
@@ -222,37 +265,25 @@ PartialSHistory::OnRequestCrossBrowserNa
   return mOwnerFrameLoader->RequestGroupedHistoryNavigation(aIndex, getter_AddRefs(promise));
 }
 
 /*******************************************************************************
  * nsISHistoryListener
  ******************************************************************************/
 
 NS_IMETHODIMP
-PartialSHistory::OnLengthChange(int32_t aCount)
+PartialSHistory::OnLengthChanged(int32_t aCount)
 {
-  if (!mOwnerFrameLoader) {
-    // Cycle collected?
-    return NS_ERROR_UNEXPECTED;
-  }
-
-  if (aCount < 0) {
-    return NS_ERROR_FAILURE;
-  }
+  return SHistoryDidUpdate(/* aTruncate = */ true);
+}
 
-  nsCOMPtr<nsIGroupedSHistory> groupedHistory;
-  mOwnerFrameLoader->GetGroupedSessionHistory(getter_AddRefs(groupedHistory));
-  if (!groupedHistory) {
-    // Maybe we're not the active partial history, but in this case we shouldn't
-    // receive any update from session history object either.
-    return NS_ERROR_FAILURE;
-  }
-
-  groupedHistory->OnPartialSessionHistoryChange(this);
-  return NS_OK;
+NS_IMETHODIMP
+PartialSHistory::OnIndexChanged(int32_t aIndex)
+{
+  return SHistoryDidUpdate(/* aTruncate = */ false);
 }
 
 NS_IMETHODIMP
 PartialSHistory::OnHistoryNewEntry(nsIURI *aNewURI, int32_t aOldIndex)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
--- a/dom/base/PartialSHistory.h
+++ b/dom/base/PartialSHistory.h
@@ -37,21 +37,26 @@ public:
    */
   explicit PartialSHistory(nsIFrameLoader* aOwnerFrameLoader);
 
 private:
   ~PartialSHistory() {}
   already_AddRefed<nsISHistory> GetSessionHistory();
   already_AddRefed<TabParent> GetTabParent();
 
+  nsresult SHistoryDidUpdate(bool aTruncate = false);
+
   // The cache of number of entries in corresponding nsISHistory. It's only
   // used for remote process case. If nsISHistory is in-process, mCount will not
   // be used at all.
   uint32_t mCount;
 
+  // The current local index of the active document in this partial SHistory.
+  uint32_t mIndex;
+
   // The cache of globalIndexOffset in corresponding nsISHistory. It's only
   // used for remote process case.
   uint32_t mGlobalIndexOffset;
 
   // The frameloader which owns this PartialSHistory.
   nsCOMPtr<nsIFrameLoader> mOwnerFrameLoader;
 };
 
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -962,26 +962,18 @@ nsFrameLoader::AddTreeItemToTreeOwner(ns
                                       nsIDocShellTreeOwner* aOwner,
                                       int32_t aParentType,
                                       nsIDocShell* aParentNode)
 {
   NS_PRECONDITION(aItem, "Must have docshell treeitem");
   NS_PRECONDITION(mOwnerContent, "Must have owning content");
 
   nsAutoString value;
-  bool isContent = false;
-  mOwnerContent->GetAttr(kNameSpaceID_None, TypeAttrName(), value);
-
-  // we accept "content" and "content-xxx" values.
-  // at time of writing, we expect "xxx" to be "primary" or "targetable", but
-  // someday it might be an integer expressing priority or something else.
-
-  isContent = value.LowerCaseEqualsLiteral("content") ||
-    StringBeginsWith(value, NS_LITERAL_STRING("content-"),
-                     nsCaseInsensitiveStringComparator());
+  bool isContent = mOwnerContent->AttrValueIs(
+    kNameSpaceID_None, TypeAttrName(), nsGkAtoms::content, eIgnoreCase);
 
   // Force mozbrowser frames to always be typeContent, even if the
   // mozbrowser interfaces are disabled.
   nsCOMPtr<nsIDOMMozBrowserFrame> mozbrowser =
     do_QueryInterface(mOwnerContent);
   if (mozbrowser) {
     bool isMozbrowser = false;
     mozbrowser->GetMozbrowser(&isMozbrowser);
@@ -1004,22 +996,23 @@ nsFrameLoader::AddTreeItemToTreeOwner(ns
   if (aParentNode) {
     aParentNode->AddChild(aItem);
   }
 
   bool retval = false;
   if (aParentType == nsIDocShellTreeItem::typeChrome && isContent) {
     retval = true;
 
-    bool is_primary = value.LowerCaseEqualsLiteral("content-primary");
-
+    bool is_primary =
+      mOwnerContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::primary,
+                                 nsGkAtoms::_true, eIgnoreCase);
     if (aOwner) {
       mOwnerContent->AddMutationObserver(this);
       mObservingOwnerContent = true;
-      aOwner->ContentShellAdded(aItem, is_primary, value);
+      aOwner->ContentShellAdded(aItem, is_primary);
     }
   }
 
   return retval;
 }
 
 static bool
 AllDescendantsOfType(nsIDocShellTreeItem* aParentItem, int32_t aType)
@@ -1990,16 +1983,21 @@ nsFrameLoader::StartDestroy()
   // Let our window know that we are gone
   if (mDocShell) {
     nsCOMPtr<nsPIDOMWindowOuter> win_private(mDocShell->GetWindow());
     if (win_private) {
       win_private->SetFrameElementInternal(nullptr);
     }
   }
 
+  // Destroy the other frame loader owners now that we are being destroyed.
+  if (mGroupedSessionHistory) {
+    mGroupedSessionHistory->CloseInactiveFrameLoaderOwners();
+  }
+
   nsCOMPtr<nsIRunnable> destroyRunnable = new nsFrameLoaderDestroyRunnable(this);
   if (mNeedsAsyncDestroy || !doc ||
       NS_FAILED(doc->FinalizeFrameLoader(this, destroyRunnable))) {
     NS_DispatchToCurrentThread(destroyRunnable);
   }
 }
 
 nsresult
@@ -2862,22 +2860,18 @@ nsFrameLoader::TryRemoteBrowser()
         return false;
       }
     }
 
     if (!mOwnerContent->IsXULElement()) {
       return false;
     }
 
-    nsAutoString value;
-    mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::type, value);
-
-    if (!value.LowerCaseEqualsLiteral("content") &&
-        !StringBeginsWith(value, NS_LITERAL_STRING("content-"),
-                          nsCaseInsensitiveStringComparator())) {
+    if (!mOwnerContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
+                                    nsGkAtoms::content, eIgnoreCase)) {
       return false;
     }
 
     // Try to get the related content parent from our browser element.
     openerContentParent = GetContentParent(mOwnerContent);
   }
 
   uint32_t chromeFlags = 0;
@@ -3278,17 +3272,18 @@ nsFrameLoader::AttributeChanged(nsIDocum
                                 mozilla::dom::Element* aElement,
                                 int32_t      aNameSpaceID,
                                 nsIAtom*     aAttribute,
                                 int32_t      aModType,
                                 const nsAttrValue* aOldValue)
 {
   MOZ_ASSERT(mObservingOwnerContent);
 
-  if (aNameSpaceID != kNameSpaceID_None || aAttribute != TypeAttrName()) {
+  if (aNameSpaceID != kNameSpaceID_None ||
+      (aAttribute != TypeAttrName() && aAttribute != nsGkAtoms::primary)) {
     return;
   }
 
   if (aElement != mOwnerContent) {
     return;
   }
 
   // Note: This logic duplicates a lot of logic in
@@ -3313,36 +3308,31 @@ nsFrameLoader::AttributeChanged(nsIDocum
   }
 
   nsCOMPtr<nsIDocShellTreeOwner> parentTreeOwner;
   parentItem->GetTreeOwner(getter_AddRefs(parentTreeOwner));
   if (!parentTreeOwner) {
     return;
   }
 
-  nsAutoString value;
-  aElement->GetAttr(kNameSpaceID_None, TypeAttrName(), value);
-
-  bool is_primary = value.LowerCaseEqualsLiteral("content-primary");
+  bool is_primary =
+    aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::primary, nsGkAtoms::_true, eIgnoreCase);
 
 #ifdef MOZ_XUL
   // when a content panel is no longer primary, hide any open popups it may have
   if (!is_primary) {
     nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
     if (pm)
       pm->HidePopupsInDocShell(mDocShell);
   }
 #endif
 
   parentTreeOwner->ContentShellRemoved(mDocShell);
-  if (value.LowerCaseEqualsLiteral("content") ||
-      StringBeginsWith(value, NS_LITERAL_STRING("content-"),
-                       nsCaseInsensitiveStringComparator())) {
-
-    parentTreeOwner->ContentShellAdded(mDocShell, is_primary, value);
+  if (aElement->AttrValueIs(kNameSpaceID_None, TypeAttrName(), nsGkAtoms::content, eIgnoreCase)) {
+    parentTreeOwner->ContentShellAdded(mDocShell, is_primary);
   }
 }
 
 /**
  * Send the RequestNotifyAfterRemotePaint message to the current Tab.
  */
 NS_IMETHODIMP
 nsFrameLoader::RequestNotifyAfterRemotePaint()
@@ -3541,20 +3531,18 @@ nsFrameLoader::MaybeUpdatePrimaryTabPare
     if (!mObservingOwnerContent) {
       mOwnerContent->AddMutationObserver(this);
       mObservingOwnerContent = true;
     }
 
     parentTreeOwner->TabParentRemoved(mRemoteBrowser);
     if (aChange == eTabParentChanged) {
       bool isPrimary =
-        mOwnerContent->AttrValueIs(kNameSpaceID_None,
-                                   TypeAttrName(),
-                                   NS_LITERAL_STRING("content-primary"),
-                                   eIgnoreCase);
+        mOwnerContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::primary,
+                                   nsGkAtoms::_true, eIgnoreCase);
       parentTreeOwner->TabParentAdded(mRemoteBrowser, isPrimary);
     }
   }
 }
 
 nsresult
 nsFrameLoader::GetNewTabContext(MutableTabContext* aTabContext,
                                 nsIURI* aURI)
--- a/dom/base/test/chrome/file_bug1209621.xul
+++ b/dom/base/test/chrome/file_bug1209621.xul
@@ -25,53 +25,53 @@ https://bugzilla.mozilla.org/show_bug.cg
 
   function run() {
     var docshell = window.getInterface(Ci.nsIDocShell);
     ok(docshell, "Active window should have a DocShell");
     var treeOwner = docshell.treeOwner;
     ok(treeOwner, "Active docshell should have a TreeOwner!");
 
     is(treeOwner.primaryContentShell, null,
-       "There shouldn't be primaryContentShell because no browser has type=content-primary.");
+       "There shouldn't be primaryContentShell because no browser has primary=true.");
     is(treeOwner.primaryTabParent, null,
-       "There shouldn't be primaryTabParent because no remote browser has type=content-primary.");
+       "There shouldn't be primaryTabParent because no remote browser has primary=true.");
 
     var ip = document.getElementById("inprocess");
     var remote = document.getElementById("remote");
     var remote2 = document.getElementById("remote2");
 
-    ip.setAttribute("type", "content-primary");
+    ip.setAttribute("primary", "true");
     ok(ip.docShell, "non-remote browser should have a DocShell.");
     is(treeOwner.primaryContentShell, ip.docShell,
-       "content-primary browser should be the primaryContentShell.");
+       "primary browser should be the primaryContentShell.");
     is(treeOwner.primaryTabParent, null,
-       "There shouldn't be primaryTabParent because no remote browser has type=content-primary.");
+       "There shouldn't be primaryTabParent because no remote browser has primary=true.");
 
-    ip.setAttribute("type", "content");
-    remote.setAttribute("type", "content-primary");
+    ip.removeAttribute("primary");
+    remote.setAttribute("primary", "true");
     is(treeOwner.primaryContentShell, null,
-       "There shouldn't be primaryContentShell because no browser has type=content-primary.");
+       "There shouldn't be primaryContentShell because no browser has primary=true.");
     var tp = remote.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader.tabParent;
     ok(tp, "Remote browsers should have a TabParent.");
     is(treeOwner.primaryTabParent, tp,
-       "content-primary remote browser should be the primaryTabParent.");
+       "primary remote browser should be the primaryTabParent.");
 
-    remote.setAttribute("type", "content");
+    remote.removeAttribute("primary");
     is(treeOwner.primaryContentShell, null,
-       "There shouldn't be primaryContentShell because no browser has type=content-primary.");
+       "There shouldn't be primaryContentShell because no browser has primary=true.");
     is(treeOwner.primaryTabParent, null,
-       "There shouldn't be primaryTabParent because no remote browser has type=content-primary.");
+       "There shouldn't be primaryTabParent because no remote browser has primary=true.");
 
-    remote2.setAttribute("type", "content-primary");
+    remote2.setAttribute("primary", "true");
     var tp2 = remote2.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader.tabParent;
     ok(tp2, "Remote browsers should have a TabParent.");
     is(treeOwner.primaryTabParent, tp2,
-       "content-primary remote browser should be the primaryTabParent.");
+       "primary remote browser should be the primaryTabParent.");
     is(treeOwner.primaryContentShell, null,
-       "There shouldn't be primaryContentShell because no browser has type=content-primary.");
+       "There shouldn't be primaryContentShell because no browser has primary=true.");
 
     opener.setTimeout("done()", 0);
     window.close();
   }
 
   ]]></script>
   <browser type="content" src="about:blank" id="inprocess"/>
   <browser type="content" remote="true" src="about:blank" id="remote"/>
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -14,41 +14,45 @@
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/SizePrintfMacros.h"
 #include "mozilla/Unused.h"
 #include "mozilla/UseCounter.h"
 
 #include "AccessCheck.h"
 #include "jsfriendapi.h"
+#include "nsContentCreatorFunctions.h"
 #include "nsContentUtils.h"
 #include "nsGlobalWindow.h"
 #include "nsIDocShell.h"
 #include "nsIDOMGlobalPropertyInitializer.h"
+#include "nsIParserService.h"
 #include "nsIPermissionManager.h"
 #include "nsIPrincipal.h"
 #include "nsIXPConnect.h"
 #include "nsUTF8Utils.h"
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
 #include "WrapperFactory.h"
 #include "xpcprivate.h"
 #include "XrayWrapper.h"
 #include "nsPrintfCString.h"
 #include "mozilla/Sprintf.h"
 #include "nsGlobalWindow.h"
 
 #include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/dom/CustomElementRegistry.h"
 #include "mozilla/dom/DOMError.h"
 #include "mozilla/dom/DOMErrorBinding.h"
 #include "mozilla/dom/DOMException.h"
 #include "mozilla/dom/ElementBinding.h"
 #include "mozilla/dom/HTMLObjectElement.h"
 #include "mozilla/dom/HTMLObjectElementBinding.h"
 #include "mozilla/dom/HTMLSharedObjectElement.h"
+#include "mozilla/dom/HTMLElementBinding.h"
 #include "mozilla/dom/HTMLEmbedElementBinding.h"
 #include "mozilla/dom/HTMLAppletElementBinding.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ResolveSystemBinding.h"
 #include "mozilla/dom/WebIDLGlobalNameHash.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/dom/WorkerScope.h"
 #include "mozilla/dom/XrayExpandoClass.h"
@@ -57,16 +61,40 @@
 #include "ipc/ErrorIPCUtils.h"
 #include "mozilla/UseCounter.h"
 
 namespace mozilla {
 namespace dom {
 
 using namespace workers;
 
+// Forward declare GetConstructorObject methods.
+#define HTML_TAG(_tag, _classname, _interfacename)                             \
+namespace HTML##_interfacename##ElementBinding {                               \
+  JSObject* GetConstructorObject(JSContext*);                                  \
+}
+#define HTML_OTHER(_tag)
+#include "nsHTMLTagList.h"
+#undef HTML_TAG
+#undef HTML_OTHER
+
+typedef JSObject* (*constructorGetterCallback)(JSContext*);
+
+// Mapping of html tag and GetConstructorObject methods.
+#define HTML_TAG(_tag, _classname, _interfacename) HTML##_interfacename##ElementBinding::GetConstructorObject,
+#define HTML_OTHER(_tag) nullptr,
+// We use eHTMLTag_foo (where foo is the tag) which is defined in nsHTMLTags.h
+// to index into this array.
+static const constructorGetterCallback sConstructorGetterCallback[] = {
+  HTMLUnknownElementBinding::GetConstructorObject,
+#include "nsHTMLTagList.h"
+#undef HTML_TAG
+#undef HTML_OTHER
+};
+
 const JSErrorFormatString ErrorFormatString[] = {
 #define MSG_DEF(_name, _argc, _exn, _str) \
   { #_name, _str, _argc, _exn },
 #include "mozilla/dom/Errors.msg"
 #undef MSG_DEF
 };
 
 #define MSG_DEF(_name, _argc, _exn, _str) \
@@ -3323,16 +3351,141 @@ GetDesiredProto(JSContext* aCx, const JS
     aDesiredProto.set(nullptr);
     return true;
   }
 
   aDesiredProto.set(&protoVal.toObject());
   return true;
 }
 
+// https://html.spec.whatwg.org/multipage/dom.html#htmlconstructor
+already_AddRefed<nsGenericHTMLElement>
+CreateHTMLElement(const GlobalObject& aGlobal, const JS::CallArgs& aCallArgs,
+                  ErrorResult& aRv)
+{
+  // Step 1.
+  nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal.GetAsSupports());
+  if (!window) {
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return nullptr;
+  }
+
+  nsIDocument* doc = window->GetExtantDoc();
+  if (!doc) {
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return nullptr;
+  }
+
+  RefPtr<mozilla::dom::CustomElementRegistry> registry(window->CustomElements());
+  if (!registry) {
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return nullptr;
+  }
+
+  // Step 2 is in the code output by CGClassConstructor.
+  // Step 3.
+  JSContext* cx = aGlobal.Context();
+  JS::Rooted<JSObject*> newTarget(cx, &aCallArgs.newTarget().toObject());
+  CustomElementDefinition* definition =
+    registry->LookupCustomElementDefinition(cx, newTarget);
+  if (!definition) {
+    aRv.ThrowTypeError<MSG_ILLEGAL_CONSTRUCTOR>();
+    return nullptr;
+  }
+
+  // The callee might be an Xray. Unwrap it to get actual callee.
+  JS::Rooted<JSObject*> callee(cx, js::CheckedUnwrap(&aCallArgs.callee()));
+  if (!callee) {
+    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
+    return nullptr;
+  }
+
+  // And the actual callee might be in different compartment, so enter its
+  // compartment before getting the standard constructor object to compare to,
+  // so we get it from the same global as callee itself.
+  JSAutoCompartment ac(cx, callee);
+  int32_t tag = eHTMLTag_userdefined;
+  if (!definition->IsCustomBuiltIn()) {
+    // Step 4.
+    // If the definition is for an autonomous custom element, the active
+    // function should be HTMLElement.
+    JS::Rooted<JSObject*> constructor(cx, HTMLElementBinding::GetConstructorObject(cx));
+    if (!constructor) {
+      aRv.NoteJSContextException(cx);
+      return nullptr;
+    }
+
+    if (callee != constructor) {
+      aRv.ThrowTypeError<MSG_ILLEGAL_CONSTRUCTOR>();
+      return nullptr;
+    }
+  } else {
+    // Step 5.
+    // If the definition is for a customized built-in element, the localName
+    // should be defined in the specification.
+    nsIParserService* parserService = nsContentUtils::GetParserService();
+    if (!parserService) {
+      aRv.Throw(NS_ERROR_UNEXPECTED);
+      return nullptr;
+    }
+
+    tag = parserService->HTMLCaseSensitiveAtomTagToId(definition->mLocalName);
+    if (tag == eHTMLTag_userdefined) {
+      aRv.ThrowTypeError<MSG_ILLEGAL_CONSTRUCTOR>();
+      return nullptr;
+    }
+
+    MOZ_ASSERT(tag <= NS_HTML_TAG_MAX, "tag is out of bounds");
+
+    // If the definition is for a customized built-in element, the active
+    // function should be the localname's element interface.
+    constructorGetterCallback cb = sConstructorGetterCallback[tag];
+    if (!cb) {
+      aRv.ThrowTypeError<MSG_ILLEGAL_CONSTRUCTOR>();
+      return nullptr;
+    }
+
+    JS::Rooted<JSObject*> constructor(cx, cb(cx));
+    if (!constructor) {
+      aRv.NoteJSContextException(cx);
+      return nullptr;
+    }
+
+    if (callee != constructor) {
+      aRv.ThrowTypeError<MSG_ILLEGAL_CONSTRUCTOR>();
+      return nullptr;
+    }
+  }
+
+  RefPtr<mozilla::dom::NodeInfo> nodeInfo =
+    doc->NodeInfoManager()->GetNodeInfo(definition->mLocalName,
+                                        nullptr,
+                                        kNameSpaceID_XHTML,
+                                        nsIDOMNode::ELEMENT_NODE);
+  if (!nodeInfo) {
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return nullptr;
+  }
+
+  // Step 6 and Step 7 are in the code output by CGClassConstructor.
+  // Step 8.
+  // Construction stack will be implemented in bug 1287348. So we always run
+  // "construction stack is empty" case for now.
+  RefPtr<nsGenericHTMLElement> element;
+  if (tag == eHTMLTag_userdefined) {
+    // Autonomous custom element.
+    element = NS_NewHTMLElement(nodeInfo.forget());
+  } else {
+    // Customized built-in element.
+    element = CreateHTMLElement(tag, nodeInfo.forget(), NOT_FROM_PARSER);
+  }
+
+  return element.forget();
+}
+
 #ifdef DEBUG
 namespace binding_detail {
 void
 AssertReflectorHasGivenProto(JSContext* aCx, JSObject* aReflector,
                              JS::Handle<JSObject*> aGivenProto)
 {
   if (!aGivenProto) {
     // Nothing to assert here
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -37,16 +37,17 @@
 #include "nsISupportsImpl.h"
 #include "qsObjectHelper.h"
 #include "xpcpublic.h"
 #include "nsIVariant.h"
 #include "mozilla/dom/FakeString.h"
 
 #include "nsWrapperCacheInlines.h"
 
+class nsGenericHTMLElement;
 class nsIJSID;
 
 namespace mozilla {
 
 enum UseCounter : int16_t;
 
 namespace dom {
 template<typename DataType> class MozMap;
@@ -3175,16 +3176,23 @@ bool GetSetlikeBackingObject(JSContext* 
                              bool* aBackingObjCreated);
 
 // Get the desired prototype object for an object construction from the given
 // CallArgs.  Null is returned if the default prototype should be used.
 bool
 GetDesiredProto(JSContext* aCx, const JS::CallArgs& aCallArgs,
                 JS::MutableHandle<JSObject*> aDesiredProto);
 
+// This function is expected to be called from the constructor function for an
+// HTML element interface; the global/callargs need to be whatever was passed to
+// that constructor function.
+already_AddRefed<nsGenericHTMLElement>
+CreateHTMLElement(const GlobalObject& aGlobal, const JS::CallArgs& aCallArgs,
+                  ErrorResult& aRv);
+
 void
 SetDocumentAndPageUseCounter(JSContext* aCx, JSObject* aObject,
                              UseCounter aUseCounter);
 
 // Warnings
 void
 DeprecationWarning(JSContext* aCx, JSObject* aObject,
                    nsIDocument::DeprecatedOperations aOperation);
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -1633,16 +1633,21 @@ DOMInterfaces = {
         'register': False,
         },
 
 'TestWorkerExposedInterface' : {
         'headerFile': 'TestBindingHeader.h',
         'register': False,
         },
 
+'TestHTMLConstructorInterface' : {
+        'headerFile': 'TestBindingHeader.h',
+        'register': False,
+        },
+
 }
 
 # These are temporary, until they've been converted to use new DOM bindings
 def addExternalIface(iface, nativeType=None, headerFile=None,
                      notflattened=False):
     if iface in DOMInterfaces:
         raise Exception('Interface declared both as WebIDL and External interface')
     domInterface = {
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -1712,39 +1712,126 @@ class CGClassConstructor(CGAbstractStati
         # the name JS sees is the interface name; for named constructors
         # identifier.name is the actual name.
         name = self._ctor.identifier.name
         if name != "constructor":
             ctorName = name
         else:
             ctorName = self.descriptor.interface.identifier.name
 
+        # [HTMLConstructor] for custom element
+        # This needs to live in bindings code because it directly examines
+        # newtarget and the callee function to do HTMLConstructor specific things.
+        if self._ctor.isHTMLConstructor():
+            htmlConstructorSanityCheck = dedent("""
+                // The newTarget might be a cross-compartment wrapper. Get the underlying object
+                // so we can do the spec's object-identity checks.
+                JS::Rooted<JSObject*> newTarget(cx, js::CheckedUnwrap(&args.newTarget().toObject()));
+                if (!newTarget) {
+                  return ThrowErrorMessage(cx, MSG_ILLEGAL_CONSTRUCTOR);
+                }
+
+                // Step 2 of https://html.spec.whatwg.org/multipage/dom.html#htmlconstructor.
+                // Enter the compartment of our underlying newTarget object, so we end
+                // up comparing to the constructor object for our interface from that global.
+                {
+                  JSAutoCompartment ac(cx, newTarget);
+                  JS::Handle<JSObject*> constructor(GetConstructorObjectHandle(cx));
+                  if (!constructor) {
+                    return false;
+                  }
+                  if (newTarget == constructor) {
+                    return ThrowErrorMessage(cx, MSG_ILLEGAL_CONSTRUCTOR);
+                  }
+                }
+
+                """)
+
+            # If we are unable to get desired prototype from newTarget, then we
+            # fall back to the interface prototype object from newTarget's realm.
+            htmlConstructorFallback = dedent("""
+                if (!desiredProto) {
+                  // Step 7 of https://html.spec.whatwg.org/multipage/dom.html#htmlconstructor.
+                  // This fallback behavior is designed to match analogous behavior for the
+                  // JavaScript built-ins. So we enter the compartment of our underlying
+                  // newTarget object and fall back to the prototype object from that global.
+                  // XXX The spec says to use GetFunctionRealm(), which is not actually
+                  // the same thing as what we have here (e.g. in the case of scripted callable proxies
+                  // whose target is not same-compartment with the proxy, or bound functions, etc).
+                  // https://bugzilla.mozilla.org/show_bug.cgi?id=1317658
+                  {
+                    JSAutoCompartment ac(cx, newTarget);
+                    desiredProto = GetProtoObjectHandle(cx);
+                    if (!desiredProto) {
+                        return false;
+                    }
+                  }
+
+                  // desiredProto is in the compartment of the underlying newTarget object.
+                  // Wrap it into the context compartment.
+                  if (!JS_WrapObject(cx, &desiredProto)) {
+                    return false;
+                  }
+                }
+                """)
+        else:
+            htmlConstructorSanityCheck = ""
+            htmlConstructorFallback = ""
+
+
+        # If we're a constructor, "obj" may not be a function, so calling
+        # XrayAwareCalleeGlobal() on it is not safe.  Of course in the
+        # constructor case either "obj" is an Xray or we're already in the
+        # content compartment, not the Xray compartment, so just
+        # constructing the GlobalObject from "obj" is fine.
         preamble = fill(
             """
             JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
             JS::Rooted<JSObject*> obj(cx, &args.callee());
             $*{chromeOnlyCheck}
             if (!args.isConstructing()) {
               // XXXbz wish I could get the name from the callee instead of
               // Adding more relocations
               return ThrowConstructorWithoutNew(cx, "${ctorName}");
             }
+
+            GlobalObject global(cx, obj);
+            if (global.Failed()) {
+              return false;
+            }
+
+            $*{htmlConstructorSanityCheck}
             JS::Rooted<JSObject*> desiredProto(cx);
             if (!GetDesiredProto(cx, args, &desiredProto)) {
               return false;
             }
+            $*{htmlConstructorFallback}
             """,
             chromeOnlyCheck=chromeOnlyCheck,
-            ctorName=ctorName)
-
-        name = self._ctor.identifier.name
-        nativeName = MakeNativeName(self.descriptor.binaryNameFor(name))
-        callGenerator = CGMethodCall(nativeName, True, self.descriptor,
-                                     self._ctor, isConstructor=True,
-                                     constructorName=ctorName)
+            ctorName=ctorName,
+            htmlConstructorSanityCheck=htmlConstructorSanityCheck,
+            htmlConstructorFallback=htmlConstructorFallback)
+
+        if  self._ctor.isHTMLConstructor():
+            signatures = self._ctor.signatures()
+            assert len(signatures) == 1
+            # Given that HTMLConstructor takes no args, we can just codegen a
+            # call to CreateHTMLElement() in BindingUtils which reuses the
+            # factory thing in HTMLContentSink. Then we don't have to implement
+            # Constructor on all the HTML elements.
+            callGenerator = CGPerSignatureCall(signatures[0][0], signatures[0][1],
+                                               "CreateHTMLElement", True,
+                                               self.descriptor, self._ctor,
+                                               isConstructor=True)
+        else:
+            name = self._ctor.identifier.name
+            nativeName = MakeNativeName(self.descriptor.binaryNameFor(name))
+            callGenerator = CGMethodCall(nativeName, True, self.descriptor,
+                                         self._ctor, isConstructor=True,
+                                         constructorName=ctorName)
         return preamble + "\n" + callGenerator.define()
 
 
 # Encapsulate the constructor in a helper method to share genConstructorBody with CGJSImplMethod.
 class CGConstructNavigatorObject(CGAbstractMethod):
     """
     Construct a new JS-implemented WebIDL DOM object, for use on navigator.
     """
@@ -7267,36 +7354,33 @@ class CGPerSignatureCall(CGThing):
             cgThings.append(CGGeneric(dedent(
                 """
                 bool foundNonFiniteFloat = false;
                 """)))
             lenientFloatCode = "foundNonFiniteFloat = true;\n"
 
         argsPre = []
         if idlNode.isStatic():
-            # If we're a constructor, "obj" may not be a function, so calling
-            # XrayAwareCalleeGlobal() on it is not safe.  Of course in the
-            # constructor case either "obj" is an Xray or we're already in the
-            # content compartment, not the Xray compartment, so just
-            # constructing the GlobalObject from "obj" is fine.
-            if isConstructor:
-                objForGlobalObject = "obj"
-            else:
-                objForGlobalObject = "xpc::XrayAwareCalleeGlobal(obj)"
-            cgThings.append(CGGeneric(fill(
-                """
-                GlobalObject global(cx, ${obj});
-                if (global.Failed()) {
-                  return false;
-                }
-
-                """,
-                obj=objForGlobalObject)))
+            # If we're a constructor, the GlobalObject struct will be created in
+            # CGClassConstructor.
+            if not isConstructor:
+                cgThings.append(CGGeneric(dedent(
+                    """
+                    GlobalObject global(cx, xpc::XrayAwareCalleeGlobal(obj));
+                    if (global.Failed()) {
+                      return false;
+                    }
+
+                    """)))
+
             argsPre.append("global")
 
+        if isConstructor and idlNode.isHTMLConstructor():
+            argsPre.append("args")
+
         # For JS-implemented interfaces we do not want to base the
         # needsCx decision on the types involved, just on our extended
         # attributes. Also, JSContext is not needed for the static case
         # since GlobalObject already contains the context.
         needsCx = needCx(returnType, arguments, self.extendedAttributes,
                          not descriptor.interface.isJSImplemented(), static)
         if needsCx:
             argsPre.append("cx")
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -1577,61 +1577,71 @@ class IDLInterface(IDLInterfaceOrNamespa
                     raise WebIDLError("[NoInterfaceObject] must take no arguments",
                                       [attr.location])
 
                 if self.ctor():
                     raise WebIDLError("Constructor and NoInterfaceObject are incompatible",
                                       [self.location])
 
                 self._noInterfaceObject = True
-            elif identifier == "Constructor" or identifier == "NamedConstructor" or identifier == "ChromeConstructor":
+            elif identifier == "Constructor" or identifier == "NamedConstructor" or identifier == "ChromeConstructor" or identifier == "HTMLConstructor":
                 if identifier == "Constructor" and not self.hasInterfaceObject():
                     raise WebIDLError(str(identifier) + " and NoInterfaceObject are incompatible",
                                       [self.location])
 
                 if identifier == "NamedConstructor" and not attr.hasValue():
                     raise WebIDLError("NamedConstructor must either take an identifier or take a named argument list",
                                       [attr.location])
 
                 if identifier == "ChromeConstructor" and not self.hasInterfaceObject():
                     raise WebIDLError(str(identifier) + " and NoInterfaceObject are incompatible",
                                       [self.location])
 
+                if identifier == "HTMLConstructor":
+                    if not self.hasInterfaceObject():
+                        raise WebIDLError(str(identifier) + " and NoInterfaceObject are incompatible",
+                                          [self.location])
+
+                    if not attr.noArguments():
+                        raise WebIDLError(str(identifier) + " must take no arguments",
+                                          [attr.location])
+
                 args = attr.args() if attr.hasArgs() else []
 
                 if self.identifier.name == "Promise":
                     promiseType = BuiltinTypes[IDLBuiltinType.Types.any]
                 else:
                     promiseType = None
                 retType = IDLWrapperType(self.location, self, promiseType)
 
-                if identifier == "Constructor" or identifier == "ChromeConstructor":
+                if identifier == "Constructor" or identifier == "ChromeConstructor" or identifier == "HTMLConstructor":
                     name = "constructor"
                     allowForbidden = True
                 else:
                     name = attr.value()
                     allowForbidden = False
 
                 methodIdentifier = IDLUnresolvedIdentifier(self.location, name,
                                                            allowForbidden=allowForbidden)
 
                 method = IDLMethod(self.location, methodIdentifier, retType,
-                                   args, static=True)
+                                   args, static=True,
+                                   htmlConstructor=(identifier == "HTMLConstructor"))
                 # Constructors are always NewObject and are always
                 # assumed to be able to throw (since there's no way to
                 # indicate otherwise) and never have any other
                 # extended attributes.
                 method.addExtendedAttributes(
                     [IDLExtendedAttribute(self.location, ("NewObject",)),
                      IDLExtendedAttribute(self.location, ("Throws",))])
                 if identifier == "ChromeConstructor":
                     method.addExtendedAttributes(
                         [IDLExtendedAttribute(self.location, ("ChromeOnly",))])
 
-                if identifier == "Constructor" or identifier == "ChromeConstructor":
+                if identifier == "Constructor" or identifier == "ChromeConstructor" or identifier == "HTMLConstructor":
                     method.resolve(self)
                 else:
                     # We need to detect conflicts for NamedConstructors across
                     # interfaces. We first call resolve on the parentScope,
                     # which will merge all NamedConstructors with the same
                     # identifier accross interfaces as overloads.
                     method.resolve(self.parentScope)
 
@@ -4503,17 +4513,17 @@ class IDLMethod(IDLInterfaceMember, IDLS
         'Named',
         'Indexed'
     )
 
     def __init__(self, location, identifier, returnType, arguments,
                  static=False, getter=False, setter=False, creator=False,
                  deleter=False, specialType=NamedOrIndexed.Neither,
                  legacycaller=False, stringifier=False, jsonifier=False,
-                 maplikeOrSetlikeOrIterable=None):
+                 maplikeOrSetlikeOrIterable=None, htmlConstructor=False):
         # REVIEW: specialType is NamedOrIndexed -- wow, this is messed up.
         IDLInterfaceMember.__init__(self, location, identifier,
                                     IDLInterfaceMember.Tags.Method)
 
         self._hasOverloads = False
 
         assert isinstance(returnType, IDLType)
 
@@ -4533,16 +4543,20 @@ class IDLMethod(IDLInterfaceMember, IDLS
         assert isinstance(legacycaller, bool)
         self._legacycaller = legacycaller
         assert isinstance(stringifier, bool)
         self._stringifier = stringifier
         assert isinstance(jsonifier, bool)
         self._jsonifier = jsonifier
         assert maplikeOrSetlikeOrIterable is None or isinstance(maplikeOrSetlikeOrIterable, IDLMaplikeOrSetlikeOrIterableBase)
         self.maplikeOrSetlikeOrIterable = maplikeOrSetlikeOrIterable
+        assert isinstance(htmlConstructor, bool)
+        # The identifier of a HTMLConstructor must be 'constructor'.
+        assert not htmlConstructor or identifier.name == "constructor"
+        self._htmlConstructor = htmlConstructor
         self._specialType = specialType
         self._unforgeable = False
         self.dependsOn = "Everything"
         self.affects = "Everything"
         self.aliases = []
 
         if static and identifier.name == "prototype":
             raise WebIDLError("The identifier of a static operation must not be 'prototype'",
@@ -4633,16 +4647,19 @@ class IDLMethod(IDLInterfaceMember, IDLS
         return (self.isGetter() or
                 self.isSetter() or
                 self.isCreator() or
                 self.isDeleter() or
                 self.isLegacycaller() or
                 self.isStringifier() or
                 self.isJsonifier())
 
+    def isHTMLConstructor(self):
+        return self._htmlConstructor
+
     def hasOverloads(self):
         return self._hasOverloads
 
     def isIdentifierLess(self):
         """
         True if the method name started with __, and if the method is not a
         maplike/setlike method. Interfaces with maplike/setlike will generate
         methods starting with __ for chrome only backing object access in JS
@@ -4688,16 +4705,18 @@ class IDLMethod(IDLInterfaceMember, IDLS
         assert not self.isCreator()
         assert not method.isCreator()
         assert not self.isDeleter()
         assert not method.isDeleter()
         assert not self.isStringifier()
         assert not method.isStringifier()
         assert not self.isJsonifier()
         assert not method.isJsonifier()
+        assert not self.isHTMLConstructor()
+        assert not method.isHTMLConstructor()
 
         return self
 
     def signatures(self):
         return [(overload.returnType, overload.arguments) for overload in
                 self._overloads]
 
     def finish(self, scope):
--- a/dom/bindings/parser/tests/test_constructor.py
+++ b/dom/bindings/parser/tests/test_constructor.py
@@ -8,32 +8,33 @@ def WebIDLTest(parser, harness):
         harness.check(argument.identifier.name, name, "Argument has the right name")
         harness.check(str(argument.type), type, "Argument has the right return type")
         harness.check(argument.optional, optional, "Argument has the right optional value")
         harness.check(argument.variadic, variadic, "Argument has the right variadic value")
 
     def checkMethod(method, QName, name, signatures,
                     static=True, getter=False, setter=False, creator=False,
                     deleter=False, legacycaller=False, stringifier=False,
-                    chromeOnly=False):
+                    chromeOnly=False, htmlConstructor=False):
         harness.ok(isinstance(method, WebIDL.IDLMethod),
                    "Should be an IDLMethod")
         harness.ok(method.isMethod(), "Method is a method")
         harness.ok(not method.isAttr(), "Method is not an attr")
         harness.ok(not method.isConst(), "Method is not a const")
         harness.check(method.identifier.QName(), QName, "Method has the right QName")
         harness.check(method.identifier.name, name, "Method has the right name")
         harness.check(method.isStatic(), static, "Method has the correct static value")
         harness.check(method.isGetter(), getter, "Method has the correct getter value")
         harness.check(method.isSetter(), setter, "Method has the correct setter value")
         harness.check(method.isCreator(), creator, "Method has the correct creator value")
         harness.check(method.isDeleter(), deleter, "Method has the correct deleter value")
         harness.check(method.isLegacycaller(), legacycaller, "Method has the correct legacycaller value")
         harness.check(method.isStringifier(), stringifier, "Method has the correct stringifier value")
         harness.check(method.getExtendedAttribute("ChromeOnly") is not None, chromeOnly, "Method has the correct value for ChromeOnly")
+        harness.check(method.isHTMLConstructor(), htmlConstructor, "Method has the correct htmlConstructor value")
         harness.check(len(method.signatures()), len(signatures), "Method has the correct number of signatures")
 
         sigpairs = zip(method.signatures(), signatures)
         for (gotSignature, expectedSignature) in sigpairs:
             (gotRetType, gotArgs) = gotSignature
             (expectedRetType, expectedArgs) = expectedSignature
 
             harness.check(str(gotRetType), expectedRetType,
@@ -89,21 +90,184 @@ def WebIDLTest(parser, harness):
     harness.ok(isinstance(results[0], WebIDL.IDLInterface),
                "Should be an IDLInterface")
 
     checkMethod(results[0].ctor(), "::TestChromeConstructor::constructor",
                 "constructor", [("TestChromeConstructor (Wrapper)", [])],
                 chromeOnly=True)
 
     parser = parser.reset()
+    parser.parse("""
+        [HTMLConstructor]
+        interface TestHTMLConstructor {
+        };
+    """)
+    results = parser.finish()
+    harness.check(len(results), 1, "Should be one production")
+    harness.ok(isinstance(results[0], WebIDL.IDLInterface),
+               "Should be an IDLInterface")
+
+    checkMethod(results[0].ctor(), "::TestHTMLConstructor::constructor",
+                "constructor", [("TestHTMLConstructor (Wrapper)", [])],
+                htmlConstructor=True)
+
+    parser = parser.reset()
     threw = False
     try:
         parser.parse("""
         [Constructor(),
          ChromeConstructor(DOMString a)]
         interface TestChromeConstructor {
         };
         """)
         results = parser.finish()
     except:
         threw = True
 
     harness.ok(threw, "Can't have both a Constructor and a ChromeConstructor")
+
+    # Test HTMLConstructor with argument
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            [HTMLConstructor(DOMString a)]
+            interface TestHTMLConstructorWithArgs {
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw, "HTMLConstructor should take no argument")
+
+    # Test HTMLConstructor on a callback interface
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            [HTMLConstructor]
+            callback interface TestHTMLConstructorOnCallbackInterface {
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw, "HTMLConstructor can't be used on a callback interface")
+
+    # Test HTMLConstructor and Constructor
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            [Constructor,
+             HTMLConstructor]
+            interface TestHTMLConstructorAndConstructor {
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw, "Can't have both a Constructor and a HTMLConstructor")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            [HTMLConstructor,
+             Constructor]
+            interface TestHTMLConstructorAndConstructor {
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw, "Can't have both a HTMLConstructor and a Constructor")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            [HTMLConstructor,
+             Constructor(DOMString a)]
+            interface TestHTMLConstructorAndConstructor {
+            };
+        """)
+    except:
+        threw = True
+
+    harness.ok(threw, "Can't have both a HTMLConstructor and a Constructor")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            [Constructor(DOMString a),
+             HTMLConstructor]
+            interface TestHTMLConstructorAndConstructor {
+            };
+        """)
+    except:
+        threw = True
+
+    harness.ok(threw, "Can't have both a HTMLConstructor and a Constructor")
+
+    # Test HTMLConstructor and ChromeConstructor
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            [ChromeConstructor,
+             HTMLConstructor]
+            interface TestHTMLConstructorAndChromeConstructor {
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw, "Can't have both a HTMLConstructor and a ChromeConstructor")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            [HTMLConstructor,
+             ChromeConstructor]
+            interface TestHTMLConstructorAndChromeConstructor {
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw, "Can't have both a HTMLConstructor and a ChromeConstructor")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            [ChromeConstructor(DOMString a),
+             HTMLConstructor]
+            interface TestHTMLConstructorAndChromeConstructor {
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            [HTMLConstructor,
+             ChromeConstructor(DOMString a)]
+            interface TestHTMLConstructorAndChromeConstructor {
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw, "Can't have both a HTMLConstructor and a ChromeConstructor")
--- a/dom/bindings/parser/tests/test_constructor_no_interface_object.py
+++ b/dom/bindings/parser/tests/test_constructor_no_interface_object.py
@@ -29,8 +29,41 @@ def WebIDLTest(parser, harness):
 
     parser = parser.reset()
 
     parser.parse("""
         [NoInterfaceObject, NamedConstructor=FooBar]
         interface TestNamedConstructorNoInterfaceObject {
         };
     """)
+
+    # Test HTMLConstructor and NoInterfaceObject
+    parser = parser.reset()
+
+    threw = False
+    try:
+        parser.parse("""
+            [NoInterfaceObject, HTMLConstructor]
+            interface TestHTMLConstructorNoInterfaceObject {
+            };
+        """)
+
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw, "Should have thrown.")
+
+    parser = parser.reset()
+
+    threw = False
+    try:
+        parser.parse("""
+            [HTMLConstructor, NoInterfaceObject]
+            interface TestHTMLConstructorNoInterfaceObject {
+            };
+        """)
+
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw, "Should have thrown.")
--- a/dom/bindings/test/TestBindingHeader.h
+++ b/dom/bindings/test/TestBindingHeader.h
@@ -1420,12 +1420,18 @@ public:
   void NeedsSubjectPrincipalMethod(Maybe<nsIPrincipal*>);
   bool NeedsSubjectPrincipalAttr(Maybe<nsIPrincipal*>);
   void SetNeedsSubjectPrincipalAttr(bool, Maybe<nsIPrincipal*>);
   void NeedsCallerTypeMethod(CallerType);
   bool NeedsCallerTypeAttr(CallerType);
   void SetNeedsCallerTypeAttr(bool, CallerType);
 };
 
+class TestHTMLConstructorInterface : public nsGenericHTMLElement
+{
+public:
+  virtual nsISupports* GetParentObject();
+};
+
 } // namespace dom
 } // namespace mozilla
 
 #endif /* TestBindingHeader_h */
--- a/dom/bindings/test/TestCodeGen.webidl
+++ b/dom/bindings/test/TestCodeGen.webidl
@@ -1257,8 +1257,12 @@ interface TestSecureContextInterface {
 
 [Exposed=(Window,Worker)]
 interface TestWorkerExposedInterface {
   [NeedsSubjectPrincipal] void needsSubjectPrincipalMethod();
   [NeedsSubjectPrincipal] attribute boolean needsSubjectPrincipalAttr;
   [NeedsCallerType] void needsCallerTypeMethod();
   [NeedsCallerType] attribute boolean needsCallerTypeAttr;
 };
+
+[HTMLConstructor]
+interface TestHTMLConstructorInterface {
+};
--- a/dom/events/EventDispatcher.cpp
+++ b/dom/events/EventDispatcher.cpp
@@ -1158,17 +1158,20 @@ EventDispatcher::CreateEvent(EventTarget
     return event.forget();
   }
   if (aEventType.LowerCaseEqualsLiteral("customevent")) {
     LOG_EVENT_CREATION(CUSTOMEVENT);
     return NS_NewDOMCustomEvent(aOwner, aPresContext, nullptr);
   }
   if (aEventType.LowerCaseEqualsLiteral("storageevent")) {
     LOG_EVENT_CREATION(STORAGEEVENT);
-    return NS_NewDOMStorageEvent(aOwner);
+    RefPtr<Event> event =
+      StorageEvent::Constructor(aOwner, EmptyString(), StorageEventInit());
+    event->MarkUninitialized();
+    return event.forget();
   }
 
 #undef LOG_EVENT_CREATION
 
   // NEW EVENT TYPES SHOULD NOT BE ADDED HERE; THEY SHOULD USE ONLY EVENT
   // CONSTRUCTORS
 
   return nullptr;
--- a/dom/events/StorageEvent.cpp
+++ b/dom/events/StorageEvent.cpp
@@ -94,21 +94,8 @@ StorageEvent::InitStorageEvent(const nsA
   mOldValue = aOldValue;
   mNewValue = aNewValue;
   mUrl = aURL;
   mStorageArea = aStorageArea;
 }
 
 } // namespace dom
 } // namespace mozilla
-
-using namespace mozilla;
-using namespace mozilla::dom;
-
-already_AddRefed<StorageEvent>
-NS_NewDOMStorageEvent(EventTarget* aOwner)
-{
-  RefPtr<StorageEvent> e = new StorageEvent(aOwner);
-
-  e->SetTrusted(e->Init(aOwner));
-  return e.forget();
-}
-
--- a/dom/events/StorageEvent.h
+++ b/dom/events/StorageEvent.h
@@ -8,20 +8,16 @@
 #define mozilla_dom_StorageEvent_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/Event.h"
 #include "mozilla/dom/StorageEventBinding.h"
 
-// Helper for EventDispatcher.
-already_AddRefed<mozilla::dom::StorageEvent>
-NS_NewDOMStorageEvent(mozilla::dom::EventTarget* aOwner);
-
 namespace mozilla {
 namespace dom {
 
 class DOMStorage;
 
 class StorageEvent : public Event
 {
 public:
--- a/dom/events/test/window_bug617528.xul
+++ b/dom/events/test/window_bug617528.xul
@@ -1,9 +1,9 @@
 <?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
 
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         width="640" height="480">
 
-  <browser id="browser" type="content-primary" flex="1" src="about:blank"
+  <browser id="browser" type="content" primary="true" flex="1" src="about:blank"
            disablehistory="true" disablesecurity="true"/>
 
 </window>
--- a/dom/html/nsHTMLContentSink.cpp
+++ b/dom/html/nsHTMLContentSink.cpp
@@ -91,24 +91,22 @@ typedef nsGenericHTMLElement*
 nsGenericHTMLElement*
 NS_NewHTMLNOTUSEDElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
                          FromParser aFromParser)
 {
   NS_NOTREACHED("The element ctor should never be called");
   return nullptr;
 }
 
-#define HTML_TAG(_tag, _classname) NS_NewHTML##_classname##Element,
-#define HTML_HTMLELEMENT_TAG(_tag) NS_NewHTMLElement,
+#define HTML_TAG(_tag, _classname, _interfacename) NS_NewHTML##_classname##Element,
 #define HTML_OTHER(_tag) NS_NewHTMLNOTUSEDElement,
 static const contentCreatorCallback sContentCreatorCallbacks[] = {
   NS_NewHTMLUnknownElement,
 #include "nsHTMLTagList.h"
 #undef HTML_TAG
-#undef HTML_HTMLELEMENT_TAG
 #undef HTML_OTHER
   NS_NewHTMLUnknownElement
 };
 
 class SinkContext;
 class HTMLContentSink;
 
 /**
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -596,22 +596,24 @@ parent:
     // After a compositor reset, it is necessary to reconnect each layers ID to
     // the compositor of the widget that will render those layers. Note that
     // this is sync so we can ensure that messages to the window compositor
     // arrive before the TabChild attempts to use its cross-process compositor
     // bridge.
     sync EnsureLayersConnected();
 
     /**
-     * Notify parent that one or more entries have been added / removed from
-     * the child session history.
+     * Notify the parent that the session history state has been updated.
      *
-     * @param aCount the updated number of entries in child session history
+     * @param aCount
+     *        The updated number of entries in child session history
+     * @param aLocalIndex
+     *        The local session history index which is loaded.
      */
-    async NotifySessionHistoryChange(uint32_t aCount);
+    async SHistoryUpdate(uint32_t aCount, uint32_t aLocalIndex, bool aTruncate);
 
     /**
      * When the session history is across multiple root docshells, this function
      * is used to notify parent that it needs to navigate to an entry out of
      * local index of the child.
      *
      * @param aGlobalIndex The global index of history entry to navigate to.
      */
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -656,18 +656,17 @@ TabChild::Init()
         }
       });
   mAPZEventState = new APZEventState(mPuppetWidget, Move(callback));
 
   mIPCOpen = true;
 
   if (GroupedSHistory::GroupedHistoryEnabled()) {
     // Set session history listener.
-    nsCOMPtr<nsISHistory> shistory;
-    mWebNav->GetSessionHistory(getter_AddRefs(shistory));
+    nsCOMPtr<nsISHistory> shistory = GetRelatedSHistory();
     if (!shistory) {
       return NS_ERROR_FAILURE;
     }
     mHistoryListener = new TabChildSHistoryListener(this);
     nsCOMPtr<nsISHistoryListener> listener(do_QueryObject(mHistoryListener));
     shistory->AddSHistoryListener(listener);
     nsCOMPtr<nsIPartialSHistoryListener> partialListener(do_QueryObject(mHistoryListener));
     shistory->SetPartialSHistoryListener(partialListener);
@@ -1504,18 +1503,17 @@ TabChild::RecvMenuKeyboardListenerInstal
 mozilla::ipc::IPCResult
 TabChild::RecvNotifyAttachGroupedSessionHistory(const uint32_t& aOffset)
 {
   // nsISHistory uses int32_t
   if (NS_WARN_IF(aOffset > INT32_MAX)) {
     return IPC_FAIL_NO_REASON(this);
   }
 
-  nsCOMPtr<nsISHistory> shistory;
-  mWebNav->GetSessionHistory(getter_AddRefs(shistory));
+  nsCOMPtr<nsISHistory> shistory = GetRelatedSHistory();
   NS_ENSURE_TRUE(shistory, IPC_FAIL_NO_REASON(this));
 
   if (NS_FAILED(shistory->OnAttachGroupedSessionHistory(aOffset))) {
     return IPC_FAIL_NO_REASON(this);
   }
   return IPC_OK();
 }
 
@@ -1523,32 +1521,30 @@ mozilla::ipc::IPCResult
 TabChild::RecvNotifyPartialSessionHistoryActive(const uint32_t& aGlobalLength,
                                                 const uint32_t& aTargetLocalIndex)
 {
   // nsISHistory uses int32_t
   if (NS_WARN_IF(aGlobalLength > INT32_MAX || aTargetLocalIndex > INT32_MAX)) {
     return IPC_FAIL_NO_REASON(this);
   }
 
-  nsCOMPtr<nsISHistory> shistory;
-  mWebNav->GetSessionHistory(getter_AddRefs(shistory));
+  nsCOMPtr<nsISHistory> shistory = GetRelatedSHistory();
   NS_ENSURE_TRUE(shistory, IPC_FAIL_NO_REASON(this));
 
   if (NS_FAILED(shistory->OnPartialSessionHistoryActive(aGlobalLength,
                                                         aTargetLocalIndex))) {
     return IPC_FAIL_NO_REASON(this);
   }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 TabChild::RecvNotifyPartialSessionHistoryDeactive()
 {
-  nsCOMPtr<nsISHistory> shistory;
-  mWebNav->GetSessionHistory(getter_AddRefs(shistory));
+  nsCOMPtr<nsISHistory> shistory = GetRelatedSHistory();
   NS_ENSURE_TRUE(shistory, IPC_FAIL_NO_REASON(this));
 
   if (NS_FAILED(shistory->OnPartialSessionHistoryDeactive())) {
     return IPC_FAIL_NO_REASON(this);
   }
   return IPC_OK();
 }
 
@@ -3088,16 +3084,48 @@ TabChild::ForcePaint(uint64_t aLayerObse
     // message on the PContent channel.
     return;
   }
 
   nsAutoScriptBlocker scriptBlocker;
   RecvSetDocShellIsActive(true, false, aLayerObserverEpoch);
 }
 
+already_AddRefed<nsISHistory>
+TabChild::GetRelatedSHistory()
+{
+  nsCOMPtr<nsISHistory> shistory;
+  mWebNav->GetSessionHistory(getter_AddRefs(shistory));
+  return shistory.forget();
+}
+
+nsresult
+TabChildSHistoryListener::SHistoryDidUpdate(bool aTruncate /* = false */)
+{
+  RefPtr<TabChild> tabChild(mTabChild);
+  if (NS_WARN_IF(!tabChild)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<nsISHistory> shistory = tabChild->GetRelatedSHistory();
+  NS_ENSURE_TRUE(shistory, NS_ERROR_FAILURE);
+
+  int32_t index, count;
+  nsresult rv = shistory->GetIndex(&index);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = shistory->GetCount(&count);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // XXX: It would be nice if we could batch these updates like SessionStore
+  // does, and provide a form of `Flush` command which would allow us to trigger
+  // an update, and wait for the state to become consistent.
+  NS_ENSURE_TRUE(tabChild->SendSHistoryUpdate(count, index, aTruncate), NS_ERROR_FAILURE);
+  return NS_OK;
+}
+
 /*******************************************************************************
  * nsISHistoryListener
  ******************************************************************************/
 
 NS_IMETHODIMP
 TabChildSHistoryListener::OnHistoryNewEntry(nsIURI *aNewURI, int32_t aOldIndex)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
@@ -3135,29 +3163,25 @@ TabChildSHistoryListener::OnHistoryPurge
 
 NS_IMETHODIMP
 TabChildSHistoryListener::OnHistoryReplaceEntry(int32_t aIndex)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
-TabChildSHistoryListener::OnLengthChange(int32_t aCount)
+TabChildSHistoryListener::OnLengthChanged(int32_t aCount)
 {
-  RefPtr<TabChild> tabChild(mTabChild);
-  if (!tabChild) {
-    return NS_ERROR_FAILURE;
-  }
-
-  if (aCount < 0) {
-    return NS_ERROR_FAILURE;
-  }
-
-  return tabChild->SendNotifySessionHistoryChange(aCount) ?
-           NS_OK : NS_ERROR_FAILURE;
+  return SHistoryDidUpdate(/* aTruncate = */ true);
+}
+
+NS_IMETHODIMP
+TabChildSHistoryListener::OnIndexChanged(int32_t aIndex)
+{
+  return SHistoryDidUpdate(/* aTruncate = */ false);
 }
 
 NS_IMETHODIMP
 TabChildSHistoryListener::OnRequestCrossBrowserNavigation(uint32_t aIndex)
 {
   RefPtr<TabChild> tabChild(mTabChild);
   if (!tabChild) {
     return NS_ERROR_FAILURE;
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -179,16 +179,18 @@ public:
   explicit TabChildSHistoryListener(TabChild* aTabChild) : mTabChild(aTabChild) {}
   void ClearTabChild() { mTabChild = nullptr; }
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSISHISTORYLISTENER
   NS_DECL_NSIPARTIALSHISTORYLISTENER
 
 private:
+  nsresult SHistoryDidUpdate(bool aTruncate = false);
+
   ~TabChildSHistoryListener() {}
   TabChild* mTabChild;
 };
 
 // This is base clase which helps to share Viewport and touch related
 // functionality between b2g/android FF/embedlite clients implementation.
 // It make sense to place in this class all helper functions, and functionality
 // which could be shared between Cross-process/Cross-thread implmentations.
@@ -656,16 +658,18 @@ public:
 
   bool TakeIsFreshProcess()
   {
     bool wasFreshProcess = mIsFreshProcess;
     mIsFreshProcess = false;
     return wasFreshProcess;
   }
 
+  already_AddRefed<nsISHistory> GetRelatedSHistory();
+
 protected:
   virtual ~TabChild();
 
   virtual PRenderFrameChild* AllocPRenderFrameChild() override;
 
   virtual bool DeallocPRenderFrameChild(PRenderFrameChild* aFrame) override;
 
   virtual mozilla::ipc::IPCResult RecvDestroy() override;
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -3219,33 +3219,33 @@ TabParent::RecvLookUpDictionary(const ns
   }
 
   widget->LookUpDictionary(aText, aFontRangeArray, aIsVertical,
                            aPoint - GetChildProcessOffset());
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
-TabParent::RecvNotifySessionHistoryChange(const uint32_t& aCount)
+TabParent::RecvSHistoryUpdate(const uint32_t& aCount, const uint32_t& aLocalIndex, const bool& aTruncate)
 {
   RefPtr<nsFrameLoader> frameLoader(GetFrameLoader());
   if (!frameLoader) {
     // FrameLoader can be nullptr if the it is destroying.
     // In this case session history change can simply be ignored.
     return IPC_OK();
   }
 
   nsCOMPtr<nsIPartialSHistory> partialHistory;
   frameLoader->GetPartialSessionHistory(getter_AddRefs(partialHistory));
   if (!partialHistory) {
     // PartialSHistory is not enabled
     return IPC_OK();
   }
 
-  partialHistory->OnSessionHistoryChange(aCount);
+  partialHistory->HandleSHistoryUpdate(aCount, aLocalIndex, aTruncate);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 TabParent::RecvRequestCrossBrowserNavigation(const uint32_t& aGlobalIndex)
 {
   RefPtr<nsFrameLoader> frameLoader(GetFrameLoader());
   if (!frameLoader) {
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -624,17 +624,19 @@ protected:
                                                     const int32_t& aX, const int32_t& aY,
                                                     const int32_t& aCx, const int32_t& aCy) override;
 
   virtual mozilla::ipc::IPCResult RecvGetTabCount(uint32_t* aValue) override;
 
   virtual mozilla::ipc::IPCResult RecvAudioChannelActivityNotification(const uint32_t& aAudioChannel,
                                                                        const bool& aActive) override;
 
-  virtual mozilla::ipc::IPCResult RecvNotifySessionHistoryChange(const uint32_t& aCount) override;
+  virtual mozilla::ipc::IPCResult RecvSHistoryUpdate(const uint32_t& aCount,
+                                                     const uint32_t& aLocalIndex,
+                                                     const bool& aTruncate) override;
 
   virtual mozilla::ipc::IPCResult RecvRequestCrossBrowserNavigation(const uint32_t& aGlobalIndex) override;
 
   ContentCacheInParent mContentCache;
 
   nsIntRect mRect;
   ScreenIntSize mDimensions;
   ScreenOrientationInternal mOrientation;
--- a/dom/media/webaudio/AnalyserNode.cpp
+++ b/dom/media/webaudio/AnalyserNode.cpp
@@ -96,16 +96,55 @@ public:
   virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override
   {
     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   }
 
   uint32_t mChunksToProcess = 0;
 };
 
+/* static */ already_AddRefed<AnalyserNode>
+AnalyserNode::Create(AudioContext& aAudioContext,
+                     const AnalyserOptions& aOptions,
+                     ErrorResult& aRv)
+{
+  if (aAudioContext.CheckClosed(aRv)) {
+    return nullptr;
+  }
+
+  RefPtr<AnalyserNode> analyserNode = new AnalyserNode(&aAudioContext);
+
+  analyserNode->Initialize(aOptions, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  analyserNode->SetFftSize(aOptions.mFftSize, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  analyserNode->SetMinDecibels(aOptions.mMinDecibels, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  analyserNode->SetMaxDecibels(aOptions.mMaxDecibels, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  analyserNode->SetSmoothingTimeConstant(aOptions.mSmoothingTimeConstant, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  return analyserNode.forget();
+}
+
 AnalyserNode::AnalyserNode(AudioContext* aContext)
   : AudioNode(aContext,
               1,
               ChannelCountMode::Max,
               ChannelInterpretation::Speakers)
   , mAnalysisBlock(2048)
   , mMinDecibels(-100.)
   , mMaxDecibels(-30.)
--- a/dom/media/webaudio/AnalyserNode.h
+++ b/dom/media/webaudio/AnalyserNode.h
@@ -10,26 +10,36 @@
 #include "AudioNode.h"
 #include "FFTBlock.h"
 #include "AlignedTArray.h"
 
 namespace mozilla {
 namespace dom {
 
 class AudioContext;
+struct AnalyserOptions;
 
 class AnalyserNode final : public AudioNode
 {
 public:
-  explicit AnalyserNode(AudioContext* aContext);
+  static already_AddRefed<AnalyserNode>
+  Create(AudioContext& aAudioContext, const AnalyserOptions& aOptions,
+         ErrorResult& aRv);
 
   NS_DECL_ISUPPORTS_INHERITED
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
+  static already_AddRefed<AnalyserNode>
+  Constructor(const GlobalObject& aGlobal, AudioContext& aAudioContext,
+              const AnalyserOptions& aOptions, ErrorResult& aRv)
+  {
+    return Create(aAudioContext, aOptions, aRv);
+  }
+
   void GetFloatFrequencyData(const Float32Array& aArray);
   void GetByteFrequencyData(const Uint8Array& aArray);
   void GetFloatTimeDomainData(const Float32Array& aArray);
   void GetByteTimeDomainData(const Uint8Array& aArray);
   uint32_t FftSize() const
   {
     return mAnalysisBlock.FFTSize();
   }
@@ -57,28 +67,29 @@ public:
   virtual const char* NodeType() const override
   {
     return "AnalyserNode";
   }
 
   virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override;
   virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override;
 
-protected:
-  ~AnalyserNode() {}
+private:
+  ~AnalyserNode() = default;
 
-private:
   friend class AnalyserNodeEngine;
   void AppendChunk(const AudioChunk& aChunk);
   bool AllocateBuffer();
   bool FFTAnalysis();
   void ApplyBlackmanWindow(float* aBuffer, uint32_t aSize);
   void GetTimeDomainData(float* aData, size_t aLength);
 
 private:
+  explicit AnalyserNode(AudioContext* aContext);
+
   FFTBlock mAnalysisBlock;
   nsTArray<AudioChunk> mChunks;
   double mMinDecibels;
   double mMaxDecibels;
   double mSmoothingTimeConstant;
   size_t mCurrentChunk = 0;
   AlignedTArray<float> mOutputBuffer;
 };
--- a/dom/media/webaudio/AudioBuffer.cpp
+++ b/dom/media/webaudio/AudioBuffer.cpp
@@ -175,16 +175,34 @@ AudioBuffer::AudioBuffer(AudioContext* a
 
 AudioBuffer::~AudioBuffer()
 {
   AudioBufferMemoryTracker::UnregisterAudioBuffer(this);
   ClearJSChannels();
   mozilla::DropJSObjects(this);
 }
 
+/* static */ already_AddRefed<AudioBuffer>
+AudioBuffer::Constructor(const GlobalObject& aGlobal,
+                         AudioContext& aAudioContext,
+                         const AudioBufferOptions& aOptions,
+                         ErrorResult& aRv)
+{
+  if (!aOptions.mNumberOfChannels) {
+    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
+    return nullptr;
+  }
+
+  float sampleRate = aOptions.mSampleRate.WasPassed()
+                       ? aOptions.mSampleRate.Value()
+                       : aAudioContext.SampleRate();
+  return Create(&aAudioContext, aOptions.mNumberOfChannels, aOptions.mLength,
+                sampleRate, aRv);
+}
+
 void
 AudioBuffer::ClearJSChannels()
 {
   mJSChannels.Clear();
 }
 
 /* static */ already_AddRefed<AudioBuffer>
 AudioBuffer::Create(AudioContext* aContext, uint32_t aNumberOfChannels,
--- a/dom/media/webaudio/AudioBuffer.h
+++ b/dom/media/webaudio/AudioBuffer.h
@@ -19,16 +19,17 @@
 
 namespace mozilla {
 
 class ErrorResult;
 class ThreadSharedFloatArrayBufferList;
 
 namespace dom {
 
+struct AudioBufferOptions;
 class AudioContext;
 
 /**
  * An AudioBuffer keeps its data either in the mJSChannels objects, which
  * are Float32Arrays, or in mSharedChannels if the mJSChannels objects' buffers
  * are detached.
  */
 class AudioBuffer final : public nsWrapperCache
@@ -51,16 +52,20 @@ public:
                   nullptr, aRv);
   }
 
   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
 
   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(AudioBuffer)
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(AudioBuffer)
 
+  static already_AddRefed<AudioBuffer>
+  Constructor(const GlobalObject& aGlobal, AudioContext& aAudioContext,
+              const AudioBufferOptions& aOptions, ErrorResult& aRv);
+
   nsPIDOMWindowInner* GetParentObject() const
   {
     nsCOMPtr<nsPIDOMWindowInner> parentObject = do_QueryReferent(mOwnerWindow);
     return parentObject;
   }
 
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
@@ -129,9 +134,8 @@ protected:
   uint32_t mLength;
   float mSampleRate;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
-
--- a/dom/media/webaudio/AudioBufferSourceNode.cpp
+++ b/dom/media/webaudio/AudioBufferSourceNode.cpp
@@ -603,20 +603,40 @@ AudioBufferSourceNode::AudioBufferSource
   AudioBufferSourceNodeEngine* engine = new AudioBufferSourceNodeEngine(this, aContext->Destination());
   mStream = AudioNodeStream::Create(aContext, engine,
                                     AudioNodeStream::NEED_MAIN_THREAD_FINISHED,
                                     aContext->Graph());
   engine->SetSourceStream(mStream);
   mStream->AddMainThreadListener(this);
 }
 
-AudioBufferSourceNode::~AudioBufferSourceNode()
+/* static */ already_AddRefed<AudioBufferSourceNode>
+AudioBufferSourceNode::Create(JSContext* aCx, AudioContext& aAudioContext,
+                              const AudioBufferSourceOptions& aOptions,
+                              ErrorResult& aRv)
 {
+  if (aAudioContext.CheckClosed(aRv)) {
+    return nullptr;
+  }
+
+  RefPtr<AudioBufferSourceNode> audioNode = new AudioBufferSourceNode(&aAudioContext);
+
+  if (aOptions.mBuffer.WasPassed()) {
+    MOZ_ASSERT(aCx);
+    audioNode->SetBuffer(aCx, aOptions.mBuffer.Value());
+  }
+
+  audioNode->Detune()->SetValue(aOptions.mDetune);
+  audioNode->SetLoop(aOptions.mLoop);
+  audioNode->SetLoopEnd(aOptions.mLoopEnd);
+  audioNode->SetLoopStart(aOptions.mLoopStart);
+  audioNode->PlaybackRate()->SetValue(aOptions.mPlaybackRate);
+
+  return audioNode.forget();
 }
-
 void
 AudioBufferSourceNode::DestroyMediaStream()
 {
   bool hadStream = mStream;
   if (hadStream) {
     mStream->RemoveMainThreadListener(this);
   }
   AudioNode::DestroyMediaStream();
--- a/dom/media/webaudio/AudioBufferSourceNode.h
+++ b/dom/media/webaudio/AudioBufferSourceNode.h
@@ -8,37 +8,47 @@
 #define AudioBufferSourceNode_h_
 
 #include "AudioNode.h"
 #include "AudioBuffer.h"
 
 namespace mozilla {
 namespace dom {
 
+struct AudioBufferSourceOptions;
 class AudioParam;
 
-class AudioBufferSourceNode final : public AudioNode,
-                                    public MainThreadMediaStreamListener
+class AudioBufferSourceNode final : public AudioNode
+                                  , public MainThreadMediaStreamListener
 {
 public:
-  explicit AudioBufferSourceNode(AudioContext* aContext);
+  static already_AddRefed<AudioBufferSourceNode>
+  Create(JSContext* aCx, AudioContext& aAudioContext,
+         const AudioBufferSourceOptions& aOptions, ErrorResult& aRv);
 
   void DestroyMediaStream() override;
 
   uint16_t NumberOfInputs() const final override
   {
     return 0;
   }
   AudioBufferSourceNode* AsAudioBufferSourceNode() override
   {
     return this;
   }
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(AudioBufferSourceNode, AudioNode)
 
+  static already_AddRefed<AudioBufferSourceNode>
+  Constructor(const GlobalObject& aGlobal, AudioContext& aAudioContext,
+              const AudioBufferSourceOptions& aOptions, ErrorResult& aRv)
+  {
+    return Create(aGlobal.Context(), aAudioContext, aOptions, aRv);
+  }
+
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   void Start(double aWhen, double aOffset,
              const Optional<double>& aDuration, ErrorResult& aRv);
   void Stop(double aWhen, ErrorResult& aRv);
 
   AudioBuffer* GetBuffer(JSContext* aCx) const
   {
@@ -94,20 +104,20 @@ public:
   const char* NodeType() const override
   {
     return "AudioBufferSourceNode";
   }
 
   size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override;
   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override;
 
-protected:
-  virtual ~AudioBufferSourceNode();
+private:
+  explicit AudioBufferSourceNode(AudioContext* aContext);
+  ~AudioBufferSourceNode() = default;
 
-private:
   friend class AudioBufferSourceNodeEngine;
   // START is sent during Start().
   // STOP is sent during Stop().
   // BUFFERSTART and BUFFEREND are sent when SetBuffer() and Start() have
   // been called (along with sending the buffer).
   enum EngineParameters {
     SAMPLE_RATE,
     START,
@@ -125,25 +135,23 @@ private:
     DETUNE,
     DOPPLERSHIFT
   };
 
   void SendLoopParametersToStream();
   void SendBufferParameterToStream(JSContext* aCx);
   void SendOffsetAndDurationParametersToStream(AudioNodeStream* aStream);
 
-private:
   double mLoopStart;
   double mLoopEnd;
   double mOffset;
   double mDuration;
   RefPtr<AudioBuffer> mBuffer;
   RefPtr<AudioParam> mPlaybackRate;
   RefPtr<AudioParam> mDetune;
   bool mLoop;
   bool mStartCalled;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
-
--- a/dom/media/webaudio/AudioContext.cpp
+++ b/dom/media/webaudio/AudioContext.cpp
@@ -7,20 +7,36 @@
 #include "AudioContext.h"
 
 #include "blink/PeriodicWave.h"
 
 #include "mozilla/ErrorResult.h"
 #include "mozilla/OwningNonNull.h"
 
 #include "mozilla/dom/AnalyserNode.h"
+#include "mozilla/dom/AnalyserNodeBinding.h"
+#include "mozilla/dom/AudioBufferSourceNodeBinding.h"
 #include "mozilla/dom/AudioContextBinding.h"
+#include "mozilla/dom/BiquadFilterNodeBinding.h"
+#include "mozilla/dom/ChannelMergerNodeBinding.h"
+#include "mozilla/dom/ChannelSplitterNodeBinding.h"
+#include "mozilla/dom/ConvolverNodeBinding.h"
+#include "mozilla/dom/DelayNodeBinding.h"
+#include "mozilla/dom/DynamicsCompressorNodeBinding.h"
+#include "mozilla/dom/GainNodeBinding.h"
+#include "mozilla/dom/IIRFilterNodeBinding.h"
 #include "mozilla/dom/HTMLMediaElement.h"
+#include "mozilla/dom/MediaElementAudioSourceNodeBinding.h"
+#include "mozilla/dom/MediaStreamAudioSourceNodeBinding.h"
 #include "mozilla/dom/OfflineAudioContextBinding.h"
+#include "mozilla/dom/OscillatorNodeBinding.h"
+#include "mozilla/dom/PannerNodeBinding.h"
 #include "mozilla/dom/Promise.h"
+#include "mozilla/dom/StereoPannerNodeBinding.h"
+#include "mozilla/dom/WaveShaperNodeBinding.h"
 
 #include "AudioBuffer.h"
 #include "AudioBufferSourceNode.h"
 #include "AudioChannelService.h"
 #include "AudioDestinationNode.h"
 #include "AudioListener.h"
 #include "AudioStream.h"
 #include "BiquadFilterNode.h"
@@ -236,23 +252,19 @@ bool AudioContext::CheckClosed(ErrorResu
     return true;
   }
   return false;
 }
 
 already_AddRefed<AudioBufferSourceNode>
 AudioContext::CreateBufferSource(ErrorResult& aRv)
 {
-  if (CheckClosed(aRv)) {
-    return nullptr;
-  }
-
-  RefPtr<AudioBufferSourceNode> bufferNode =
-    new AudioBufferSourceNode(this);
-  return bufferNode.forget();
+  return AudioBufferSourceNode::Create(nullptr, *this,
+                                       AudioBufferSourceOptions(),
+                                       aRv);
 }
 
 already_AddRefed<ConstantSourceNode>
 AudioContext::CreateConstantSource(ErrorResult& aRv)
 {
   if (CheckClosed(aRv)) {
     return nullptr;
   }
@@ -294,28 +306,18 @@ bool IsValidBufferSize(uint32_t aBufferS
   }
 }
 
 } // namespace
 
 already_AddRefed<MediaStreamAudioDestinationNode>
 AudioContext::CreateMediaStreamDestination(ErrorResult& aRv)
 {
-  if (mIsOffline) {
-    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
-    return nullptr;
-  }
-
-  if (CheckClosed(aRv)) {
-    return nullptr;
-  }
-
-  RefPtr<MediaStreamAudioDestinationNode> node =
-      new MediaStreamAudioDestinationNode(this);
-  return node.forget();
+  return MediaStreamAudioDestinationNode::Create(*this, AudioNodeOptions(),
+                                                 aRv);
 }
 
 already_AddRefed<ScriptProcessorNode>
 AudioContext::CreateScriptProcessor(uint32_t aBufferSize,
                                     uint32_t aNumberOfInputChannels,
                                     uint32_t aNumberOfOutputChannels,
                                     ErrorResult& aRv)
 {
@@ -335,244 +337,120 @@ AudioContext::CreateScriptProcessor(uint
     new ScriptProcessorNode(this, aBufferSize, aNumberOfInputChannels,
                             aNumberOfOutputChannels);
   return scriptProcessor.forget();
 }
 
 already_AddRefed<AnalyserNode>
 AudioContext::CreateAnalyser(ErrorResult& aRv)
 {
-  if (CheckClosed(aRv)) {
-    return nullptr;
-  }
-
-  RefPtr<AnalyserNode> analyserNode = new AnalyserNode(this);
-  return analyserNode.forget();
+  return AnalyserNode::Create(*this, AnalyserOptions(), aRv);
 }
 
 already_AddRefed<StereoPannerNode>
 AudioContext::CreateStereoPanner(ErrorResult& aRv)
 {
-  if (CheckClosed(aRv)) {
-    return nullptr;
-  }
-
-  RefPtr<StereoPannerNode> stereoPannerNode = new StereoPannerNode(this);
-  return stereoPannerNode.forget();
+  return StereoPannerNode::Create(*this, StereoPannerOptions(), aRv);
 }
 
 already_AddRefed<MediaElementAudioSourceNode>
 AudioContext::CreateMediaElementSource(HTMLMediaElement& aMediaElement,
                                        ErrorResult& aRv)
 {
-  if (mIsOffline) {
-    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
-    return nullptr;
-  }
-
-  if (aMediaElement.ContainsRestrictedContent()) {
-    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
-    return nullptr;
-  }
+  MediaElementAudioSourceOptions options;
+  options.mMediaElement = aMediaElement;
 
-  if (CheckClosed(aRv)) {
-    return nullptr;
-  }
-
-  RefPtr<DOMMediaStream> stream =
-    aMediaElement.CaptureAudio(aRv, mDestination->Stream()->Graph());
-  if (aRv.Failed()) {
-    return nullptr;
-  }
-  return MediaElementAudioSourceNode::Create(this, stream, aRv);
+  return MediaElementAudioSourceNode::Create(*this, options, aRv);
 }
 
 already_AddRefed<MediaStreamAudioSourceNode>
 AudioContext::CreateMediaStreamSource(DOMMediaStream& aMediaStream,
                                       ErrorResult& aRv)
 {
-  if (mIsOffline) {
-    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
-    return nullptr;
-  }
+  MediaStreamAudioSourceOptions options;
+  options.mMediaStream = aMediaStream;
 
-  if (CheckClosed(aRv)) {
-    return nullptr;
-  }
-
-  return MediaStreamAudioSourceNode::Create(this, &aMediaStream, aRv);
+  return MediaStreamAudioSourceNode::Create(*this, options, aRv);
 }
 
 already_AddRefed<GainNode>
 AudioContext::CreateGain(ErrorResult& aRv)
 {
-  if (CheckClosed(aRv)) {
-    return nullptr;
-  }
-
-  RefPtr<GainNode> gainNode = new GainNode(this);
-  return gainNode.forget();
+  return GainNode::Create(*this, GainOptions(), aRv);
 }
 
 already_AddRefed<WaveShaperNode>
 AudioContext::CreateWaveShaper(ErrorResult& aRv)
 {
-  if (CheckClosed(aRv)) {
-    return nullptr;
-  }
-
-  RefPtr<WaveShaperNode> waveShaperNode = new WaveShaperNode(this);
-  return waveShaperNode.forget();
+  return WaveShaperNode::Create(*this, WaveShaperOptions(), aRv);
 }
 
 already_AddRefed<DelayNode>
 AudioContext::CreateDelay(double aMaxDelayTime, ErrorResult& aRv)
 {
-  if (CheckClosed(aRv)) {
-    return nullptr;
-  }
-
-  if (aMaxDelayTime > 0. && aMaxDelayTime < 180.) {
-    RefPtr<DelayNode> delayNode = new DelayNode(this, aMaxDelayTime);
-    return delayNode.forget();
-  }
-
-  aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
-  return nullptr;
+  DelayOptions options;
+  options.mMaxDelayTime = aMaxDelayTime;
+  return DelayNode::Create(*this, options, aRv);
 }
 
 already_AddRefed<PannerNode>
 AudioContext::CreatePanner(ErrorResult& aRv)
 {
-  if (CheckClosed(aRv)) {
-    return nullptr;
-  }
-
-  RefPtr<PannerNode> pannerNode = new PannerNode(this);
-  mPannerNodes.PutEntry(pannerNode);
-  return pannerNode.forget();
+  return PannerNode::Create(*this, PannerOptions(), aRv);
 }
 
 already_AddRefed<ConvolverNode>
 AudioContext::CreateConvolver(ErrorResult& aRv)
 {
-  if (CheckClosed(aRv)) {
-    return nullptr;
-  }
-
-  RefPtr<ConvolverNode> convolverNode = new ConvolverNode(this);
-  return convolverNode.forget();
+  return ConvolverNode::Create(nullptr, *this, ConvolverOptions(), aRv);
 }
 
 already_AddRefed<ChannelSplitterNode>
 AudioContext::CreateChannelSplitter(uint32_t aNumberOfOutputs, ErrorResult& aRv)
 {
-  if (aNumberOfOutputs == 0 ||
-      aNumberOfOutputs > WebAudioUtils::MaxChannelCount) {
-    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
-    return nullptr;
-  }
-
-  if (CheckClosed(aRv)) {
-    return nullptr;
-  }
-
-  RefPtr<ChannelSplitterNode> splitterNode =
-    new ChannelSplitterNode(this, aNumberOfOutputs);
-  return splitterNode.forget();
+  ChannelSplitterOptions options;
+  options.mNumberOfOutputs = aNumberOfOutputs;
+  return ChannelSplitterNode::Create(*this, options, aRv);
 }
 
 already_AddRefed<ChannelMergerNode>
 AudioContext::CreateChannelMerger(uint32_t aNumberOfInputs, ErrorResult& aRv)
 {
-  if (aNumberOfInputs == 0 ||
-      aNumberOfInputs > WebAudioUtils::MaxChannelCount) {
-    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
-    return nullptr;
-  }
-
-  if (CheckClosed(aRv)) {
-    return nullptr;
-  }
-
-  RefPtr<ChannelMergerNode> mergerNode =
-    new ChannelMergerNode(this, aNumberOfInputs);
-  return mergerNode.forget();
+  ChannelMergerOptions options;
+  options.mNumberOfInputs = aNumberOfInputs;
+  return ChannelMergerNode::Create(*this, options, aRv);
 }
 
 already_AddRefed<DynamicsCompressorNode>
 AudioContext::CreateDynamicsCompressor(ErrorResult& aRv)
 {
-  if (CheckClosed(aRv)) {
-    return nullptr;
-  }
-
-  RefPtr<DynamicsCompressorNode> compressorNode =
-    new DynamicsCompressorNode(this);
-  return compressorNode.forget();
+  return DynamicsCompressorNode::Create(*this, DynamicsCompressorOptions(), aRv);
 }
 
 already_AddRefed<BiquadFilterNode>
 AudioContext::CreateBiquadFilter(ErrorResult& aRv)
 {
-  if (CheckClosed(aRv)) {
-    return nullptr;
-  }
-
-  RefPtr<BiquadFilterNode> filterNode =
-    new BiquadFilterNode(this);
-  return filterNode.forget();
+  return BiquadFilterNode::Create(*this, BiquadFilterOptions(), aRv);
 }
 
 already_AddRefed<IIRFilterNode>
 AudioContext::CreateIIRFilter(const mozilla::dom::binding_detail::AutoSequence<double>& aFeedforward,
                               const mozilla::dom::binding_detail::AutoSequence<double>& aFeedback,
                               mozilla::ErrorResult& aRv)
 {
-  if (CheckClosed(aRv)) {
-    return nullptr;
-  }
-
-  if (aFeedforward.Length() == 0 || aFeedforward.Length() > 20) {
-    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
-    return nullptr;
-  }
-
-  if (aFeedback.Length() == 0 || aFeedback.Length() > 20) {
-    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
-    return nullptr;
-  }
-
-  bool feedforwardAllZeros = true;
-  for (size_t i = 0; i < aFeedforward.Length(); ++i) {
-    if (aFeedforward.Elements()[i] != 0.0) {
-      feedforwardAllZeros = false;
-    }
-  }
-
-  if (feedforwardAllZeros || aFeedback.Elements()[0] == 0.0) {
-    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return nullptr;
-  }
-
-  RefPtr<IIRFilterNode> filterNode =
-    new IIRFilterNode(this, aFeedforward, aFeedback);
-  return filterNode.forget();
+  IIRFilterOptions options;
+  options.mFeedforward = aFeedforward;
+  options.mFeedback = aFeedback;
+  return IIRFilterNode::Create(*this, options, aRv);
 }
 
 already_AddRefed<OscillatorNode>
 AudioContext::CreateOscillator(ErrorResult& aRv)
 {
-  if (CheckClosed(aRv)) {
-    return nullptr;
-  }
-
-  RefPtr<OscillatorNode> oscillatorNode =
-    new OscillatorNode(this);
-  return oscillatorNode.forget();
+  return OscillatorNode::Create(*this, OscillatorOptions(), aRv);
 }
 
 already_AddRefed<PeriodicWave>
 AudioContext::CreatePeriodicWave(const Float32Array& aRealData,
                                  const Float32Array& aImagData,
                                  const PeriodicWaveConstraints& aConstraints,
                                  ErrorResult& aRv)
 {
--- a/dom/media/webaudio/AudioContext.h
+++ b/dom/media/webaudio/AudioContext.h
@@ -318,28 +318,28 @@ public:
 
   void OnStateChanged(void* aPromise, AudioContextState aNewState);
 
   BasicWaveFormCache* GetBasicWaveFormCache();
 
   IMPL_EVENT_HANDLER(mozinterruptbegin)
   IMPL_EVENT_HANDLER(mozinterruptend)
 
+  bool CheckClosed(ErrorResult& aRv);
+
 private:
   void DisconnectFromWindow();
   void RemoveFromDecodeQueue(WebAudioDecodeJob* aDecodeJob);
   void ShutdownDecoder();
 
   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
   NS_DECL_NSIMEMORYREPORTER
 
   friend struct ::mozilla::WebAudioDecodeJob;
 
-  bool CheckClosed(ErrorResult& aRv);
-
   nsTArray<MediaStream*> GetAllStreams() const;
 
 private:
   // Each AudioContext has an id, that is passed down the MediaStreams that
   // back the AudioNodes, so we can easily compute the set of all the
   // MediaStreams for a given context, on the MediasStreamGraph side.
   const AudioContextId mId;
   // Note that it's important for mSampleRate to be initialized before
--- a/dom/media/webaudio/AudioNode.cpp
+++ b/dom/media/webaudio/AudioNode.cpp
@@ -67,16 +67,38 @@ AudioNode::~AudioNode()
   MOZ_ASSERT(mOutputParams.IsEmpty());
   MOZ_ASSERT(!mStream,
              "The webaudio-node-demise notification must have been sent");
   if (mContext) {
     mContext->UnregisterNode(this);
   }
 }
 
+void
+AudioNode::Initialize(const AudioNodeOptions& aOptions, ErrorResult& aRv)
+{
+  if (aOptions.mChannelCount.WasPassed()) {
+    SetChannelCount(aOptions.mChannelCount.Value(), aRv);
+    if (NS_WARN_IF(aRv.Failed())) {
+      return;
+    }
+  }
+
+  if (aOptions.mChannelCountMode.WasPassed()) {
+    SetChannelCountModeValue(aOptions.mChannelCountMode.Value(), aRv);
+    if (NS_WARN_IF(aRv.Failed())) {
+      return;
+    }
+  }
+
+  if (aOptions.mChannelInterpretation.WasPassed()) {
+    SetChannelInterpretationValue(aOptions.mChannelInterpretation.Value());
+  }
+}
+
 size_t
 AudioNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
 {
   // Not owned:
   // - mContext
   // - mStream
   size_t amount = 0;
 
--- a/dom/media/webaudio/AudioNode.h
+++ b/dom/media/webaudio/AudioNode.h
@@ -246,16 +246,19 @@ private:
   }
   // Callers must hold a reference to 'this'.
   void DisconnectFromGraph();
 
   template<typename DestinationType>
   bool DisconnectFromOutputIfConnected(uint32_t aOutputIndex, uint32_t aInputIndex);
 
 protected:
+  // Helper for the Constructors for nodes.
+  void Initialize(const AudioNodeOptions& aOptions, ErrorResult& aRv);
+
   // Helpers for sending different value types to streams
   void SendDoubleParameterToStream(uint32_t aIndex, double aValue);
   void SendInt32ParameterToStream(uint32_t aIndex, int32_t aValue);
   void SendThreeDPointParameterToStream(uint32_t aIndex, const ThreeDPoint& aValue);
   void SendChannelMixingParametersToStream();
 
 private:
   RefPtr<AudioContext> mContext;
--- a/dom/media/webaudio/BiquadFilterNode.cpp
+++ b/dom/media/webaudio/BiquadFilterNode.cpp
@@ -258,18 +258,39 @@ BiquadFilterNode::BiquadFilterNode(Audio
 {
   uint64_t windowID = aContext->GetParentObject()->WindowID();
   BiquadFilterNodeEngine* engine = new BiquadFilterNodeEngine(this, aContext->Destination(), windowID);
   mStream = AudioNodeStream::Create(aContext, engine,
                                     AudioNodeStream::NO_STREAM_FLAGS,
                                     aContext->Graph());
 }
 
-BiquadFilterNode::~BiquadFilterNode()
+/* static */ already_AddRefed<BiquadFilterNode>
+BiquadFilterNode::Create(AudioContext& aAudioContext,
+                         const BiquadFilterOptions& aOptions,
+                         ErrorResult& aRv)
 {
+  if (aAudioContext.CheckClosed(aRv)) {
+    return nullptr;
+  }
+
+  RefPtr<BiquadFilterNode> audioNode = new BiquadFilterNode(&aAudioContext);
+
+  audioNode->Initialize(aOptions, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  audioNode->SetType(aOptions.mType);
+  audioNode->Q()->SetValue(aOptions.mQ);
+  audioNode->Detune()->SetValue(aOptions.mDetune);
+  audioNode->Frequency()->SetValue(aOptions.mFrequency);
+  audioNode->Gain()->SetValue(aOptions.mGain);
+
+  return audioNode.forget();
 }
 
 size_t
 BiquadFilterNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
 {
   size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
 
   if (mFrequency) {
--- a/dom/media/webaudio/BiquadFilterNode.h
+++ b/dom/media/webaudio/BiquadFilterNode.h
@@ -10,25 +10,35 @@
 #include "AudioNode.h"
 #include "AudioParam.h"
 #include "mozilla/dom/BiquadFilterNodeBinding.h"
 
 namespace mozilla {
 namespace dom {
 
 class AudioContext;
+struct BiquadFilterOptions;
 
 class BiquadFilterNode final : public AudioNode
 {
 public:
-  explicit BiquadFilterNode(AudioContext* aContext);
+  static already_AddRefed<BiquadFilterNode>
+  Create(AudioContext& aAudioContext, const BiquadFilterOptions& aOptions,
+         ErrorResult& aRv);
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(BiquadFilterNode, AudioNode)
 
+  static already_AddRefed<BiquadFilterNode>
+  Constructor(const GlobalObject& aGlobal, AudioContext& aAudioContext,
+              const BiquadFilterOptions& aOptions, ErrorResult& aRv)
+  {
+    return Create(aAudioContext, aOptions, aRv);
+  }
+
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   BiquadFilterType Type() const
   {
     return mType;
   }
   void SetType(BiquadFilterType aType);
 
@@ -59,24 +69,23 @@ public:
   const char* NodeType() const override
   {
     return "BiquadFilterNode";
   }
 
   size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override;
   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override;
 
-protected:
-  virtual ~BiquadFilterNode();
+private:
+  explicit BiquadFilterNode(AudioContext* aContext);
+  ~BiquadFilterNode() = default;
 
-private:
   BiquadFilterType mType;
   RefPtr<AudioParam> mFrequency;
   RefPtr<AudioParam> mDetune;
   RefPtr<AudioParam> mQ;
   RefPtr<AudioParam> mGain;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
-
--- a/dom/media/webaudio/ChannelMergerNode.cpp
+++ b/dom/media/webaudio/ChannelMergerNode.cpp
@@ -70,21 +70,42 @@ ChannelMergerNode::ChannelMergerNode(Aud
   , mInputCount(aInputCount)
 {
   mStream = AudioNodeStream::Create(aContext,
                                     new ChannelMergerNodeEngine(this),
                                     AudioNodeStream::NO_STREAM_FLAGS,
                                     aContext->Graph());
 }
 
-ChannelMergerNode::~ChannelMergerNode()
+/* static */ already_AddRefed<ChannelMergerNode>
+ChannelMergerNode::Create(AudioContext& aAudioContext,
+                          const ChannelMergerOptions& aOptions,
+                          ErrorResult& aRv)
 {
+  if (aAudioContext.CheckClosed(aRv)) {
+    return nullptr;
+  }
+
+  if (aOptions.mNumberOfInputs == 0 ||
+      aOptions.mNumberOfInputs > WebAudioUtils::MaxChannelCount) {
+    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
+    return nullptr;
+  }
+
+  RefPtr<ChannelMergerNode> audioNode =
+    new ChannelMergerNode(&aAudioContext, aOptions.mNumberOfInputs);
+
+  audioNode->Initialize(aOptions, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  return audioNode.forget();
 }
 
 JSObject*
 ChannelMergerNode::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return ChannelMergerNodeBinding::Wrap(aCx, this, aGivenProto);
 }
 
 } // namespace dom
 } // namespace mozilla
-
--- a/dom/media/webaudio/ChannelMergerNode.h
+++ b/dom/media/webaudio/ChannelMergerNode.h
@@ -8,43 +8,52 @@
 #define ChannelMergerNode_h_
 
 #include "AudioNode.h"
 
 namespace mozilla {
 namespace dom {
 
 class AudioContext;
+struct ChannelMergerOptions;
 
 class ChannelMergerNode final : public AudioNode
 {
 public:
-  ChannelMergerNode(AudioContext* aContext,
-                    uint16_t aInputCount);
+  static already_AddRefed<ChannelMergerNode>
+  Create(AudioContext& aAudioContext, const ChannelMergerOptions& aOptions,
+         ErrorResult& aRv);
 
   NS_DECL_ISUPPORTS_INHERITED
 
+  static already_AddRefed<ChannelMergerNode>
+  Constructor(const GlobalObject& aGlobal, AudioContext& aAudioContext,
+              const ChannelMergerOptions& aOptions, ErrorResult& aRv)
+  {
+    return Create(aAudioContext, aOptions, aRv);
+  }
+
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   uint16_t NumberOfInputs() const override { return mInputCount; }
 
   const char* NodeType() const override
   {
     return "ChannelMergerNode";
   }
 
   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override
   {
     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   }
 
-protected:
-  virtual ~ChannelMergerNode();
+private:
+  ChannelMergerNode(AudioContext* aContext,
+                    uint16_t aInputCount);
+  ~ChannelMergerNode() = default;
 
-private:
   const uint16_t mInputCount;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
-
--- a/dom/media/webaudio/ChannelSplitterNode.cpp
+++ b/dom/media/webaudio/ChannelSplitterNode.cpp
@@ -61,21 +61,42 @@ ChannelSplitterNode::ChannelSplitterNode
   , mOutputCount(aOutputCount)
 {
   mStream = AudioNodeStream::Create(aContext,
                                     new ChannelSplitterNodeEngine(this),
                                     AudioNodeStream::NO_STREAM_FLAGS,
                                     aContext->Graph());
 }
 
-ChannelSplitterNode::~ChannelSplitterNode()
+/* static */ already_AddRefed<ChannelSplitterNode>
+ChannelSplitterNode::Create(AudioContext& aAudioContext,
+                            const ChannelSplitterOptions& aOptions,
+                            ErrorResult& aRv)
 {
+  if (aAudioContext.CheckClosed(aRv)) {
+    return nullptr;
+  }
+
+  if (aOptions.mNumberOfOutputs == 0 ||
+      aOptions.mNumberOfOutputs > WebAudioUtils::MaxChannelCount) {
+    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
+    return nullptr;
+  }
+
+  RefPtr<ChannelSplitterNode> audioNode =
+    new ChannelSplitterNode(&aAudioContext, aOptions.mNumberOfOutputs);
+
+  audioNode->Initialize(aOptions, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  return audioNode.forget();
 }
 
 JSObject*
 ChannelSplitterNode::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return ChannelSplitterNodeBinding::Wrap(aCx, this, aGivenProto);
 }
 
 } // namespace dom
 } // namespace mozilla
-
--- a/dom/media/webaudio/ChannelSplitterNode.h
+++ b/dom/media/webaudio/ChannelSplitterNode.h
@@ -8,43 +8,52 @@
 #define ChannelSplitterNode_h_
 
 #include "AudioNode.h"
 
 namespace mozilla {
 namespace dom {
 
 class AudioContext;
+struct ChannelSplitterOptions;
 
 class ChannelSplitterNode final : public AudioNode
 {
 public:
-  ChannelSplitterNode(AudioContext* aContext,
-                      uint16_t aOutputCount);
+  static already_AddRefed<ChannelSplitterNode>
+  Create(AudioContext& aAudioContext, const ChannelSplitterOptions& aOptions,
+         ErrorResult& aRv);
 
   NS_DECL_ISUPPORTS_INHERITED
 
+  static already_AddRefed<ChannelSplitterNode>
+  Constructor(const GlobalObject& aGlobal, AudioContext& aAudioContext,
+              const ChannelSplitterOptions& aOptions, ErrorResult& aRv)
+  {
+    return Create(aAudioContext, aOptions, aRv);
+  }
+
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   uint16_t NumberOfOutputs() const override { return mOutputCount; }
 
   const char* NodeType() const override
   {
     return "ChannelSplitterNode";
   }
 
   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override
   {
     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   }
 
-protected:
-  virtual ~ChannelSplitterNode();
+private:
+  ChannelSplitterNode(AudioContext* aContext,
+                      uint16_t aOutputCount);
+  ~ChannelSplitterNode() = default;
 
-private:
   const uint16_t mOutputCount;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
-
--- a/dom/media/webaudio/ConvolverNode.cpp
+++ b/dom/media/webaudio/ConvolverNode.cpp
@@ -199,18 +199,44 @@ ConvolverNode::ConvolverNode(AudioContex
   , mNormalize(true)
 {
   ConvolverNodeEngine* engine = new ConvolverNodeEngine(this, mNormalize);
   mStream = AudioNodeStream::Create(aContext, engine,
                                     AudioNodeStream::NO_STREAM_FLAGS,
                                     aContext->Graph());
 }
 
-ConvolverNode::~ConvolverNode()
+/* static */ already_AddRefed<ConvolverNode>
+ConvolverNode::Create(JSContext* aCx, AudioContext& aAudioContext,
+                      const ConvolverOptions& aOptions,
+                      ErrorResult& aRv)
 {
+  if (aAudioContext.CheckClosed(aRv)) {
+    return nullptr;
+  }
+
+  RefPtr<ConvolverNode> audioNode = new ConvolverNode(&aAudioContext);
+
+  audioNode->Initialize(aOptions, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  // This must be done before setting the buffer.
+  audioNode->SetNormalize(!aOptions.mDisableNormalization);
+
+  if (aOptions.mBuffer.WasPassed()) {
+    MOZ_ASSERT(aCx);
+    audioNode->SetBuffer(aCx, aOptions.mBuffer.Value(), aRv);
+    if (NS_WARN_IF(aRv.Failed())) {
+      return nullptr;
+    }
+  }
+
+  return audioNode.forget();
 }
 
 size_t
 ConvolverNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
 {
   size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
   if (mBuffer) {
     // NB: mBuffer might be shared with the associated engine, by convention
@@ -287,9 +313,8 @@ void
 ConvolverNode::SetNormalize(bool aNormalize)
 {
   mNormalize = aNormalize;
   SendInt32ParameterToStream(ConvolverNodeEngine::NORMALIZE, aNormalize);
 }
 
 } // namespace dom
 } // namespace mozilla
-
--- a/dom/media/webaudio/ConvolverNode.h
+++ b/dom/media/webaudio/ConvolverNode.h
@@ -8,32 +8,44 @@
 #define ConvolverNode_h_
 
 #include "AudioNode.h"
 #include "AudioBuffer.h"
 
 namespace mozilla {
 namespace dom {
 
+class AudioContext;
+struct ConvolverOptions;
+
 class ConvolverNode final : public AudioNode
 {
 public:
-  explicit ConvolverNode(AudioContext* aContext);
+  static already_AddRefed<ConvolverNode>
+  Create(JSContext* aCx, AudioContext& aAudioContext,
+         const ConvolverOptions& aOptions, ErrorResult& aRv);
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ConvolverNode, AudioNode);
 
+  static already_AddRefed<ConvolverNode>
+  Constructor(const GlobalObject& aGlobal, AudioContext& aAudioContext,
+              const ConvolverOptions& aOptions, ErrorResult& aRv)
+  {
+    return Create(aGlobal.Context(), aAudioContext, aOptions, aRv);
+  }
+
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   AudioBuffer* GetBuffer(JSContext* aCx) const
   {
     return mBuffer;
   }
 
-  void SetBuffer(JSContext* aCx, AudioBuffer* aBufferi, ErrorResult& aRv);
+  void SetBuffer(JSContext* aCx, AudioBuffer* aBuffer, ErrorResult& aRv);
 
   bool Normalize() const
   {
     return mNormalize;
   }
 
   void SetNormalize(bool aNormal);
 
@@ -57,22 +69,21 @@ public:
   const char* NodeType() const override
   {
     return "ConvolverNode";
   }
 
   size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override;
   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override;
 
-protected:
-  virtual ~ConvolverNode();
+private:
+  explicit ConvolverNode(AudioContext* aContext);
+  ~ConvolverNode() = default;
 
-private:
   RefPtr<AudioBuffer> mBuffer;
   bool mNormalize;
 };
 
 
 } //end namespace dom
 } //end namespace mozilla
 
 #endif
-
--- a/dom/media/webaudio/DelayNode.cpp
+++ b/dom/media/webaudio/DelayNode.cpp
@@ -201,18 +201,40 @@ DelayNode::DelayNode(AudioContext* aCont
   DelayNodeEngine* engine =
     new DelayNodeEngine(this, aContext->Destination(),
                         aContext->SampleRate() * aMaxDelay);
   mStream = AudioNodeStream::Create(aContext, engine,
                                     AudioNodeStream::NO_STREAM_FLAGS,
                                     aContext->Graph());
 }
 
-DelayNode::~DelayNode()
+/* static */ already_AddRefed<DelayNode>
+DelayNode::Create(AudioContext& aAudioContext,
+                  const DelayOptions& aOptions,
+                  ErrorResult& aRv)
 {
+  if (aAudioContext.CheckClosed(aRv)) {
+    return nullptr;
+  }
+
+  if (aOptions.mMaxDelayTime <= 0. || aOptions.mMaxDelayTime >= 180.) {
+    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return nullptr;
+  }
+
+  RefPtr<DelayNode> audioNode = new DelayNode(&aAudioContext,
+                                              aOptions.mMaxDelayTime);
+
+  audioNode->Initialize(aOptions, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  audioNode->DelayTime()->SetValue(aOptions.mDelayTime);
+  return audioNode.forget();
 }
 
 size_t
 DelayNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
 {
   size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
   amount += mDelay->SizeOfIncludingThis(aMallocSizeOf);
   return amount;
--- a/dom/media/webaudio/DelayNode.h
+++ b/dom/media/webaudio/DelayNode.h
@@ -9,47 +9,55 @@
 
 #include "AudioNode.h"
 #include "AudioParam.h"
 
 namespace mozilla {
 namespace dom {
 
 class AudioContext;
+struct DelayOptions;
 
 class DelayNode final : public AudioNode
 {
 public:
-  DelayNode(AudioContext* aContext, double aMaxDelay);
+  static already_AddRefed<DelayNode>
+  Create(AudioContext& aAudioContext, const DelayOptions& aOptions,
+         ErrorResult& aRv);
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(DelayNode, AudioNode)
 
+  static already_AddRefed<DelayNode>
+  Constructor(const GlobalObject& aGlobal, AudioContext& aAudioContext,
+              const DelayOptions& aOptions, ErrorResult& aRv)
+  {
+    return Create(aAudioContext, aOptions, aRv);
+  }
+
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   AudioParam* DelayTime() const
   {
     return mDelay;
   }
 
   const char* NodeType() const override
   {
     return "DelayNode";
   }
 
   size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override;
   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override;
 
-protected:
-  virtual ~DelayNode();
+private:
+  DelayNode(AudioContext* aContext, double aMaxDelay);
+  ~DelayNode() = default;
 
-private:
   friend class DelayNodeEngine;
 
-private:
   RefPtr<AudioParam> mDelay;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
-
--- a/dom/media/webaudio/DynamicsCompressorNode.cpp
+++ b/dom/media/webaudio/DynamicsCompressorNode.cpp
@@ -200,18 +200,40 @@ DynamicsCompressorNode::DynamicsCompress
                             0.25f, "release"))
 {
   DynamicsCompressorNodeEngine* engine = new DynamicsCompressorNodeEngine(this, aContext->Destination());
   mStream = AudioNodeStream::Create(aContext, engine,
                                     AudioNodeStream::NO_STREAM_FLAGS,
                                     aContext->Graph());
 }
 
-DynamicsCompressorNode::~DynamicsCompressorNode()
+/* static */ already_AddRefed<DynamicsCompressorNode>
+DynamicsCompressorNode::Create(AudioContext& aAudioContext,
+                               const DynamicsCompressorOptions& aOptions,
+                               ErrorResult& aRv)
 {
+  if (aAudioContext.CheckClosed(aRv)) {
+    return nullptr;
+  }
+
+  RefPtr<DynamicsCompressorNode> audioNode =
+    new DynamicsCompressorNode(&aAudioContext);
+
+  audioNode->Initialize(aOptions, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  audioNode->Attack()->SetValue(aOptions.mAttack);
+  audioNode->Knee()->SetValue(aOptions.mKnee);
+  audioNode->Ratio()->SetValue(aOptions.mRatio);
+  audioNode->GetRelease()->SetValue(aOptions.mRelease);
+  audioNode->Threshold()->SetValue(aOptions.mThreshold);
+
+  return audioNode.forget();
 }
 
 size_t
 DynamicsCompressorNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
 {
   size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
   amount += mThreshold->SizeOfIncludingThis(aMallocSizeOf);
   amount += mKnee->SizeOfIncludingThis(aMallocSizeOf);
--- a/dom/media/webaudio/DynamicsCompressorNode.h
+++ b/dom/media/webaudio/DynamicsCompressorNode.h
@@ -9,25 +9,35 @@
 
 #include "AudioNode.h"
 #include "AudioParam.h"
 
 namespace mozilla {
 namespace dom {
 
 class AudioContext;
+struct DynamicsCompressorOptions;
 
 class DynamicsCompressorNode final : public AudioNode
 {
 public:
-  explicit DynamicsCompressorNode(AudioContext* aContext);
+  static already_AddRefed<DynamicsCompressorNode>
+  Create(AudioContext& aAudioContext, const DynamicsCompressorOptions& aOptions,
+         ErrorResult& aRv);
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(DynamicsCompressorNode, AudioNode)
 
+  static already_AddRefed<DynamicsCompressorNode>
+  Constructor(const GlobalObject& aGlobal, AudioContext& aAudioContext,
+              const DynamicsCompressorOptions& aOptions, ErrorResult& aRv)
+  {
+    return Create(aAudioContext, aOptions, aRv);
+  }
+
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   AudioParam* Threshold() const
   {
     return mThreshold;
   }
 
   AudioParam* Knee() const
@@ -65,25 +75,24 @@ public:
   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override;
 
   void SetReduction(float aReduction)
   {
     MOZ_ASSERT(NS_IsMainThread());
     mReduction = aReduction;
   }
 
-protected:
-  virtual ~DynamicsCompressorNode();
+private:
+  explicit DynamicsCompressorNode(AudioContext* aContext);
+  ~DynamicsCompressorNode() = default;
 
-private:
   RefPtr<AudioParam> mThreshold;
   RefPtr<AudioParam> mKnee;
   RefPtr<AudioParam> mRatio;
   float mReduction;
   RefPtr<AudioParam> mAttack;
   RefPtr<AudioParam> mRelease;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
-
--- a/dom/media/webaudio/GainNode.cpp
+++ b/dom/media/webaudio/GainNode.cpp
@@ -123,18 +123,34 @@ GainNode::GainNode(AudioContext* aContex
   , mGain(new AudioParam(this, GainNodeEngine::GAIN, 1.0f, "gain"))
 {
   GainNodeEngine* engine = new GainNodeEngine(this, aContext->Destination());
   mStream = AudioNodeStream::Create(aContext, engine,
                                     AudioNodeStream::NO_STREAM_FLAGS,
                                     aContext->Graph());
 }
 
-GainNode::~GainNode()
+/* static */ already_AddRefed<GainNode>
+GainNode::Create(AudioContext& aAudioContext,
+                 const GainOptions& aOptions,
+                 ErrorResult& aRv)
 {
+  if (aAudioContext.CheckClosed(aRv)) {
+    return nullptr;
+  }
+
+  RefPtr<GainNode> audioNode = new GainNode(&aAudioContext);
+
+  audioNode->Initialize(aOptions, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  audioNode->Gain()->SetValue(aOptions.mGain);
+  return audioNode.forget();
 }
 
 size_t
 GainNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
 {
   size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
   amount += mGain->SizeOfIncludingThis(aMallocSizeOf);
   return amount;
--- a/dom/media/webaudio/GainNode.h
+++ b/dom/media/webaudio/GainNode.h
@@ -9,44 +9,53 @@
 
 #include "AudioNode.h"
 #include "AudioParam.h"
 
 namespace mozilla {
 namespace dom {
 
 class AudioContext;
+struct GainOptions;
 
 class GainNode final : public AudioNode
 {
 public:
-  explicit GainNode(AudioContext* aContext);
+  static already_AddRefed<GainNode>
+  Create(AudioContext& aAudioContext, const GainOptions& aOptions,
+         ErrorResult& aRv);
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(GainNode, AudioNode)
 
+  static already_AddRefed<GainNode>
+  Constructor(const GlobalObject& aGlobal, AudioContext& aAudioContext,
+              const GainOptions& aOptions, ErrorResult& aRv)
+  {
+    return Create(aAudioContext, aOptions, aRv);
+  }
+
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   AudioParam* Gain() const
   {
     return mGain;
   }
 
   const char* NodeType() const override
   {
     return "GainNode";
   }
 
   size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override;
   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override;
 
-protected:
-  virtual ~GainNode();
+private:
+  explicit GainNode(AudioContext* aContext);
+  ~GainNode() = default;
 
-private:
   RefPtr<AudioParam> mGain;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
-
--- a/dom/media/webaudio/IIRFilterNode.cpp
+++ b/dom/media/webaudio/IIRFilterNode.cpp
@@ -128,18 +128,18 @@ private:
   AudioNodeStream* mDestination;
   nsTArray<nsAutoPtr<blink::IIRFilter>> mIIRFilters;
   AudioDoubleArray mFeedforward;
   AudioDoubleArray mFeedback;
   uint64_t mWindowID;
 };
 
 IIRFilterNode::IIRFilterNode(AudioContext* aContext,
-                             const mozilla::dom::binding_detail::AutoSequence<double>& aFeedforward,
-                             const mozilla::dom::binding_detail::AutoSequence<double>& aFeedback)
+                             const Sequence<double>& aFeedforward,
+                             const Sequence<double>& aFeedback)
   : AudioNode(aContext,
               2,
               ChannelCountMode::Max,
               ChannelInterpretation::Speakers)
 {
   mFeedforward.SetLength(aFeedforward.Length());
   PodCopy(mFeedforward.Elements(), aFeedforward.Elements(), aFeedforward.Length());
   mFeedback.SetLength(aFeedback.Length());
@@ -163,18 +163,56 @@ IIRFilterNode::IIRFilterNode(AudioContex
 
   uint64_t windowID = aContext->GetParentObject()->WindowID();
   IIRFilterNodeEngine* engine = new IIRFilterNodeEngine(this, aContext->Destination(), mFeedforward, mFeedback, windowID);
   mStream = AudioNodeStream::Create(aContext, engine,
                                     AudioNodeStream::NO_STREAM_FLAGS,
                                     aContext->Graph());
 }
 
-IIRFilterNode::~IIRFilterNode()
+/* static */ already_AddRefed<IIRFilterNode>
+IIRFilterNode::Create(AudioContext& aAudioContext,
+                 const IIRFilterOptions& aOptions,
+                 ErrorResult& aRv)
 {
+  if (aAudioContext.CheckClosed(aRv)) {
+    return nullptr;
+  }
+
+  if (aOptions.mFeedforward.Length() == 0 || aOptions.mFeedforward.Length() > 20) {
+    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return nullptr;
+  }
+
+  if (aOptions.mFeedback.Length() == 0 || aOptions.mFeedback.Length() > 20) {
+    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return nullptr;
+  }
+
+  bool feedforwardAllZeros = true;
+  for (size_t i = 0; i < aOptions.mFeedforward.Length(); ++i) {
+    if (aOptions.mFeedforward.Elements()[i] != 0.0) {
+      feedforwardAllZeros = false;
+    }
+  }
+
+  if (feedforwardAllZeros || aOptions.mFeedback.Elements()[0] == 0.0) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return nullptr;
+  }
+
+  RefPtr<IIRFilterNode> audioNode =
+    new IIRFilterNode(&aAudioContext, aOptions.mFeedforward, aOptions.mFeedback);
+
+  audioNode->Initialize(aOptions, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  return audioNode.forget();
 }
 
 size_t
 IIRFilterNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
 {
   size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
   return amount;
 }
--- a/dom/media/webaudio/IIRFilterNode.h
+++ b/dom/media/webaudio/IIRFilterNode.h
@@ -10,46 +10,54 @@
 #include "AudioNode.h"
 #include "AudioParam.h"
 #include "mozilla/dom/IIRFilterNodeBinding.h"
 
 namespace mozilla {
 namespace dom {
 
 class AudioContext;
+struct IIRFilterOptions;
 
 class IIRFilterNode final : public AudioNode
 {
 public:
-  explicit IIRFilterNode(AudioContext* aContext,
-                         const mozilla::dom::binding_detail::AutoSequence<double>& aFeedforward,
-                         const mozilla::dom::binding_detail::AutoSequence<double>& aFeedback);
+  static already_AddRefed<IIRFilterNode>
+  Create(AudioContext& aAudioContext, const IIRFilterOptions& aOptions,
+         ErrorResult& aRv);
 
   NS_DECL_ISUPPORTS_INHERITED
 
+  static already_AddRefed<IIRFilterNode>
+  Constructor(const GlobalObject& aGlobal, AudioContext& aAudioContext,
+              const IIRFilterOptions& aOptions, ErrorResult& aRv)
+  {
+    return Create(aAudioContext, aOptions, aRv);
+  }
+
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
-
   void GetFrequencyResponse(const Float32Array& aFrequencyHz,
                             const Float32Array& aMagResponse,
                             const Float32Array& aPhaseResponse);
 
   const char* NodeType() const override
   {
     return "IIRFilterNode";
   }
 
   size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override;
   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override;
 
-protected:
-  virtual ~IIRFilterNode();
+private:
+  IIRFilterNode(AudioContext* aContext,
+                const Sequence<double>& aFeedforward,
+                const Sequence<double>& aFeedback);
+  ~IIRFilterNode() = default;
 
-private:
-    nsTArray<double> mFeedback;
-    nsTArray<double> mFeedforward;
+  nsTArray<double> mFeedback;
+  nsTArray<double> mFeedforward;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
-
--- a/dom/media/webaudio/MediaElementAudioSourceNode.cpp
+++ b/dom/media/webaudio/MediaElementAudioSourceNode.cpp
@@ -11,23 +11,44 @@ namespace mozilla {
 namespace dom {
 
 MediaElementAudioSourceNode::MediaElementAudioSourceNode(AudioContext* aContext)
   : MediaStreamAudioSourceNode(aContext)
 {
 }
 
 /* static */ already_AddRefed<MediaElementAudioSourceNode>
-MediaElementAudioSourceNode::Create(AudioContext* aContext,
-                                    DOMMediaStream* aStream, ErrorResult& aRv)
+MediaElementAudioSourceNode::Create(AudioContext& aAudioContext,
+                                    const MediaElementAudioSourceOptions& aOptions,
+                                    ErrorResult& aRv)
 {
+  if (aAudioContext.IsOffline()) {
+    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return nullptr;
+  }
+
+  if (aOptions.mMediaElement->ContainsRestrictedContent()) {
+    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return nullptr;
+  }
+
+  if (aAudioContext.CheckClosed(aRv)) {
+    return nullptr;
+  }
+
   RefPtr<MediaElementAudioSourceNode> node =
-    new MediaElementAudioSourceNode(aContext);
+    new MediaElementAudioSourceNode(&aAudioContext);
 
-  node->Init(aStream, aRv);
+  RefPtr<DOMMediaStream> stream =
+    aOptions.mMediaElement->CaptureAudio(aRv, aAudioContext.Destination()->Stream()->Graph());
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+
+  node->Init(stream, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
 
   return node.forget();
 }
 
 JSObject*
--- a/dom/media/webaudio/MediaElementAudioSourceNode.h
+++ b/dom/media/webaudio/MediaElementAudioSourceNode.h
@@ -7,21 +7,33 @@
 #ifndef MediaElementAudioSourceNode_h_
 #define MediaElementAudioSourceNode_h_
 
 #include "MediaStreamAudioSourceNode.h"
 
 namespace mozilla {
 namespace dom {
 
+class AudioContext;
+struct MediaElementAudioSourceOptions;
+
 class MediaElementAudioSourceNode final : public MediaStreamAudioSourceNode
 {
 public:
   static already_AddRefed<MediaElementAudioSourceNode>
-  Create(AudioContext* aContext, DOMMediaStream* aStream, ErrorResult& aRv);
+  Create(AudioContext& aAudioContext,
+         const MediaElementAudioSourceOptions& aOptions,
+         ErrorResult& aRv);
+
+  static already_AddRefed<MediaElementAudioSourceNode>
+  Constructor(const GlobalObject& aGlobal, AudioContext& aAudioContext,
+              const MediaElementAudioSourceOptions& aOptions, ErrorResult& aRv)
+  {
+    return Create(aAudioContext, aOptions, aRv);
+  }
 
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   const char* NodeType() const override
   {
     return "MediaElementAudioSourceNode";
   }
 
--- a/dom/media/webaudio/MediaStreamAudioDestinationNode.cpp
+++ b/dom/media/webaudio/MediaStreamAudioDestinationNode.cpp
@@ -11,17 +11,17 @@
 #include "AudioNodeStream.h"
 #include "DOMMediaStream.h"
 #include "MediaStreamTrack.h"
 #include "TrackUnionStream.h"
 
 namespace mozilla {
 namespace dom {
 
-class AudioDestinationTrackSource :
+class AudioDestinationTrackSource final :
   public MediaStreamTrackSource
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(AudioDestinationTrackSource,
                                            MediaStreamTrackSource)
 
   AudioDestinationTrackSource(MediaStreamAudioDestinationNode* aNode,
@@ -45,17 +45,17 @@ public:
   }
 
   void Stop() override
   {
     Destroy();
   }
 
 private:
-  virtual ~AudioDestinationTrackSource() {}
+  ~AudioDestinationTrackSource() = default;
 
   RefPtr<MediaStreamAudioDestinationNode> mNode;
 };
 
 NS_IMPL_ADDREF_INHERITED(AudioDestinationTrackSource,
                          MediaStreamTrackSource)
 NS_IMPL_RELEASE_INHERITED(AudioDestinationTrackSource,
                           MediaStreamTrackSource)
@@ -97,18 +97,39 @@ MediaStreamAudioDestinationNode::MediaSt
   MOZ_ASSERT(!!outputStream);
   AudioNodeEngine* engine = new AudioNodeEngine(this);
   mStream = AudioNodeStream::Create(aContext, engine,
                                     AudioNodeStream::EXTERNAL_OUTPUT,
                                     aContext->Graph());
   mPort = outputStream->AllocateInputPort(mStream, AudioNodeStream::AUDIO_TRACK);
 }
 
-MediaStreamAudioDestinationNode::~MediaStreamAudioDestinationNode()
+/* static */ already_AddRefed<MediaStreamAudioDestinationNode>
+MediaStreamAudioDestinationNode::Create(AudioContext& aAudioContext,
+                                        const AudioNodeOptions& aOptions,
+                                        ErrorResult& aRv)
 {
+  if (aAudioContext.IsOffline()) {
+    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return nullptr;
+  }
+
+  if (aAudioContext.CheckClosed(aRv)) {
+    return nullptr;
+  }
+
+  RefPtr<MediaStreamAudioDestinationNode> audioNode =
+    new MediaStreamAudioDestinationNode(&aAudioContext);
+
+  audioNode->Initialize(aOptions, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  return audioNode.forget();
 }
 
 size_t
 MediaStreamAudioDestinationNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
 {
   // Future:
   // - mDOMStream
   size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
--- a/dom/media/webaudio/MediaStreamAudioDestinationNode.h
+++ b/dom/media/webaudio/MediaStreamAudioDestinationNode.h
@@ -7,24 +7,36 @@
 #ifndef MediaStreamAudioDestinationNode_h_
 #define MediaStreamAudioDestinationNode_h_
 
 #include "AudioNode.h"
 
 namespace mozilla {
 namespace dom {
 
+class AudioContext;
+struct AudioNodeOptions;
+
 class MediaStreamAudioDestinationNode final : public AudioNode
 {
 public:
-  explicit MediaStreamAudioDestinationNode(AudioContext* aContext);
+  static already_AddRefed<MediaStreamAudioDestinationNode>
+  Create(AudioContext& aAudioContext, const AudioNodeOptions& aOptions,
+         ErrorResult& aRv);
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MediaStreamAudioDestinationNode, AudioNode)
 
+  static already_AddRefed<MediaStreamAudioDestinationNode>
+  Constructor(const GlobalObject& aGlobal, AudioContext& aAudioContext,
+              const AudioNodeOptions& aOptions, ErrorResult& aRv)
+  {
+    return Create(aAudioContext, aOptions, aRv);
+  }
+
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   uint16_t NumberOfOutputs() const final override
   {
     return 0;
   }
 
   void DestroyMediaStream() override;
@@ -37,20 +49,20 @@ public:
   const char* NodeType() const override
   {
     return "MediaStreamAudioDestinationNode";
   }
 
   size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override;
   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override;
 
-protected:
-  virtual ~MediaStreamAudioDestinationNode();
+private:
+  explicit MediaStreamAudioDestinationNode(AudioContext* aContext);
+  ~MediaStreamAudioDestinationNode() = default;
 
-private:
   RefPtr<DOMMediaStream> mDOMStream;
   RefPtr<MediaInputPort> mPort;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
--- a/dom/media/webaudio/MediaStreamAudioSourceNode.cpp
+++ b/dom/media/webaudio/MediaStreamAudioSourceNode.cpp
@@ -38,23 +38,33 @@ MediaStreamAudioSourceNode::MediaStreamA
   : AudioNode(aContext,
               2,
               ChannelCountMode::Max,
               ChannelInterpretation::Speakers)
 {
 }
 
 /* static */ already_AddRefed<MediaStreamAudioSourceNode>
-MediaStreamAudioSourceNode::Create(AudioContext* aContext,
-                                   DOMMediaStream* aStream, ErrorResult& aRv)
+MediaStreamAudioSourceNode::Create(AudioContext& aAudioContext,
+                                   const MediaStreamAudioSourceOptions& aOptions,
+                                   ErrorResult& aRv)
 {
+  if (aAudioContext.IsOffline()) {
+    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return nullptr;
+  }
+
+  if (aAudioContext.CheckClosed(aRv)) {
+    return nullptr;
+  }
+
   RefPtr<MediaStreamAudioSourceNode> node =
-    new MediaStreamAudioSourceNode(aContext);
+    new MediaStreamAudioSourceNode(&aAudioContext);
 
-  node->Init(aStream, aRv);
+  node->Init(aOptions.mMediaStream, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
 
   return node.forget();
 }
 
 void
--- a/dom/media/webaudio/MediaStreamAudioSourceNode.h
+++ b/dom/media/webaudio/MediaStreamAudioSourceNode.h
@@ -10,16 +10,19 @@
 #include "AudioNode.h"
 #include "DOMMediaStream.h"
 #include "AudioNodeEngine.h"
 
 namespace mozilla {
 
 namespace dom {
 
+class AudioContext;
+struct MediaStreamAudioSourceOptions;
+
 class MediaStreamAudioSourceNodeEngine final : public AudioNodeEngine
 {
 public:
   explicit MediaStreamAudioSourceNodeEngine(AudioNode* aNode)
     : AudioNodeEngine(aNode), mEnabled(false) {}
 
   bool IsEnabled() const { return mEnabled; }
   enum Parameters {
@@ -41,21 +44,29 @@ private:
 };
 
 class MediaStreamAudioSourceNode : public AudioNode,
                                    public DOMMediaStream::TrackListener,
                                    public PrincipalChangeObserver<MediaStreamTrack>
 {
 public:
   static already_AddRefed<MediaStreamAudioSourceNode>
-  Create(AudioContext* aContext, DOMMediaStream* aStream, ErrorResult& aRv);
+  Create(AudioContext& aContext, const MediaStreamAudioSourceOptions& aOptions,
+         ErrorResult& aRv);
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MediaStreamAudioSourceNode, AudioNode)
 
+  static already_AddRefed<MediaStreamAudioSourceNode>
+  Constructor(const GlobalObject& aGlobal, AudioContext& aAudioContext,
+              const MediaStreamAudioSourceOptions& aOptions, ErrorResult& aRv)
+  {
+    return Create(aAudioContext, aOptions, aRv);
+  }
+
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   void DestroyMediaStream() override;
 
   uint16_t NumberOfInputs() const override { return 0; }
 
   const char* NodeType() const override
   {
--- a/dom/media/webaudio/OscillatorNode.cpp
+++ b/dom/media/webaudio/OscillatorNode.cpp
@@ -421,18 +421,45 @@ OscillatorNode::OscillatorNode(AudioCont
   OscillatorNodeEngine* engine = new OscillatorNodeEngine(this, aContext->Destination());
   mStream = AudioNodeStream::Create(aContext, engine,
                                     AudioNodeStream::NEED_MAIN_THREAD_FINISHED,
                                     aContext->Graph());
   engine->SetSourceStream(mStream);
   mStream->AddMainThreadListener(this);
 }
 
-OscillatorNode::~OscillatorNode()
+/* static */ already_AddRefed<OscillatorNode>
+OscillatorNode::Create(AudioContext& aAudioContext,
+                       const OscillatorOptions& aOptions,
+                       ErrorResult& aRv)
 {
+  if (aAudioContext.CheckClosed(aRv)) {
+    return nullptr;
+  }
+
+  RefPtr<OscillatorNode> audioNode = new OscillatorNode(&aAudioContext);
+
+  audioNode->Initialize(aOptions, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  audioNode->SetType(aOptions.mType, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  audioNode->Frequency()->SetValue(aOptions.mFrequency);
+  audioNode->Detune()->SetValue(aOptions.mDetune);
+
+  if (aOptions.mPeriodicWave.WasPassed()) {
+    audioNode->SetPeriodicWave(aOptions.mPeriodicWave.Value());
+  }
+
+  return audioNode.forget();
 }
 
 size_t
 OscillatorNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
 {
   size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
 
   // For now only report if we know for sure that it's not shared.
--- a/dom/media/webaudio/OscillatorNode.h
+++ b/dom/media/webaudio/OscillatorNode.h
@@ -11,26 +11,36 @@
 #include "AudioParam.h"
 #include "PeriodicWave.h"
 #include "mozilla/dom/OscillatorNodeBinding.h"
 
 namespace mozilla {
 namespace dom {
 
 class AudioContext;
+struct OscillatorOptions;
 
 class OscillatorNode final : public AudioNode,
                              public MainThreadMediaStreamListener
 {
 public:
-  explicit OscillatorNode(AudioContext* aContext);
+  static already_AddRefed<OscillatorNode>
+  Create(AudioContext& aAudioContext, const OscillatorOptions& aOptions,
+         ErrorResult& aRv);
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(OscillatorNode, AudioNode)
 
+  static already_AddRefed<OscillatorNode>
+  Constructor(const GlobalObject& aGlobal, AudioContext& aAudioContext,
+              const OscillatorOptions& aOptions, ErrorResult& aRv)
+  {
+    return Create(aAudioContext, aOptions, aRv);
+  }
+
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   void DestroyMediaStream() override;
 
   uint16_t NumberOfInputs() const final override
   {
     return 0;
   }
@@ -77,28 +87,26 @@ public:
   const char* NodeType() const override
   {
     return "OscillatorNode";
   }
 
   size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override;
   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override;
 
-protected:
-  virtual ~OscillatorNode();
+private:
+  explicit OscillatorNode(AudioContext* aContext);
+  ~OscillatorNode() = default;
 
-private:
   void SendTypeToStream();
   void SendPeriodicWaveToStream();
 
-private:
   OscillatorType mType;
   RefPtr<PeriodicWave> mPeriodicWave;
   RefPtr<AudioParam> mFrequency;
   RefPtr<AudioParam> mDetune;
   bool mStartCalled;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
-
--- a/dom/media/webaudio/PannerNode.cpp
+++ b/dom/media/webaudio/PannerNode.cpp
@@ -325,16 +325,48 @@ PannerNode::PannerNode(AudioContext* aCo
 
 PannerNode::~PannerNode()
 {
   if (Context()) {
     Context()->UnregisterPannerNode(this);
   }
 }
 
+/* static */ already_AddRefed<PannerNode>
+PannerNode::Create(AudioContext& aAudioContext,
+                   const PannerOptions& aOptions,
+                   ErrorResult& aRv)
+{
+  if (aAudioContext.CheckClosed(aRv)) {
+    return nullptr;
+  }
+
+  RefPtr<PannerNode> audioNode = new PannerNode(&aAudioContext);
+
+  audioNode->Initialize(aOptions, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  audioNode->SetPanningModel(aOptions.mPanningModel);
+  audioNode->SetDistanceModel(aOptions.mDistanceModel);
+  audioNode->SetPosition(aOptions.mPositionX, aOptions.mPositionY,
+                         aOptions.mPositionZ);
+  audioNode->SetOrientation(aOptions.mOrientationX, aOptions.mOrientationY,
+                            aOptions.mOrientationZ);
+  audioNode->SetRefDistance(aOptions.mRefDistance);
+  audioNode->SetMaxDistance(aOptions.mMaxDistance);
+  audioNode->SetRolloffFactor(aOptions.mRolloffFactor);
+  audioNode->SetConeInnerAngle(aOptions.mConeInnerAngle);
+  audioNode->SetConeOuterAngle(aOptions.mConeOuterAngle);
+  audioNode->SetConeOuterGain(aOptions.mConeOuterGain);
+
+  return audioNode.forget();
+}
+
 void PannerNode::SetPanningModel(PanningModelType aPanningModel)
 {
   mPanningModel = aPanningModel;
   if (mPanningModel == PanningModelType::HRTF) {
     // We can set the engine's `mHRTFPanner` member here from the main thread,
     // because the engine will not touch it from the MediaStreamGraph
     // thread until the PANNING_MODEL message sent below is received.
     static_cast<PannerNodeEngine*>(mStream->Engine())->CreateHRTFPanner();
@@ -778,9 +810,8 @@ PannerNode::SendDopplerToSourcesIfNeeded
       mSources[i]->SendDopplerShiftToStream(ComputeDopplerShift());
     }
   }
 }
 
 
 } // namespace dom
 } // namespace mozilla
-
--- a/dom/media/webaudio/PannerNode.h
+++ b/dom/media/webaudio/PannerNode.h
@@ -15,23 +15,34 @@
 #include <limits>
 #include <set>
 
 namespace mozilla {
 namespace dom {
 
 class AudioContext;
 class AudioBufferSourceNode;
+struct PannerOptions;
 
 class PannerNode final : public AudioNode,
                          public SupportsWeakPtr<PannerNode>
 {
 public:
+  static already_AddRefed<PannerNode>
+  Create(AudioContext& aAudioContext, const PannerOptions& aOptions,
+         ErrorResult& aRv);
+
   MOZ_DECLARE_WEAKREFERENCE_TYPENAME(PannerNode)
-  explicit PannerNode(AudioContext* aContext);
+
+  static already_AddRefed<PannerNode>
+  Constructor(const GlobalObject& aGlobal, AudioContext& aAudioContext,
+              const PannerOptions& aOptions, ErrorResult& aRv)
+  {
+    return Create(aAudioContext, aOptions, aRv);
+  }
 
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   void DestroyMediaStream() override;
 
   void SetChannelCount(uint32_t aChannelCount, ErrorResult& aRv) override
   {
     if (aChannelCount > 2) {
@@ -225,20 +236,20 @@ public:
   const char* NodeType() const override
   {
     return "PannerNode";
   }
 
   size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override;
   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override;
 
-protected:
-  virtual ~PannerNode();
+private:
+  explicit PannerNode(AudioContext* aContext);
+  ~PannerNode();
 
-private:
   friend class AudioListener;
   friend class PannerNodeEngine;
   enum EngineParameters {
     LISTENER_POSITION,
     LISTENER_FRONT_VECTOR, // unit length
     LISTENER_RIGHT_VECTOR, // unit length, orthogonal to LISTENER_FRONT_VECTOR
     LISTENER_VELOCITY,
     LISTENER_DOPPLER_FACTOR,
@@ -288,9 +299,8 @@ private:
   // to this AudioPannerNode.
   nsTArray<AudioBufferSourceNode*> mSources;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
-
--- a/dom/media/webaudio/PeriodicWave.cpp
+++ b/dom/media/webaudio/PeriodicWave.cpp
@@ -39,16 +39,42 @@ PeriodicWave::PeriodicWave(AudioContext*
     return;
   }
   PodCopy(buffer, aRealData, aLength);
   mCoefficients->SetData(0, buffer, free, buffer);
   PodCopy(buffer+aLength, aImagData, aLength);
   mCoefficients->SetData(1, nullptr, free, buffer+aLength);
 }
 
+/* static */ already_AddRefed<PeriodicWave>
+PeriodicWave::Constructor(const GlobalObject& aGlobal,
+                          AudioContext& aAudioContext,
+                          const PeriodicWaveOptions& aOptions,
+                          ErrorResult& aRv)
+{
+  if (!aOptions.mReal.WasPassed() || !aOptions.mImag.WasPassed() ||
+      aOptions.mReal.Value().Length() != aOptions.mImag.Value().Length() ||
+      aOptions.mReal.Value().Length() == 0) {
+    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return nullptr;
+  }
+
+  RefPtr<PeriodicWave> wave =
+    new PeriodicWave(&aAudioContext, aOptions.mReal.Value().Elements(),
+                     aOptions.mImag.Value().Elements(),
+                     aOptions.mReal.Value().Length(),
+                     aOptions.mDisableNormalization,
+                     aRv);
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+
+  return wave.forget();
+}
+
 size_t
 PeriodicWave::SizeOfExcludingThisIfNotShared(MallocSizeOf aMallocSizeOf) const
 {
   // Not owned:
   // - mContext
   size_t amount = 0;
   if (!mCoefficients->IsShared()) {
     amount += mCoefficients->SizeOfIncludingThis(aMallocSizeOf);
@@ -66,9 +92,8 @@ PeriodicWave::SizeOfIncludingThisIfNotSh
 JSObject*
 PeriodicWave::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return PeriodicWaveBinding::Wrap(aCx, this, aGivenProto);
 }
 
 } // namespace dom
 } // namespace mozilla
-
--- a/dom/media/webaudio/PeriodicWave.h
+++ b/dom/media/webaudio/PeriodicWave.h
@@ -12,29 +12,36 @@
 #include "mozilla/Attributes.h"
 #include "AudioContext.h"
 #include "AudioNodeEngine.h"
 
 namespace mozilla {
 
 namespace dom {
 
+class AudioContext;
+struct PeriodicWaveOptions;
+
 class PeriodicWave final : public nsWrapperCache
 {
 public:
   PeriodicWave(AudioContext* aContext,
                const float* aRealData,
                const float* aImagData,
                const uint32_t aLength,
                const bool aDisableNormalization,
                ErrorResult& aRv);
 
   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(PeriodicWave)
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(PeriodicWave)
 
+  static already_AddRefed<PeriodicWave>
+  Constructor(const GlobalObject& aGlobal, AudioContext& aAudioContext,
+              const PeriodicWaveOptions& aOptions, ErrorResult& aRv);
+
   AudioContext* GetParentObject() const
   {
     return mContext;
   }
 
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   uint32_t DataLength() const
@@ -51,17 +58,17 @@ public:
   {
     return mCoefficients;
   }
 
   size_t SizeOfExcludingThisIfNotShared(MallocSizeOf aMallocSizeOf) const;
   size_t SizeOfIncludingThisIfNotShared(MallocSizeOf aMallocSizeOf) const;
 
 private:
-  ~PeriodicWave() {}
+  ~PeriodicWave() = default;
 
   RefPtr<AudioContext> mContext;
   RefPtr<ThreadSharedFloatArrayBufferList> mCoefficients;
   uint32_t mLength;
   bool mDisableNormalization;
 };
 
 } // namespace dom
--- a/dom/media/webaudio/StereoPannerNode.cpp
+++ b/dom/media/webaudio/StereoPannerNode.cpp
@@ -178,18 +178,34 @@ StereoPannerNode::StereoPannerNode(Audio
   , mPan(new AudioParam(this, StereoPannerNodeEngine::PAN, 0.f, "pan"))
 {
   StereoPannerNodeEngine* engine = new StereoPannerNodeEngine(this, aContext->Destination());
   mStream = AudioNodeStream::Create(aContext, engine,
                                     AudioNodeStream::NO_STREAM_FLAGS,
                                     aContext->Graph());
 }
 
-StereoPannerNode::~StereoPannerNode()
+/* static */ already_AddRefed<StereoPannerNode>
+StereoPannerNode::Create(AudioContext& aAudioContext,
+                         const StereoPannerOptions& aOptions,
+                         ErrorResult& aRv)
 {
+  if (aAudioContext.CheckClosed(aRv)) {
+    return nullptr;
+  }
+
+  RefPtr<StereoPannerNode> audioNode = new StereoPannerNode(&aAudioContext);
+
+  audioNode->Initialize(aOptions, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  audioNode->Pan()->SetValue(aOptions.mPan);
+  return audioNode.forget();
 }
 
 size_t
 StereoPannerNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
 {
   size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
   amount += mPan->SizeOfIncludingThis(aMallocSizeOf);
   return amount;
--- a/dom/media/webaudio/StereoPannerNode.h
+++ b/dom/media/webaudio/StereoPannerNode.h
@@ -9,22 +9,33 @@
 
 #include "AudioNode.h"
 #include "mozilla/dom/StereoPannerNodeBinding.h"
 
 namespace mozilla {
 namespace dom {
 
 class AudioContext;
+struct StereoPannerOptions;
 
 class StereoPannerNode final : public AudioNode
 {
 public:
+  static already_AddRefed<StereoPannerNode>
+  Create(AudioContext& aAudioContext, const StereoPannerOptions& aOptions,
+         ErrorResult& aRv);
+
   MOZ_DECLARE_REFCOUNTED_TYPENAME(StereoPannerNode)
-  explicit StereoPannerNode(AudioContext* aContext);
+
+  static already_AddRefed<StereoPannerNode>
+  Constructor(const GlobalObject& aGlobal, AudioContext& aAudioContext,
+              const StereoPannerOptions& aOptions, ErrorResult& aRv)
+  {
+    return Create(aAudioContext, aOptions, aRv);
+  }
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   virtual void SetChannelCount(uint32_t aChannelCount, ErrorResult& aRv) override
   {
     if (aChannelCount > 2) {
       aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
       return;
@@ -51,20 +62,19 @@ public:
   virtual const char* NodeType() const override
   {
     return "StereoPannerNode";
   }
 
   virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override;
   virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override;
 
-protected:
-  virtual ~StereoPannerNode();
+private:
+  explicit StereoPannerNode(AudioContext* aContext);
+  ~StereoPannerNode() = default;
 
-private:
   RefPtr<AudioParam> mPan;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
-
--- a/dom/media/webaudio/WaveShaperNode.cpp
+++ b/dom/media/webaudio/WaveShaperNode.cpp
@@ -14,26 +14,24 @@
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(WaveShaperNode)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WaveShaperNode, AudioNode)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
-  tmp->ClearCurve();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WaveShaperNode, AudioNode)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(WaveShaperNode)
   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
-  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCurve)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(WaveShaperNode)
 NS_INTERFACE_MAP_END_INHERITING(AudioNode)
 
 NS_IMPL_ADDREF_INHERITED(WaveShaperNode, AudioNode)
 NS_IMPL_RELEASE_INHERITED(WaveShaperNode, AudioNode)
 
@@ -310,83 +308,135 @@ private:
   Resampler mResampler;
 };
 
 WaveShaperNode::WaveShaperNode(AudioContext* aContext)
   : AudioNode(aContext,
               2,
               ChannelCountMode::Max,
               ChannelInterpretation::Speakers)
-  , mCurve(nullptr)
   , mType(OverSampleType::None)
 {
-  mozilla::HoldJSObjects(this);
-
   WaveShaperNodeEngine* engine = new WaveShaperNodeEngine(this);
   mStream = AudioNodeStream::Create(aContext, engine,
                                     AudioNodeStream::NO_STREAM_FLAGS,
                                     aContext->Graph());
 }
 
-WaveShaperNode::~WaveShaperNode()
+/* static */ already_AddRefed<WaveShaperNode>
+WaveShaperNode::Create(AudioContext& aAudioContext,
+                       const WaveShaperOptions& aOptions,
+                       ErrorResult& aRv)
 {
-  ClearCurve();
-}
+  if (aAudioContext.CheckClosed(aRv)) {
+    return nullptr;
+  }
+
+  RefPtr<WaveShaperNode> audioNode = new WaveShaperNode(&aAudioContext);
 
-void
-WaveShaperNode::ClearCurve()
-{
-  mCurve = nullptr;
-  mozilla::DropJSObjects(this);
+  audioNode->Initialize(aOptions, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  if (aOptions.mCurve.WasPassed()) {
+    audioNode->SetCurveInternal(aOptions.mCurve.Value(), aRv);
+    if (NS_WARN_IF(aRv.Failed())) {
+      return nullptr;
+    }
+  }
+
+  audioNode->SetOversample(aOptions.mOversample);
+  return audioNode.forget();
 }
 
 JSObject*
 WaveShaperNode::WrapObject(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return WaveShaperNodeBinding::Wrap(aCx, this, aGivenProto);
 }
 
 void
 WaveShaperNode::SetCurve(const Nullable<Float32Array>& aCurve, ErrorResult& aRv)
 {
-  nsTArray<float> curve;
-  if (!aCurve.IsNull()) {
-    const Float32Array& floats = aCurve.Value();
+  // Let's purge the cached value for the curve attribute.
+  WaveShaperNodeBinding::ClearCachedCurveValue(this);
 
-    floats.ComputeLengthAndData();
-    if (floats.IsShared()) {
-      // Throw if the object is mapping shared memory (must opt in).
-      aRv.ThrowTypeError<MSG_TYPEDARRAY_IS_SHARED>(NS_LITERAL_STRING("Argument of WaveShaperNode.setCurve"));
-      return;
-    }
+  if (aCurve.IsNull()) {
+    CleanCurveInternal();
+    return;
+  }
+
+  const Float32Array& floats = aCurve.Value();
 
-    uint32_t argLength = floats.Length();
-    if (argLength < 2) {
-      aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
-      return;
-    }
+  floats.ComputeLengthAndData();
+  if (floats.IsShared()) {
+    // Throw if the object is mapping shared memory (must opt in).
+    aRv.ThrowTypeError<MSG_TYPEDARRAY_IS_SHARED>(NS_LITERAL_STRING("Argument of WaveShaperNode.setCurve"));
+    return;
+  }
 
-    if (!curve.SetLength(argLength, fallible)) {
-      aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
-      return;
-    }
-
-    PodCopy(curve.Elements(), floats.Data(), floats.Length());
-
-    mCurve = floats.Obj();
-  } else {
-    mCurve = nullptr;
+  nsTArray<float> curve;
+  uint32_t argLength = floats.Length();
+  if (!curve.SetLength(argLength, fallible)) {
+    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
+    return;
   }
 
+  PodCopy(curve.Elements(), floats.Data(), argLength);
+  SetCurveInternal(curve, aRv);
+}
+
+void
+WaveShaperNode::SetCurveInternal(const nsTArray<float>& aCurve,
+                                 ErrorResult& aRv)
+{
+  if (aCurve.Length() < 2) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return;
+  }
+
+  mCurve = aCurve;
+  SendCurveToStream();
+}
+
+void
+WaveShaperNode::CleanCurveInternal()
+{
+  mCurve.Clear();
+  SendCurveToStream();
+}
+
+void
+WaveShaperNode::SendCurveToStream()
+{
   AudioNodeStream* ns = mStream;
   MOZ_ASSERT(ns, "Why don't we have a stream here?");
-  ns->SetRawArrayData(curve);
+
+  nsTArray<float> copyCurve(mCurve);
+  ns->SetRawArrayData(copyCurve);
+}
+
+void
+WaveShaperNode::GetCurve(JSContext* aCx,
+                         JS::MutableHandle<JSObject*> aRetval)
+{
+  // Let's return a null value if the list is empty.
+  if (mCurve.IsEmpty()) {
+    aRetval.set(nullptr);
+    return;
+  }
+
+  MOZ_ASSERT(mCurve.Length() >= 2);
+  aRetval.set(Float32Array::Create(aCx, this, mCurve.Length(),
+                                   mCurve.Elements()));
 }
 
 void
 WaveShaperNode::SetOversample(OverSampleType aType)
 {
   mType = aType;
-  SendInt32ParameterToStream(WaveShaperNodeEngine::TYPE, static_cast<int32_t>(aType));
+  SendInt32ParameterToStream(WaveShaperNodeEngine::TYPE,
+                             static_cast<int32_t>(aType));
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/media/webaudio/WaveShaperNode.h
+++ b/dom/media/webaudio/WaveShaperNode.h
@@ -10,31 +10,38 @@
 #include "AudioNode.h"
 #include "mozilla/dom/WaveShaperNodeBinding.h"
 #include "mozilla/dom/TypedArray.h"
 
 namespace mozilla {
 namespace dom {
 
 class AudioContext;
+struct WaveShaperOptions;
 
 class WaveShaperNode final : public AudioNode
 {
 public:
-  explicit WaveShaperNode(AudioContext *aContext);
+  static already_AddRefed<WaveShaperNode>
+  Create(AudioContext& aAudioContext, const WaveShaperOptions& aOptions,
+         ErrorResult& aRv);
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(WaveShaperNode, AudioNode)
 
+  static already_AddRefed<WaveShaperNode>
+  Constructor(const GlobalObject& aGlobal, AudioContext& aAudioContext,
+              const WaveShaperOptions& aOptions, ErrorResult& aRv)
+  {
+    return Create(aAudioContext, aOptions, aRv);
+  }
+
   JSObject* WrapObject(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override;
 
-  void GetCurve(JSContext* aCx, JS::MutableHandle<JSObject*> aRetval) const
-  {
-    aRetval.set(mCurve);
-  }
+  void GetCurve(JSContext* aCx, JS::MutableHandle<JSObject*> aRetval);
   void SetCurve(const Nullable<Float32Array>& aData, ErrorResult& aRv);
 
   OverSampleType Oversample() const
   {
     return mType;
   }
   void SetOversample(OverSampleType aType);
 
@@ -50,23 +57,26 @@ public:
     return "WaveShaperNode";
   }
 
   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override
   {
     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   }
 
-protected:
-  virtual ~WaveShaperNode();
+private:
+  explicit WaveShaperNode(AudioContext *aContext);
+  ~WaveShaperNode() = default;
 
-private:
-  void ClearCurve();
+  void SetCurveInternal(const nsTArray<float>& aCurve,
+                        ErrorResult& aRv);
+  void CleanCurveInternal();
 
-private:
-  JS::Heap<JSObject*> mCurve;
+  void SendCurveToStream();
+
+  nsTArray<float> mCurve;
   OverSampleType mType;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
--- a/dom/media/webaudio/test/test_analyserNode.html
+++ b/dom/media/webaudio/test/test_analyserNode.html
@@ -5,19 +5,17 @@
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="text/javascript" src="webaudio.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
-SimpleTest.waitForExplicitFinish();
-addLoadEvent(function() {
-
+function testNode() {
   var context = new AudioContext();
   var buffer = context.createBuffer(1, 2048, context.sampleRate);
   for (var i = 0; i < 2048; ++i) {
     buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
   }
 
   var destination = context.destination;
 
@@ -79,25 +77,102 @@ addLoadEvent(function() {
   }, DOMException.INDEX_SIZE_ERR);
   expectException(function() {
     analyser.maxDecibels = -100;
   }, DOMException.INDEX_SIZE_ERR);
   expectException(function() {
     analyser.maxDecibels = -101;
   }, DOMException.INDEX_SIZE_ERR);
 
-  is(analyser.smoothingTimeConstant, 0.8, "Correct default value for smoothingTimeConstant");
+  ok(Math.abs(analyser.smoothingTimeConstant - 0.8) < 0.001, "Correct default value for smoothingTimeConstant");
   expectException(function() {
     analyser.smoothingTimeConstant = -0.1;
   }, DOMException.INDEX_SIZE_ERR);
   expectException(function() {
     analyser.smoothingTimeConstant = 1.1;
   }, DOMException.INDEX_SIZE_ERR);
   analyser.smoothingTimeConstant = 0;
   analyser.smoothingTimeConstant = 1;
+}
+
+function testConstructor() {
+  var context = new AudioContext();
+
+  var analyser = new AnalyserNode(context);
+  is(analyser.channelCount, 1, "analyser node has 1 input channels by default");
+  is(analyser.channelCountMode, "max", "Correct channelCountMode for the analyser node");
+  is(analyser.channelInterpretation, "speakers", "Correct channelCountInterpretation for the analyser node");
+
+  is(analyser.fftSize, 2048, "Correct default value for fftSize");
+  is(analyser.frequencyBinCount, 1024, "Correct default value for frequencyBinCount");
+  is(analyser.minDecibels, -100, "Correct default value for minDecibels");
+  is(analyser.maxDecibels, -30, "Correct default value for maxDecibels");
+  ok(Math.abs(analyser.smoothingTimeConstant - 0.8) < 0.001, "Correct default value for smoothingTimeConstant");
+
+  expectException(function() {
+    analyser = new AnalyserNode(context, { fftSize: 0 });
+  }, DOMException.INDEX_SIZE_ERR);
+  expectException(function() {
+    analyser = new AnalyserNode(context, { fftSize: 1 });
+  }, DOMException.INDEX_SIZE_ERR);
+  expectException(function() {
+    analyser = new AnalyserNode(context, { fftSize: 8 });
+  }, DOMException.INDEX_SIZE_ERR);
+  expectException(function() {
+    analyser = new AnalyserNode(context, { fftSize: 100 }); // non-power of two
+  }, DOMException.INDEX_SIZE_ERR);
+  expectException(function() {
+    analyser = new AnalyserNode(context, { fftSize: 2049 });
+  }, DOMException.INDEX_SIZE_ERR);
+  expectException(function() {
+    analyser = new AnalyserNode(context, { fftSize: 4097 });
+  }, DOMException.INDEX_SIZE_ERR);
+  expectException(function() {
+    analyser = new AnalyserNode(context, { fftSize: 8193 });
+  }, DOMException.INDEX_SIZE_ERR);
+  expectException(function() {
+    analyser = new AnalyserNode(context, { fftSize: 16385 });
+  }, DOMException.INDEX_SIZE_ERR);
+  expectException(function() {
+    analyser = new AnalyserNode(context, { fftSize: 32769 });
+  }, DOMException.INDEX_SIZE_ERR);
+  expectException(function() {
+    analyser = new AnalyserNode(context, { fftSize: 65536 });
+  }, DOMException.INDEX_SIZE_ERR);
+  analyser = new AnalyserNode(context, { fftSize: 1024 });
+  is(analyser.frequencyBinCount, 512, "Correct new value for frequencyBinCount");
+
+  expectException(function() {
+    analyser = new AnalyserNode(context, { minDecibels: -30 });
+  }, DOMException.INDEX_SIZE_ERR);
+  expectException(function() {
+    analyser = new AnalyserNode(context, { minDecibels: -29 });
+  }, DOMException.INDEX_SIZE_ERR);
+  expectException(function() {
+    analyser = new AnalyserNode(context, { maxDecibels: -100 });
+  }, DOMException.INDEX_SIZE_ERR);
+  expectException(function() {
+    analyser = new AnalyserNode(context, { maxDecibels: -101 });
+  }, DOMException.INDEX_SIZE_ERR);
+
+  expectException(function() {
+    analyser = new AnalyserNode(context, { smoothingTimeConstant: -0.1 });
+  }, DOMException.INDEX_SIZE_ERR);
+  expectException(function() {
+    analyser = new AnalyserNode(context, { smoothingTimeConstant: -1.1 });
+  }, DOMException.INDEX_SIZE_ERR);
+  analyser = new AnalyserNode(context, { smoothingTimeConstant: 0 });
+  analyser = new AnalyserNode(context, { smoothingTimeConstant: 1 });
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(function() {
+
+  testNode();
+  testConstructor();
 
   SimpleTest.finish();
 });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/webaudio/test/test_audioBufferSourceNodeLoopStartEnd.html
+++ b/dom/media/webaudio/test/test_audioBufferSourceNodeLoopStartEnd.html
@@ -14,22 +14,18 @@ var gTest = {
   length: 2048 * 4,
   numberOfChannels: 1,
   createGraph: function(context) {
     var buffer = context.createBuffer(1, 2048, context.sampleRate);
     for (var i = 0; i < 2048; ++i) {
       buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
     }
 
-    var source = context.createBufferSource();
-    source.buffer = buffer;
+    var source = new AudioBufferSourceNode(context, {buffer: buffer, loop: true, loopStart: buffer.duration * 0.25, loopEnd: buffer.duration * 0.75 });
     source.start(0);
-    source.loop = true;
-    source.loopStart = buffer.duration * 0.25;
-    source.loopEnd = buffer.duration * 0.75;
     return source;
   },
   createExpectedBuffers: function(context) {
     var expectedBuffer = context.createBuffer(1, 2048 * 4, context.sampleRate);
     for (var i = 0; i < 1536; ++i) {
       expectedBuffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
     }
     for (var i = 0; i < 6; ++i) {
--- a/dom/media/webaudio/test/test_channelMergerNode.html
+++ b/dom/media/webaudio/test/test_channelMergerNode.html
@@ -19,17 +19,17 @@ var gTest = {
       var buffer = context.createBuffer(2, 2048, context.sampleRate);
       for (var i = 0; i < 2048; ++i) {
         buffer.getChannelData(0)[i] = Math.sin(440 * 2 * (j + 1) * Math.PI * i / context.sampleRate);
         // Second channel is silent
       }
       buffers.push(buffer);
     }
 
-    var merger = context.createChannelMerger();
+    var merger = new ChannelMergerNode(context);
     is(merger.channelCount, 1, "merger node has 1 input channels");
     is(merger.channelCountMode, "explicit", "Correct channelCountMode for the merger node");
     is(merger.channelInterpretation, "speakers", "Correct channelCountInterpretation for the merger node");
 
     for (var i = 0; i < 6; ++i) {
       var source = context.createBufferSource();
       source.buffer = buffers[i];
       source.connect(merger, 0, i);
--- a/dom/media/webaudio/test/test_channelSplitterNode.html
+++ b/dom/media/webaudio/test/test_channelSplitterNode.html
@@ -24,17 +24,17 @@ addLoadEvent(function() {
     }
   }
   var emptyBuffer = context.createBuffer(1, 2048, context.sampleRate);
 
   var destination = context.destination;
 
   var source = context.createBufferSource();
 
-  var splitter = context.createChannelSplitter();
+  var splitter = new ChannelSplitterNode(context);
   is(splitter.channelCount, 2, "splitter node has 2 input channels by default");
   is(splitter.channelCountMode, "max", "Correct channelCountMode for the splitter node");
   is(splitter.channelInterpretation, "speakers", "Correct channelCountInterpretation for the splitter node");
 
   source.buffer = buffer;
   source.connect(splitter);
 
   var channelsSeen = 0;
--- a/dom/media/webaudio/test/test_convolverNode.html
+++ b/dom/media/webaudio/test/test_convolverNode.html
@@ -8,17 +8,17 @@
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 SimpleTest.waitForExplicitFinish();
 addLoadEvent(function() {
   var context = new AudioContext();
-  var conv = context.createConvolver();
+  var conv = new ConvolverNode(context);
 
   is(conv.channelCount, 2, "Convolver node has 2 input channels by default");
   is(conv.channelCountMode, "clamped-max", "Correct channelCountMode for the Convolver node");
   is(conv.channelInterpretation, "speakers", "Correct channelCountInterpretation for the Convolver node");
 
   is(conv.buffer, null, "Default buffer value");
   conv.buffer = context.createBuffer(2, 1024, 22050);
   is(conv.normalize, true, "Default normalize value");
--- a/dom/media/webaudio/test/test_delayNode.html
+++ b/dom/media/webaudio/test/test_delayNode.html
@@ -9,24 +9,37 @@
 <pre id="test">
 <script src="webaudio.js" type="text/javascript"></script>
 <script class="testbody" type="text/javascript">
 
 var gTest = {
   length: 4096,
   numberOfChannels: 1,
   createGraph: function(context) {
+    var delay = new DelayNode(context);
+    ok(delay.delayTime, "The audioparam member must exist");
+    is(delay.delayTime.value, 0, "Correct initial value");
+    is(delay.delayTime.defaultValue, 0, "Correct default value");
+    delay.delayTime.value = 0.5;
+    is(delay.delayTime.value, 0.5, "Correct initial value");
+    is(delay.delayTime.defaultValue, 0, "Correct default value");
+
+    delay = new DelayNode(context, { delayTime: 0.5 });
+    ok(delay.delayTime, "The audioparam member must exist");
+    is(delay.delayTime.value, 0.5, "Correct initial value");
+    is(delay.delayTime.defaultValue, 0, "Correct default value");
+
     var buffer = context.createBuffer(1, 2048, context.sampleRate);
     for (var i = 0; i < 2048; ++i) {
       buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
     }
 
     var source = context.createBufferSource();
 
-    var delay = context.createDelay();
+    delay = context.createDelay();
 
     source.buffer = buffer;
 
     source.connect(delay);
 
     ok(delay.delayTime, "The audioparam member must exist");
     is(delay.delayTime.value, 0, "Correct initial value");
     is(delay.delayTime.defaultValue, 0, "Correct default value");
@@ -44,16 +57,30 @@ var gTest = {
       context.createDelay(180);
     }, DOMException.NOT_SUPPORTED_ERR);
     expectTypeError(function() {
       context.createDelay(NaN);
     }, DOMException.NOT_SUPPORTED_ERR);
     expectException(function() {
       context.createDelay(-1);
     }, DOMException.NOT_SUPPORTED_ERR);
+
+    expectException(function() {
+      new DelayNode(context, { maxDelayTime: 0 });
+    }, DOMException.NOT_SUPPORTED_ERR);
+    expectException(function() {
+      new DelayNode(context, { maxDelayTime: 180 });
+    }, DOMException.NOT_SUPPORTED_ERR);
+    expectTypeError(function() {
+      new DelayNode(context, { maxDelayTime: NaN });
+    }, DOMException.NOT_SUPPORTED_ERR);
+    expectException(function() {
+      new DelayNode(context, { maxDelayTime: -1 });
+    }, DOMException.NOT_SUPPORTED_ERR);
+
     context.createDelay(1); // should not throw
 
     // Delay the source stream by 2048 frames
     delay.delayTime.value = 2048 / context.sampleRate;
 
     source.start(0);
     return delay;
   },
--- a/dom/media/webaudio/test/test_dynamicsCompressorNode.html
+++ b/dom/media/webaudio/test/test_dynamicsCompressorNode.html
@@ -15,17 +15,17 @@ function near(a, b, msg) {
 
 SimpleTest.waitForExplicitFinish();
 addLoadEvent(function() {
   var context = new AudioContext();
 
   var osc = context.createOscillator();
   var sp = context.createScriptProcessor();
 
-  var compressor = context.createDynamicsCompressor();
+  var compressor = new DynamicsCompressorNode(context);
 
   osc.connect(compressor);
   osc.connect(sp);
   compressor.connect(context.destination);
 
   is(compressor.channelCount, 2, "compressor node has 2 input channels by default");
   is(compressor.channelCountMode, "explicit", "Correct channelCountMode for the compressor node");
   is(compressor.channelInterpretation, "speakers", "Correct channelCountInterpretation for the compressor node");
--- a/dom/media/webaudio/test/test_gainNode.html
+++ b/dom/media/webaudio/test/test_gainNode.html
@@ -9,27 +9,42 @@
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 var gTest = {
   length: 2048,
   numberOfChannels: 1,
   createGraph: function(context) {
+
+    var gain = new GainNode(context);
+    ok(gain.gain, "The audioparam member must exist");
+    is(gain.gain.value, 1.0, "Correct initial value");
+    is(gain.gain.defaultValue, 1.0, "Correct default value");
+    gain.gain.value = 0.5;
+    is(gain.gain.value, 0.5, "Correct initial value");
+    is(gain.gain.defaultValue, 1.0, "Correct default value");
+
+    gain = new GainNode(context, { gain: 0.5 });
+    ok(gain.gain, "The audioparam member must exist");
+    is(gain.gain.value, 0.5, "Correct initial value");
+    is(gain.gain.defaultValue, 1.0, "Correct default value");
+    gain.gain.value = 0.5;
+    is(gain.gain.value, 0.5, "Correct initial value");
+    is(gain.gain.defaultValue, 1.0, "Correct default value");
+
     var buffer = context.createBuffer(1, 2048, context.sampleRate);
     for (var i = 0; i < 2048; ++i) {
       buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
     }
 
     var source = context.createBufferSource();
-
-    var gain = context.createGain();
-
     source.buffer = buffer;
 
+    gain = context.createGain();
     source.connect(gain);
 
     ok(gain.gain, "The audioparam member must exist");
     is(gain.gain.value, 1.0, "Correct initial value");
     is(gain.gain.defaultValue, 1.0, "Correct default value");
     gain.gain.value = 0.5;
     is(gain.gain.value, 0.5, "Correct initial value");
     is(gain.gain.defaultValue, 1.0, "Correct default value");
--- a/dom/media/webaudio/test/test_mediaElementAudioSourceNode.html
+++ b/dom/media/webaudio/test/test_mediaElementAudioSourceNode.html
@@ -51,17 +51,17 @@ function test() {
       ok(true,
        "Check received enough nonzero samples (got " + nonzeroSampleCount + ", expected min " + expectedMinNonzeroSampleCount + ")");
       SimpleTest.finish();
       complete = true;
     }
   }
 
   audio.onloadedmetadata = function() {
-    var node = context.createMediaElementSource(audio);
+    var node = new MediaElementAudioSourceNode(context, { mediaElement: audio });
     var sp = context.createScriptProcessor(2048, 1);
     node.connect(sp);
     // Use a fuzz factor of 100 to account for samples that just happen to be zero
     expectedMinNonzeroSampleCount = Math.floor(audio.duration*context.sampleRate) - 100;
     expectedMaxNonzeroSampleCount = Math.floor(audio.duration*context.sampleRate) + 500;
     sp.onaudioprocess = processSamples;
   };
 }
--- a/dom/media/webaudio/test/test_mediaStreamAudioDestinationNode.html
+++ b/dom/media/webaudio/test/test_mediaStreamAudioDestinationNode.html
@@ -18,17 +18,17 @@ addLoadEvent(function() {
   var buffer = context.createBuffer(1, 2048, context.sampleRate);
   for (var i = 0; i < 2048; ++i) {
     buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
   }
 
   var source = context.createBufferSource();
   source.buffer = buffer;
 
-  var dest = context.createMediaStreamDestination();
+  var dest = new MediaStreamAudioDestinationNode(context);
   source.connect(dest);
 
   var elem = document.getElementById('audioelem');
   elem.srcObject = dest.stream;
   elem.onloadedmetadata = function() {
     ok(true, "got metadata event");
     setTimeout(function() {
       is(elem.played.length, 1, "should have a played interval");
--- a/dom/media/webaudio/test/test_mediaStreamAudioSourceNode.html
+++ b/dom/media/webaudio/test/test_mediaStreamAudioSourceNode.html
@@ -26,17 +26,17 @@ var gTest = {
   createGraph: function(context) {
     var sourceGraph = new AudioContext();
     var source = sourceGraph.createBufferSource();
     source.buffer = createBuffer(context);
     var dest = sourceGraph.createMediaStreamDestination();
     source.connect(dest);
     source.start(0);
 
-    var mediaStreamSource = context.createMediaStreamSource(dest.stream);
+    var mediaStreamSource = new MediaStreamAudioSourceNode(context, { mediaStream: dest.stream });
     // channelCount and channelCountMode should have no effect
     mediaStreamSource.channelCount = 1;
     mediaStreamSource.channelCountMode = "explicit";
     return mediaStreamSource;
   },
   createExpectedBuffers: function(context) {
     return createBuffer(context);
   },
--- a/dom/media/webaudio/test/test_oscillatorNode.html
+++ b/dom/media/webaudio/test/test_oscillatorNode.html
@@ -9,17 +9,17 @@
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 SimpleTest.waitForExplicitFinish();
 addLoadEvent(function() {
 
   var context = new AudioContext();
-  var osc = context.createOscillator();
+  var osc = new OscillatorNode(context);
 
   is(osc.channelCount, 2, "Oscillator node has 2 input channels by default");
   is(osc.channelCountMode, "max", "Correct channelCountMode for the Oscillator node");
   is(osc.channelInterpretation, "speakers", "Correct channelCountInterpretation for the Oscillator node");
   is(osc.type, "sine", "Correct default type");
   expectException(function() {
     osc.type = "custom";
   }, DOMException.INVALID_STATE_ERR);
--- a/dom/media/webaudio/test/test_pannerNode.html
+++ b/dom/media/webaudio/test/test_pannerNode.html
@@ -20,17 +20,17 @@ addLoadEvent(function() {
   for (var i = 0; i < 2048; ++i) {
     buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
   }
 
   var destination = context.destination;
 
   var source = context.createBufferSource();
 
-  var panner = context.createPanner();
+  var panner = new PannerNode(context);
 
   source.buffer = buffer;
 
   source.connect(panner);
   panner.connect(destination);
 
   // Verify default values
   is(panner.panningModel, "equalpower", "Correct default value for panning model");
--- a/dom/media/webaudio/test/test_stereoPannerNode.html
+++ b/dom/media/webaudio/test/test_stereoPannerNode.html
@@ -44,17 +44,17 @@ function applyStereoToStereoPanning(l, r
 }
 
 function applyMonoToStereoPanning(c, panning) {
   return [c * panning[0], c * panning[1]];
 }
 
 // Test the DOM interface
 var context = new OfflineAudioContext(1, 1, SR);
-var stereoPanner = context.createStereoPanner();
+var stereoPanner = new StereoPannerNode(context);
 ok(stereoPanner.pan, "The AudioParam member must exist");
 is(stereoPanner.pan.value, 0.0, "Correct initial value");
 is(stereoPanner.pan.defaultValue, 0.0, "Correct default value");
 is(stereoPanner.channelCount, 2, "StereoPannerNode node has 2 input channels by default");
 is(stereoPanner.channelCountMode, "clamped-max", "Correct channelCountMode for the StereoPannerNode");
 is(stereoPanner.channelInterpretation, "speakers", "Correct channelCountInterpretation for the StereoPannerNode");
 expectException(function() {
   stereoPanner.channelCount = 3;
@@ -65,53 +65,62 @@ expectException(function() {
 
 // A sine to be used to fill the buffers
 function sine(t) {
   return Math.sin(440 * 2 * Math.PI * t / context.sampleRate);
 }
 
 // A couple mono and stereo buffers: the StereoPannerNode equation is different
 // if the input is mono or stereo
-var stereoBuffer = context.createBuffer(2, BUF_SIZE, context.sampleRate);
-var monoBuffer = context.createBuffer(1, BUF_SIZE, context.sampleRate);
+var stereoBuffer = new AudioBuffer(context, { numberOfChannels: 2,
+                                              length: BUF_SIZE });
+var monoBuffer = new AudioBuffer(context, { numberOfChannels: 1,
+                                            length: BUF_SIZE,
+                                            sampleRate: context.sampleRate });
 for (var i = 0; i < BUF_SIZE; ++i) {
   monoBuffer.getChannelData(0)[i]   =
   stereoBuffer.getChannelData(0)[i] =
   stereoBuffer.getChannelData(1)[i] = sine(i);
 }
 
 // Expected test vectors
 function expectedBufferNoop(gain) {
   gain = gain || 1.0;
-  var expectedBuffer = context.createBuffer(2, BUF_SIZE, SR);
+  var expectedBuffer = new AudioBuffer(context, { numberOfChannels: 2,
+                                                  length: BUF_SIZE,
+                                                  sampleRate: SR });
   for (var i = 0; i < BUF_SIZE; i++) {
     expectedBuffer.getChannelData(0)[i] = gain * sine(i);
     expectedBuffer.getChannelData(1)[i] = gain * sine(i);
   }
   return expectedBuffer;
 }
 
 function expectedBufferForStereo(gain) {
   gain = gain || 1.0;
-  var expectedBuffer = context.createBuffer(2, BUF_SIZE, SR);
+  var expectedBuffer = new AudioBuffer(context, { numberOfChannels: 2,
+                                                  length: BUF_SIZE,
+                                                  sampleRate: SR });
   var gainPanning = gainForPanningStereoToStereo(PANNING);
   gainPanning[0] *= gain;
   gainPanning[1] *= gain;
   for (var i = 0; i < BUF_SIZE; i++) {
     var values = [ sine(i), sine(i) ];
     var processed = applyStereoToStereoPanning(values[0], values[1], gainPanning, PANNING);
     expectedBuffer.getChannelData(0)[i] = processed[0];
     expectedBuffer.getChannelData(1)[i] = processed[1];
   }
   return expectedBuffer;
 }
 
 function expectedBufferForMono(gain) {
   gain = gain || 1.0;
-  var expectedBuffer = context.createBuffer(2, BUF_SIZE, SR);
+  var expectedBuffer = new AudioBuffer(context, { numberOfChannels: 2,
+                                                  length: BUF_SIZE,
+                                                  sampleRate: SR });
   var gainPanning = gainForPanningMonoToStereo(PANNING);
   gainPanning[0] *= gain;
   gainPanning[1] *= gain;
   for (var i = 0; i < BUF_SIZE; i++) {
     var value = sine(i);
     var processed = applyMonoToStereoPanning(value, gainPanning);
     expectedBuffer.getChannelData(0)[i] = processed[0];
     expectedBuffer.getChannelData(1)[i] = processed[1];
--- a/dom/media/webaudio/test/test_waveShaper.html
+++ b/dom/media/webaudio/test/test_waveShaper.html
@@ -12,17 +12,17 @@
 
 var gTest = {
   length: 4096,
   numberOfChannels: 1,
   createGraph: function(context) {
     var source = context.createBufferSource();
     source.buffer = this.buffer;
 
-    var shaper = context.createWaveShaper();
+    var shaper = new WaveShaperNode(context);
     shaper.curve = this.curve;
 
     source.connect(shaper);
 
     source.start(0);
     return shaper;
   },
   createExpectedBuffers: function(context) {
--- a/dom/plugins/base/nsJSNPRuntime.cpp
+++ b/dom/plugins/base/nsJSNPRuntime.cpp
@@ -1741,17 +1741,17 @@ NPObjWrapper_ObjectMoved(JSObject *obj, 
   }
 
   // Calling PLDHashTable::Search() will not result in GC.
   JS::AutoSuppressGCAnalysis nogc;
 
   auto entry =
     static_cast<NPObjWrapperHashEntry*>(sNPObjWrappers->Search(npobj));
   MOZ_ASSERT(entry && entry->mJSObj);
-  MOZ_ASSERT(entry->mJSObj.unbarrieredGetPtr() == old);
+  MOZ_ASSERT(entry->mJSObj == old);
   entry->mJSObj = obj;
 }
 
 static bool
 NPObjWrapper_Call(JSContext *cx, unsigned argc, JS::Value *vp)
 {
   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
   JS::Rooted<JSObject*> obj(cx, &args.callee());
--- a/dom/plugins/test/mochitest/xulbrowser_plugin_visibility.xul
+++ b/dom/plugins/test/mochitest/xulbrowser_plugin_visibility.xul
@@ -5,18 +5,18 @@
 	orient="vertical">
 
   <tabbox id="tabbox" flex="1">
     <tabs>
       <tab id="tab1" label="Tab 1" />
       <tab id="tab2" label="Tab 2" />
     </tabs>
     <tabpanels flex="1">
-      <browser id="browser1" type="content-primary" flex="1" src="about:blank"/>
-      <browser id="browser2" type="content-primary" flex="1" src="about:blank"/>
+      <browser id="browser1" type="content" primary="true" flex="1" src="about:blank"/>
+      <browser id="browser2" type="content" primary="true" flex="1" src="about:blank"/>
     </tabpanels>
   </tabbox>
   <script type="application/javascript" src="plugin-utils.js"/>
   <script type="application/javascript"><![CDATA[
     const ok = window.opener.wrappedJSObject.ok;
     const is = window.opener.wrappedJSObject.is;
     const done = window.opener.wrappedJSObject.done;
     const SimpleTest = window.opener.wrappedJSObject.SimpleTest;
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/webcomponents/chrome.ini
@@ -0,0 +1,9 @@
+[DEFAULT]
+support-files =
+  dummy_page.html
+
+[test_custom_element_htmlconstructor_chrome.html]
+skip-if = os == 'android' # bug 1323645
+support-files =
+  htmlconstructor_autonomous_tests.js
+  htmlconstructor_builtin_tests.js
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/webcomponents/dummy_page.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<title>Dummy test page</title>
+<meta charset="utf-8"/>
+</head>
+<body>
+<p>Dummy test page</p>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/webcomponents/htmlconstructor_autonomous_tests.js
@@ -0,0 +1,81 @@
+promises.push(test_with_new_window((testWindow) => {
+  // Test calling the HTMLElement constructor.
+  (() => {
+    SimpleTest.doesThrow(() => {
+      testWindow.HTMLElement();
+    }, 'calling the HTMLElement constructor should throw a TypeError');
+  })();
+
+  // Test constructing a HTMLELement.
+  (() => {
+    SimpleTest.doesThrow(() => {
+      new testWindow.HTMLElement();
+    }, 'constructing a HTMLElement should throw a TypeError');
+  })();
+
+  // Test constructing a custom element with defining HTMLElement as entry.
+  (() => {
+    testWindow.customElements.define('x-defining-html-element',
+                                     testWindow.HTMLElement);
+    SimpleTest.doesThrow(() => {
+      new testWindow.HTMLElement();
+    }, 'constructing a custom element with defining HTMLElement as registry ' +
+       'entry should throw a TypeError');
+  })();
+
+  // Test calling a custom element constructor and constructing an autonomous
+  // custom element.
+  (() => {
+    let num_constructor_invocations = 0;
+    class X extends testWindow.HTMLElement {
+      constructor() {
+        super();
+        num_constructor_invocations++;
+      }
+    }
+    testWindow.customElements.define('x-element', X);
+    SimpleTest.doesThrow(() => {
+      X();
+    }, 'calling an autonomous custom element constructor should throw a TypeError');
+
+    let element = new X();
+    SimpleTest.is(Object.getPrototypeOf(Cu.waiveXrays(element)), X.prototype,
+                  'constructing an autonomous custom element; ' +
+                  'the element should be a registered constructor');
+    SimpleTest.is(element.localName, 'x-element',
+                  'constructing an autonomous custom element; ' +
+                  'the element tag name should be "x-element"');
+    SimpleTest.is(element.namespaceURI, 'http://www.w3.org/1999/xhtml',
+                  'constructing an autonomous custom element; ' +
+                  'the element should be in the HTML namespace');
+    SimpleTest.is(element.prefix, null,
+                  'constructing an autonomous custom element; ' +
+                  'the element name should not have a prefix');
+    SimpleTest.is(element.ownerDocument, testWindow.document,
+                  'constructing an autonomous custom element; ' +
+                  'the element should be owned by the registry\'s associated ' +
+                  'document');
+    SimpleTest.is(num_constructor_invocations, 1,
+                  'constructing an autonomous custom element; ' +
+                  'the constructor should have been invoked once');
+  })();
+
+  // Test if prototype is no an object.
+  (() => {
+    function ElementWithNonObjectPrototype() {
+      let o = Reflect.construct(testWindow.HTMLElement, [], new.target);
+      SimpleTest.is(Object.getPrototypeOf(Cu.waiveXrays(o)), window.HTMLElement.prototype,
+                    'constructing an autonomous custom element; ' +
+                    'if prototype is not object, fallback from NewTarget\'s realm');
+    }
+
+     // Prototype have to be an object during define(), otherwise define will
+     // throw an TypeError exception.
+    ElementWithNonObjectPrototype.prototype = {};
+    testWindow.customElements.define('x-non-object-prototype',
+                                     ElementWithNonObjectPrototype);
+
+    ElementWithNonObjectPrototype.prototype = "string";
+    new ElementWithNonObjectPrototype();
+  })();
+}));
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/webcomponents/htmlconstructor_builtin_tests.js
@@ -0,0 +1,247 @@
+[
+  // [TagName, InterfaceName]
+  ['a', 'Anchor'],
+  ['abbr', ''],
+  ['acronym', ''],
+  ['address', ''],
+  ['area', 'Area'],
+  ['article', ''],
+  ['aside', ''],
+  ['audio', 'Audio'],
+  ['b', ''],
+  ['base', 'Base'],
+  ['basefont', ''],
+  ['bdo', ''],
+  ['big', ''],
+  ['blockquote', 'Quote'],
+  ['body', 'Body'],
+  ['br', 'BR'],
+  ['button', 'Button'],
+  ['canvas', 'Canvas'],
+  ['caption', 'TableCaption'],
+  ['center', ''],
+  ['cite', ''],
+  ['code', ''],
+  ['col', 'TableCol'],
+  ['colgroup', 'TableCol'],
+  ['data', 'Data'],
+  ['datalist', 'DataList'],
+  ['dd', ''],
+  ['del', 'Mod'],
+  ['details', 'Details'],
+  ['dfn', ''],
+  ['dir', 'Directory'],
+  ['div', 'Div'],
+  ['dl', 'DList'],
+  ['dt', ''],
+  ['em', ''],
+  ['embed', 'Embed'],
+  ['fieldset', 'FieldSet'],
+  ['figcaption', ''],
+  ['figure', ''],
+  ['font', 'Font'],
+  ['footer', ''],
+  ['form', 'Form'],
+  ['frame', 'Frame'],
+  ['frameset', 'FrameSet'],
+  ['h1', 'Heading'],
+  ['h2', 'Heading'],
+  ['h3', 'Heading'],
+  ['h4', 'Heading'],
+  ['h5', 'Heading'],
+  ['h6', 'Heading'],
+  ['head', 'Head'],
+  ['header', ''],
+  ['hgroup', ''],
+  ['hr', 'HR'],
+  ['html', 'Html'],
+  ['i', ''],
+  ['iframe', 'IFrame'],
+  ['image', ''],
+  ['img', 'Image'],
+  ['input', 'Input'],
+  ['ins', 'Mod'],
+  ['kbd', ''],
+  ['label', 'Label'],
+  ['legend', 'Legend'],
+  ['li', 'LI'],
+  ['link', 'Link'],
+  ['listing', 'Pre'],
+  ['main', ''],
+  ['map', 'Map'],
+  ['mark', ''],
+  ['marquee', 'Div'],
+  ['menu', 'Menu'],
+  ['menuitem', 'MenuItem'],
+  ['meta', 'Meta'],
+  ['meter', 'Meter'],
+  ['nav', ''],
+  ['nobr', ''],
+  ['noembed', ''],
+  ['noframes', ''],
+  ['noscript', ''],
+  ['object', 'Object'],
+  ['ol', 'OList'],
+  ['optgroup', 'OptGroup'],
+  ['option', 'Option'],
+  ['output', 'Output'],
+  ['p', 'Paragraph'],
+  ['param', 'Param'],
+  ['picture', 'Picture'],
+  ['plaintext', ''],
+  ['pre', 'Pre'],
+  ['progress', 'Progress'],
+  ['q', 'Quote'],
+  ['rb', ''],
+  ['rp', ''],
+  ['rt', ''],
+  ['rtc', ''],
+  ['ruby', ''],
+  ['s', ''],
+  ['samp', ''],
+  ['script', 'Script'],
+  ['section', ''],
+  ['select', 'Select'],
+  ['small', ''],
+  ['source', 'Source'],
+  ['span', 'Span'],
+  ['strike', ''],
+  ['strong', ''],
+  ['style', 'Style'],
+  ['sub', ''],
+  ['summary', ''],
+  ['sup', ''],
+  ['table', 'Table'],
+  ['tbody', 'TableSection'],
+  ['td', 'TableCell'],
+  ['textarea', 'TextArea'],
+  ['tfoot', 'TableSection'],
+  ['th', 'TableCell'],
+  ['thead', 'TableSection'],
+  ['template', 'Template'],
+  ['time', 'Time'],
+  ['title', 'Title'],
+  ['tr', 'TableRow'],
+  ['track', 'Track'],
+  ['tt', ''],
+  ['u', ''],
+  ['ul', 'UList'],
+  ['var', ''],
+  ['video', 'Video'],
+  ['wbr', ''],
+  ['xmp', 'Pre'],
+].forEach((e) => {
+  let tagName = e[0];
+  let interfaceName = 'HTML' + e[1] + 'Element';
+  promises.push(test_with_new_window((testWindow) => {
+    // Use window from iframe to isolate the test.
+    // Test calling the HTML*Element constructor.
+    (() => {
+      SimpleTest.doesThrow(() => {
+        testWindow[interfaceName]();
+      }, 'calling the ' + interfaceName + ' constructor should throw a TypeError');
+    })();
+
+    // Test constructing a HTML*ELement.
+    (() => {
+      SimpleTest.doesThrow(() => {
+        new testWindow[interfaceName]();
+      }, 'constructing a ' + interfaceName + ' should throw a TypeError');
+    })();
+
+    // Test constructing a custom element with defining HTML*Element as entry.
+    (() => {
+      testWindow.customElements.define('x-defining-' + tagName,
+                                       testWindow[interfaceName]);
+      SimpleTest.doesThrow(() => {
+        new testWindow[interfaceName]();
+      }, 'constructing a custom element with defining ' + interfaceName +
+         ' as registry entry should throw a TypeError');
+    })();
+
+    // Since HTMLElement can be registered without specifying "extends", skip
+    // testing HTMLElement tags.
+    if (interfaceName !== "HTMLElement") {
+      // Test constructing a customized HTML*Element with defining a registry entry
+      // without specifying "extends".
+      (() => {
+        class X extends testWindow[interfaceName] {}
+        testWindow.customElements.define('x-defining-invalid-' + tagName, X);
+        SimpleTest.doesThrow(() => {
+          new X();
+        }, 'constructing a customized ' + interfaceName + ' with defining a ' +
+           'registry entry without specifying "extends" should throw a TypeError');
+      })();
+    }
+
+    // Test constructing a built-in custom element with defining a registry entry
+    // with incorrect "extends" information.
+    (() => {
+      class X extends testWindow[interfaceName] {}
+      testWindow.customElements.define('x-defining-incorrect-' + tagName, X,
+                                       { extends: tagName === 'img' ? 'p' : 'img' });
+      SimpleTest.doesThrow(() => {
+        new X();
+      }, 'constructing a customized ' + interfaceName + ' with defining a ' +
+         'registry entry with incorrect "extends" should throw a TypeError');
+    })();
+
+    // Test calling a custom element constructor and constructing a built-in
+    // custom element.
+    (() => {
+      let num_constructor_invocations = 0;
+      class X extends testWindow[interfaceName] {
+        constructor() {
+          super();
+          num_constructor_invocations++;
+        }
+      }
+      testWindow.customElements.define('x-' + tagName, X, { extends: tagName });
+      SimpleTest.doesThrow(() => {
+        X();
+      }, 'calling a customized ' + interfaceName + ' constructor should throw a TypeError');
+
+      let element = new X();
+
+      SimpleTest.is(Object.getPrototypeOf(Cu.waiveXrays(element)), X.prototype,
+                    'constructing a customized ' + interfaceName +
+                    '; the element should be a registered constructor');
+      SimpleTest.is(element.localName, tagName,
+                    'constructing a customized ' + interfaceName +
+                    '; the element tag name should be "' + tagName + '"');
+      SimpleTest.is(element.namespaceURI, 'http://www.w3.org/1999/xhtml',
+                    'constructing a customized ' + interfaceName +
+                    '; the element should be in the HTML namespace');
+      SimpleTest.is(element.prefix, null,
+                    'constructing a customized ' + interfaceName +
+                    '; the element name should not have a prefix');
+      SimpleTest.is(element.ownerDocument, testWindow.document,
+                    'constructing a customized ' + interfaceName +
+                    '; the element should be owned by the registry\'s associated ' +
+                    'document');
+      SimpleTest.is(num_constructor_invocations, 1,
+                    'constructing a customized ' + interfaceName +
+                    '; the constructor should have been invoked once');
+    })();
+
+    // Test if prototype is no an object.
+    (() => {
+      function ElementWithNonObjectPrototype() {
+        let o = Reflect.construct(testWindow[interfaceName], [], new.target);
+        SimpleTest.is(Object.getPrototypeOf(Cu.waiveXrays(o)), window[interfaceName].prototype,
+                      'constructing a customized ' + interfaceName +
+                      '; if prototype is not object, fallback from NewTarget\'s realm');
+      }
+
+      // Prototype have to be an object during define(), otherwise define will
+      // throw an TypeError exception.
+      ElementWithNonObjectPrototype.prototype = {};
+      testWindow.customElements.define('x-non-object-prototype-' + tagName,
+                                       ElementWithNonObjectPrototype,
+                                       { extends: tagName });
+
+      ElementWithNonObjectPrototype.prototype = "string";
+      new ElementWithNonObjectPrototype();
+    })();
+  }));
+});
--- a/dom/tests/mochitest/webcomponents/mochitest.ini
+++ b/dom/tests/mochitest/webcomponents/mochitest.ini
@@ -1,21 +1,27 @@
 [DEFAULT]
 support-files =
   inert_style.css
+  dummy_page.html
 
 [test_bug900724.html]
 [test_bug1017896.html]
 [test_bug1176757.html]
 [test_bug1276240.html]
 [test_content_element.html]
 [test_custom_element_adopt_callbacks.html]
 [test_custom_element_callback_innerhtml.html]
 [test_custom_element_clone_callbacks.html]
 [test_custom_element_clone_callbacks_extended.html]
+[test_custom_element_htmlconstructor.html]
+skip-if = os == 'android' # bug 1323645
+support-files =
+  htmlconstructor_autonomous_tests.js
+  htmlconstructor_builtin_tests.js
 [test_custom_element_import_node_created_callback.html]
 [test_custom_element_in_shadow.html]
 [test_custom_element_register_invalid_callbacks.html]
 [test_custom_element_get.html]
 [test_custom_element_when_defined.html]
 [test_nested_content_element.html]
 [test_dest_insertion_points.html]
 [test_dest_insertion_points_shadow.html]
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/webcomponents/test_custom_element_htmlconstructor.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1274159
+-->
+<head>
+  <title>Test HTMLConstructor for custom elements.</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1274159">Bug 1274159</a>
+<script type="text/javascript">
+function test_with_new_window(f) {
+  return new Promise((aResolve) => {
+    let iframe = document.createElement('iframe');
+    iframe.setAttribute('type', 'content');
+    iframe.setAttribute('src', 'dummy_page.html');
+    iframe.onload = function() {
+      f(iframe.contentWindow);
+      aResolve();
+    };
+    document.body.appendChild(iframe);
+  });
+}
+
+// Fake the Cu.waiveXrays, so that we can share the tests with mochitest chrome.
+var Cu = { waiveXrays: (obj) => obj };
+var promises = [];
+SimpleTest.waitForExplicitFinish();
+</script>
+<!-- Test cases for autonomous element -->
+<script type="text/javascript" src="htmlconstructor_autonomous_tests.js"></script>
+<!-- Test cases for customized built-in element -->
+<script type="text/javascript" src="htmlconstructor_builtin_tests.js"></script>
+<script type="text/javascript">
+Promise.all(promises).then(() => {
+  SimpleTest.finish();
+});
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/webcomponents/test_custom_element_htmlconstructor_chrome.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1274159
+-->
+<head>
+  <title>Test HTMLConstructor for custom elements.</title>
+  <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1274159">Bug 1274159</a>
+<script type="text/javascript">
+function test_with_new_window(f) {
+  return new Promise((aResolve) => {
+    let iframe = document.createElement('iframe');
+    iframe.setAttribute('type', 'content');
+    iframe.setAttribute('src', 'http://example.org/tests/dom/tests/mochitest/webcomponents/dummy_page.html');
+    iframe.onload = function() {
+      f(iframe.contentWindow);
+      aResolve();
+    };
+    document.body.appendChild(iframe);
+  });
+}
+
+var Cu = Components.utils;
+var promises = [];
+SimpleTest.waitForExplicitFinish();
+</script>
+<!-- Test cases for autonomous element -->
+<script type="text/javascript" src="htmlconstructor_autonomous_tests.js"></script>
+<!-- Test cases for customized built-in element -->
+<script type="text/javascript" src="htmlconstructor_builtin_tests.js"></script>
+<script type="text/javascript">
+Promise.all(promises).then(() => {
+  SimpleTest.finish();
+});
+</script>
+</body>
+</html>
--- a/dom/tests/moz.build
+++ b/dom/tests/moz.build
@@ -32,16 +32,17 @@ MOCHITEST_MANIFESTS += [
 
 MOCHITEST_CHROME_MANIFESTS += [
     'mochitest/beacon/chrome.ini',
     'mochitest/chrome/chrome.ini',
     'mochitest/general/chrome.ini',
     'mochitest/geolocation/chrome.ini',
     'mochitest/localstorage/chrome.ini',
     'mochitest/sessionstorage/chrome.ini',
+    'mochitest/webcomponents/chrome.ini',
     'mochitest/whatwg/chrome.ini',
 ]
 
 if CONFIG['MOZ_GAMEPAD']:
     MOCHITEST_MANIFESTS += [
         'mochitest/gamepad/mochitest.ini',
     ]
 
--- a/dom/webidl/AccessibleNode.webidl
+++ b/dom/webidl/AccessibleNode.webidl
@@ -5,9 +5,11 @@
  */
 
 [Pref="accessibility.AOM.enabled"]
 interface AccessibleNode {
   readonly attribute DOMString role;
   [Frozen, Cached, Pure]
   readonly attribute sequence<DOMString> states;
   readonly attribute Node? DOMNode;
+
+  boolean is(DOMString... states);
 };
--- a/dom/webidl/AnalyserNode.webidl
+++ b/dom/webidl/AnalyserNode.webidl
@@ -5,17 +5,25 @@
  *
  * The origin of this IDL file is
  * https://webaudio.github.io/web-audio-api/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
-[Pref="dom.webaudio.enabled"]
+dictionary AnalyserOptions : AudioNodeOptions {
+             unsigned long fftSize = 2048;
+             float         maxDecibels = -30;
+             float         minDecibels = -100;
+             float         smoothingTimeConstant = 0.8;
+};
+
+[Pref="dom.webaudio.enabled",
+ Constructor(AudioContext context, optional AnalyserOptions options)]
 interface AnalyserNode : AudioNode {
 
     // Real-time frequency-domain data
     void getFloatFrequencyData(Float32Array array);
     void getByteFrequencyData(Uint8Array array);
 
     // Real-time waveform data
     void getFloatTimeDomainData(Float32Array array);
@@ -33,9 +41,8 @@ interface AnalyserNode : AudioNode {
 
     [SetterThrows, Pure]
     attribute double smoothingTimeConstant;
 
 };
 
 // Mozilla extension
 AnalyserNode implements AudioNodePassThrough;
-
--- a/dom/webidl/AudioBuffer.webidl
+++ b/dom/webidl/AudioBuffer.webidl
@@ -5,17 +5,24 @@
  *
  * The origin of this IDL file is
  * https://webaudio.github.io/web-audio-api/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
-[Pref="dom.webaudio.enabled"]
+dictionary AudioBufferOptions {
+             unsigned long numberOfChannels = 1;
+    required unsigned long length;
+             float         sampleRate;
+};
+
+[Pref="dom.webaudio.enabled",
+ Constructor(AudioContext context, AudioBufferOptions options)]
 interface AudioBuffer {
 
     readonly attribute float sampleRate;
     readonly attribute unsigned long length;
 
     // in seconds 
     readonly attribute double duration;
 
@@ -24,9 +31,8 @@ interface AudioBuffer {
     [Throws]
     Float32Array getChannelData(unsigned long channel);
 
     [Throws]
     void copyFromChannel(Float32Array destination, long channelNumber, optional unsigned long startInChannel = 0);
     [Throws]
     void copyToChannel(Float32Array source, long channelNumber, optional unsigned long startInChannel = 0);
 };
-
--- a/dom/webidl/AudioBufferSourceNode.webidl
+++ b/dom/webidl/AudioBufferSourceNode.webidl
@@ -5,17 +5,27 @@
  *
  * The origin of this IDL file is
  * https://webaudio.github.io/web-audio-api/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
-[Pref="dom.webaudio.enabled"]
+dictionary AudioBufferSourceOptions {
+             AudioBuffer? buffer;
+             float        detune = 0;
+             boolean      loop = false;
+             double       loopEnd = 0;
+             double       loopStart = 0;
+             float        playbackRate = 1;
+};
+
+[Pref="dom.webaudio.enabled",
+ Constructor(AudioContext context, optional AudioBufferSourceOptions options)]
 interface AudioBufferSourceNode : AudioNode {
 
     attribute AudioBuffer? buffer;
 
     readonly attribute AudioParam playbackRate;
     readonly attribute AudioParam detune;
 
     attribute boolean loop;
--- a/dom/webidl/AudioNode.webidl
+++ b/dom/webidl/AudioNode.webidl
@@ -16,16 +16,22 @@ enum ChannelCountMode {
     "explicit"
 };
 
 enum ChannelInterpretation {
     "speakers",
     "discrete"
 };
 
+dictionary AudioNodeOptions {
+             unsigned long         channelCount;
+             ChannelCountMode      channelCountMode;
+             ChannelInterpretation channelInterpretation;
+};
+
 [Pref="dom.webaudio.enabled"]
 interface AudioNode : EventTarget {
 
     [Throws]
     AudioNode connect(AudioNode destination, optional unsigned long output = 0, optional unsigned long input = 0);
     [Throws]
     void connect(AudioParam destination, optional unsigned long output = 0);
     [Throws]
--- a/dom/webidl/BiquadFilterNode.webidl
+++ b/dom/webidl/BiquadFilterNode.webidl
@@ -16,17 +16,26 @@ enum BiquadFilterType {
   "bandpass",
   "lowshelf",
   "highshelf",
   "peaking",
   "notch",
   "allpass"
 };
 
-[Pref="dom.webaudio.enabled"]
+dictionary BiquadFilterOptions : AudioNodeOptions {
+             BiquadFilterType type = "lowpass";
+             float            Q = 1;
+             float            detune = 0;
+             float            frequency = 350;
+             float            gain = 0;
+};
+
+[Pref="dom.webaudio.enabled",
+ Constructor(AudioContext context, optional BiquadFilterOptions options)]
 interface BiquadFilterNode : AudioNode {
 
     attribute BiquadFilterType type;
     readonly attribute AudioParam frequency; // in Hertz
     readonly attribute AudioParam detune; // in Cents
     readonly attribute AudioParam Q; // Quality factor
     readonly attribute AudioParam gain; // in Decibels
 
--- a/dom/webidl/ChannelMergerNode.webidl
+++ b/dom/webidl/ChannelMergerNode.webidl
@@ -5,13 +5,16 @@
  *
  * The origin of this IDL file is
  * https://webaudio.github.io/web-audio-api/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
-[Pref="dom.webaudio.enabled"]
-interface ChannelMergerNode : AudioNode {
-
+dictionary ChannelMergerOptions : AudioNodeOptions {
+             unsigned long numberOfInputs = 6;
 };
 
+[Pref="dom.webaudio.enabled",
+ Constructor(AudioContext context, optional ChannelMergerOptions options)]
+interface ChannelMergerNode : AudioNode {
+};
--- a/dom/webidl/ChannelSplitterNode.webidl
+++ b/dom/webidl/ChannelSplitterNode.webidl
@@ -5,13 +5,18 @@
  *
  * The origin of this IDL file is
  * https://webaudio.github.io/web-audio-api/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
-[Pref="dom.webaudio.enabled"]
+dictionary ChannelSplitterOptions : AudioNodeOptions {
+             unsigned long numberOfOutputs = 6;
+};
+
+[Pref="dom.webaudio.enabled",
+ Constructor(AudioContext context, optional ChannelSplitterOptions options)]
 interface ChannelSplitterNode : AudioNode {
 
 };
 
--- a/dom/webidl/ConvolverNode.webidl
+++ b/dom/webidl/ConvolverNode.webidl
@@ -5,17 +5,23 @@
  *
  * The origin of this IDL file is
  * https://webaudio.github.io/web-audio-api/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
-[Pref="dom.webaudio.enabled"]
+dictionary ConvolverOptions : AudioNodeOptions {
+             AudioBuffer? buffer;
+             boolean      disableNormalization = false;
+};
+
+[Pref="dom.webaudio.enabled",
+ Constructor(AudioContext context, optional ConvolverOptions options)]
 interface ConvolverNode : AudioNode {
 
       [SetterThrows]
       attribute AudioBuffer? buffer;
       attribute boolean normalize;
 
 };
 
--- a/dom/webidl/DelayNode.webidl
+++ b/dom/webidl/DelayNode.webidl
@@ -5,17 +5,23 @@
  *
  * The origin of this IDL file is
  * https://webaudio.github.io/web-audio-api/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
-[Pref="dom.webaudio.enabled"]
+dictionary DelayOptions : AudioNodeOptions {
+             double maxDelayTime = 1;
+             double delayTime = 0;
+};
+
+[Pref="dom.webaudio.enabled",
+ Constructor(AudioContext context, optional DelayOptions options)]
 interface DelayNode : AudioNode {
 
     readonly attribute AudioParam delayTime;
 
 };
 
 // Mozilla extension
 DelayNode implements AudioNodePassThrough;
--- a/dom/webidl/DynamicsCompressorNode.webidl
+++ b/dom/webidl/DynamicsCompressorNode.webidl
@@ -5,17 +5,26 @@
  *
  * The origin of this IDL file is
  * https://webaudio.github.io/web-audio-api/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
-[Pref="dom.webaudio.enabled"]
+dictionary DynamicsCompressorOptions : AudioNodeOptions {
+             float attack = 0.003;
+             float knee = 30;
+             float ratio = 12;
+             float release = 0.25;
+             float threshold = -24;
+};
+
+[Pref="dom.webaudio.enabled",
+ Constructor(AudioContext context, optional DynamicsCompressorOptions options)]
 interface DynamicsCompressorNode : AudioNode {
 
     readonly attribute AudioParam threshold; // in Decibels
     readonly attribute AudioParam knee; // in Decibels
     readonly attribute AudioParam ratio; // unit-less
     readonly attribute float reduction; // in Decibels
     readonly attribute AudioParam attack; // in Seconds
     readonly attribute AudioParam release; // in Seconds
--- a/dom/webidl/GainNode.webidl
+++ b/dom/webidl/GainNode.webidl
@@ -5,17 +5,22 @@
  *
  * The origin of this IDL file is
  * https://webaudio.github.io/web-audio-api/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
-[Pref="dom.webaudio.enabled"]
+dictionary GainOptions : AudioNodeOptions {
+             float gain = 1.0;
+};
+
+[Pref="dom.webaudio.enabled",
+ Constructor(AudioContext context, optional GainOptions options)]
 interface GainNode : AudioNode {
 
     readonly attribute AudioParam gain;
 
 };
 
 // Mozilla extension
 GainNode implements AudioNodePassThrough;
--- a/dom/webidl/HTMLAnchorElement.webidl
+++ b/dom/webidl/HTMLAnchorElement.webidl
@@ -7,16 +7,17 @@
  * http://www.whatwg.org/specs/web-apps/current-work/#the-a-element
  * http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
 // http://www.whatwg.org/specs/web-apps/current-work/#the-a-element
+[HTMLConstructor]
 interface HTMLAnchorElement : HTMLElement {
            [SetterThrows]
            attribute DOMString target;
            [SetterThrows]
            attribute DOMString download;
            [SetterThrows]
            attribute DOMString ping;
            [SetterThrows]
--- a/dom/webidl/HTMLAreaElement.webidl
+++ b/dom/webidl/HTMLAreaElement.webidl
@@ -8,16 +8,17 @@
  * http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
  &
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
 // http://www.whatwg.org/specs/web-apps/current-work/#the-area-element
+[HTMLConstructor]
 interface HTMLAreaElement : HTMLElement {
            [SetterThrows]
            attribute DOMString alt;
            [SetterThrows]
            attribute DOMString coords;
            [SetterThrows]
            attribute DOMString shape;
            [SetterThrows]
--- a/dom/webidl/HTMLAudioElement.webidl
+++ b/dom/webidl/HTMLAudioElement.webidl
@@ -6,11 +6,11 @@
  * The origin of this IDL file is
  * http://www.whatwg.org/specs/web-apps/current-work/#the-audio-element
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
-[NamedConstructor=Audio(optional DOMString src)]
+[HTMLConstructor, NamedConstructor=Audio(optional DOMString src)]
 interface HTMLAudioElement : HTMLMediaElement {};
 
--- a/dom/webidl/HTMLBRElement.webidl
+++ b/dom/webidl/HTMLBRElement.webidl
@@ -8,16 +8,17 @@
  * http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
 // http://www.whatwg.org/specs/web-apps/current-work/#the-br-element
+[HTMLConstructor]
 interface HTMLBRElement : HTMLElement {};
 
 // http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
 partial interface HTMLBRElement {
              [SetterThrows]
              attribute DOMString clear;
 };
 
--- a/dom/webidl/HTMLBaseElement.webidl
+++ b/dom/webidl/HTMLBaseElement.webidl
@@ -7,15 +7,16 @@
  * http://www.whatwg.org/specs/web-apps/current-work/#the-base-element
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
 // http://www.whatwg.org/specs/web-apps/current-work/#the-base-element
+[HTMLConstructor]
 interface HTMLBaseElement : HTMLElement {
            [SetterThrows, Pure]
            attribute DOMString href;
            [SetterThrows, Pure]
            attribute DOMString target;
 };
 
--- a/dom/webidl/HTMLBodyElement.webidl
+++ b/dom/webidl/HTMLBodyElement.webidl
@@ -6,16 +6,17 @@
  * The origin of this IDL file is
  * http://www.whatwg.org/specs/web-apps/current-work/
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
+[HTMLConstructor]
 interface HTMLBodyElement : HTMLElement {
 };
 
 partial interface HTMLBodyElement {
   [TreatNullAs=EmptyString, SetterThrows] attribute DOMString text;
   [TreatNullAs=EmptyString, SetterThrows] attribute DOMString link;
   [TreatNullAs=EmptyString, SetterThrows] attribute DOMString vLink;
   [TreatNullAs=EmptyString, SetterThrows] attribute DOMString aLink;
--- a/dom/webidl/HTMLButtonElement.webidl
+++ b/dom/webidl/HTMLButtonElement.webidl
@@ -6,16 +6,17 @@
  * The origin of this IDL file is
  * http://www.whatwg.org/specs/web-apps/current-work/#the-button-element
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
 // http://www.whatwg.org/specs/web-apps/current-work/#the-button-element
+[HTMLConstructor]
 interface HTMLButtonElement : HTMLElement {
   [SetterThrows, Pure]
            attribute boolean autofocus;
   [SetterThrows, Pure]
            attribute boolean disabled;
   [Pure]
   readonly attribute HTMLFormElement? form;
   [SetterThrows, Pure]
--- a/dom/webidl/HTMLCanvasElement.webidl
+++ b/dom/webidl/HTMLCanvasElement.webidl
@@ -8,16 +8,17 @@
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
 interface nsISupports;
 interface Variant;
 
+[HTMLConstructor]
 interface HTMLCanvasElement : HTMLElement {
   [Pure, SetterThrows]
            attribute unsigned long width;
   [Pure, SetterThrows]
            attribute unsigned long height;
 
   [Throws]
   nsISupports? getContext(DOMString contextId, optional any contextOptions = null);
--- a/dom/webidl/HTMLDListElement.webidl
+++ b/dom/webidl/HTMLDListElement.webidl
@@ -8,16 +8,17 @@
  * http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
 // http://www.whatwg.org/specs/web-apps/current-work/#the-dl-element
+[HTMLConstructor]
 interface HTMLDListElement : HTMLElement {
 };
 
 // http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
 partial interface HTMLDListElement {
            [SetterThrows]
            attribute boolean compact;
 };
--- a/dom/webidl/HTMLDataElement.webidl
+++ b/dom/webidl/HTMLDataElement.webidl
@@ -2,12 +2,13 @@
 /* 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/.
  *
  * The origin of this IDL file is
  * http://www.whatwg.org/specs/web-apps/current-work/multipage/text-level-semantics.html#the-data-element
  */
 
+[HTMLConstructor]
 interface HTMLDataElement : HTMLElement {
            [SetterThrows]
            attribute DOMString value;
 };
--- a/dom/webidl/HTMLDataListElement.webidl
+++ b/dom/webidl/HTMLDataListElement.webidl
@@ -6,11 +6,12 @@
  * The origin of this IDL file is
  * http://www.whatwg.org/specs/web-apps/current-work/
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
+[HTMLConstructor]
 interface HTMLDataListElement : HTMLElement {
   readonly attribute HTMLCollection options;
 };
--- a/dom/webidl/HTMLDetailsElement.webidl
+++ b/dom/webidl/HTMLDetailsElement.webidl
@@ -6,12 +6,13 @@
  * The origin of this IDL file is
  * https://html.spec.whatwg.org/multipage/forms.html#the-details-element
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
+[HTMLConstructor]
 interface HTMLDetailsElement : HTMLElement {
   [SetterThrows]
   attribute boolean open;
 };
--- a/dom/webidl/HTMLDirectoryElement.webidl
+++ b/dom/webidl/HTMLDirectoryElement.webidl
@@ -7,13 +7,14 @@
  * http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
 // http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
+[HTMLConstructor]
 interface HTMLDirectoryElement : HTMLElement {
            [SetterThrows, Pure]
            attribute boolean compact;
 };
 
--- a/dom/webidl/HTMLDivElement.webidl
+++ b/dom/webidl/HTMLDivElement.webidl
@@ -6,14 +6,15 @@
  * The origin of this IDL file is
  * http://www.whatwg.org/specs/web-apps/current-work/
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
+[HTMLConstructor]
 interface HTMLDivElement : HTMLElement {};
 
 partial interface HTMLDivElement {
   [SetterThrows]
            attribute DOMString align;
 };
--- a/dom/webidl/HTMLElement.webidl
+++ b/dom/webidl/HTMLElement.webidl
@@ -7,16 +7,17 @@
  * http://www.whatwg.org/specs/web-apps/current-work/ and
  * http://dev.w3.org/csswg/cssom-view/
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
+[HTMLConstructor]
 interface HTMLElement : Element {
   // metadata attributes
            attribute DOMString title;
            attribute DOMString lang;
   //         attribute boolean translate;
   [SetterThrows, Pure]
            attribute DOMString dir;
   [Constant]
--- a/dom/webidl/HTMLEmbedElement.webidl
+++ b/dom/webidl/HTMLEmbedElement.webidl
@@ -8,17 +8,17 @@
  * http://www.whatwg.org/specs/web-apps/current-work/#HTMLEmbedElement-partial
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
 // http://www.whatwg.org/specs/web-apps/current-work/#the-embed-element
-[NeedResolve]
+[HTMLConstructor, NeedResolve]
 interface HTMLEmbedElement : HTMLElement {
   [Pure, SetterThrows]
            attribute DOMString src;
   [Pure, SetterThrows]
            attribute DOMString type;
   [Pure, SetterThrows]
            attribute DOMString width;
   [Pure, SetterThrows]
--- a/dom/webidl/HTMLFieldSetElement.webidl
+++ b/dom/webidl/HTMLFieldSetElement.webidl
@@ -6,16 +6,17 @@
  * The origin of this IDL file is
  * http://www.whatwg.org/specs/web-apps/current-work/#the-fieldset-element
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
+[HTMLConstructor]
 interface HTMLFieldSetElement : HTMLElement {
   [SetterThrows]
            attribute boolean disabled;
   readonly attribute HTMLFormElement? form;
   [SetterThrows]
            attribute DOMString name;
 
   readonly attribute DOMString type;
--- a/dom/webidl/HTMLFontElement.webidl
+++ b/dom/webidl/HTMLFontElement.webidl
@@ -6,13 +6,14 @@
  * The origin of this IDL file is
  * http://www.whatwg.org/specs/web-apps/current-work/
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
+[HTMLConstructor]
 interface HTMLFontElement : HTMLElement {
   [TreatNullAs=EmptyString, SetterThrows] attribute DOMString color;
   [SetterThrows]                          attribute DOMString face;
   [SetterThrows]                          attribute DOMString size;
 };
--- a/dom/webidl/HTMLFormElement.webidl
+++ b/dom/webidl/HTMLFormElement.webidl
@@ -6,17 +6,17 @@
  * The origin of this IDL file is
  * http://www.whatwg.org/specs/web-apps/current-work/#htmlformelement
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
-[OverrideBuiltins, LegacyUnenumerableNamedProperties]
+[OverrideBuiltins, LegacyUnenumerableNamedProperties, HTMLConstructor]
 interface HTMLFormElement : HTMLElement {
            [Pure, SetterThrows]
            attribute DOMString acceptCharset;
            [Pure, SetterThrows]
            attribute DOMString action;
            [Pure, SetterThrows]
            attribute DOMString autocomplete;
            [Pure, SetterThrows]
--- a/dom/webidl/HTMLFrameElement.webidl
+++ b/dom/webidl/HTMLFrameElement.webidl
@@ -6,16 +6,17 @@
  * The origin of this IDL file is
  * http://www.whatwg.org/specs/web-apps/current-work/#htmlframeelement
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
 // http://www.whatwg.org/specs/web-apps/current-work/#htmlframeelement
+[HTMLConstructor]
 interface HTMLFrameElement : HTMLElement {
            [SetterThrows]
            attribute DOMString name;
            [SetterThrows]
            attribute DOMString scrolling;
            [SetterThrows]
            attribute DOMString src;
            [SetterThrows]
--- a/dom/webidl/HTMLFrameSetElement.webidl
+++ b/dom/webidl/HTMLFrameSetElement.webidl
@@ -6,16 +6,17 @@
  * The origin of this IDL file is
  * http://www.whatwg.org/specs/web-apps/current-work/
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
+[HTMLConstructor]
 interface HTMLFrameSetElement : HTMLElement {
   [SetterThrows]
   attribute DOMString cols;
   [SetterThrows]
   attribute DOMString rows;
 };
 
 HTMLFrameSetElement implements WindowEventHandlers;
--- a/dom/webidl/HTMLHRElement.webidl
+++ b/dom/webidl/HTMLHRElement.webidl
@@ -7,16 +7,17 @@
  * http://www.whatwg.org/specs/web-apps/current-work/#the-hr-element
  * http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
 // http://www.whatwg.org/specs/web-apps/current-work/#the-hr-element
+[HTMLConstructor]
 interface HTMLHRElement : HTMLElement {
 };
 
 // http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
 partial interface HTMLHRElement {
            [SetterThrows]
            attribute DOMString align;
            [SetterThrows]
--- a/dom/webidl/HTMLHeadElement.webidl
+++ b/dom/webidl/HTMLHeadElement.webidl
@@ -7,10 +7,11 @@
  * http://www.whatwg.org/specs/web-apps/current-work/#the-head-element
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
 // http://www.whatwg.org/specs/web-apps/current-work/#the-head-element
+[HTMLConstructor]
 interface HTMLHeadElement : HTMLElement {};
 
--- a/dom/webidl/HTMLHeadingElement.webidl
+++ b/dom/webidl/HTMLHeadingElement.webidl
@@ -7,16 +7,17 @@
  * http://www.whatwg.org/specs/web-apps/current-work/#the-h1,-h2,-h3,-h4,-h5,-and-h6-elements
  * http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
 // http://www.whatwg.org/specs/web-apps/current-work/#the-h1,-h2,-h3,-h4,-h5,-and-h6-elements
+[HTMLConstructor]
 interface HTMLHeadingElement : HTMLElement {
 };
 
 // http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
 partial interface HTMLHeadingElement {
            [SetterThrows]
            attribute DOMString align;
 };
--- a/dom/webidl/HTMLHtmlElement.webidl
+++ b/dom/webidl/HTMLHtmlElement.webidl
@@ -8,15 +8,16 @@
  * http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
 // http://www.whatwg.org/specs/web-apps/current-work/#the-html-element
+[HTMLConstructor]
 interface HTMLHtmlElement : HTMLElement {};
 
 // http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
 partial interface HTMLHtmlElement {
            [SetterThrows, Pure]
            attribute DOMString version;
 };
--- a/dom/webidl/HTMLIFrameElement.webidl
+++ b/dom/webidl/HTMLIFrameElement.webidl
@@ -6,16 +6,17 @@
  * The origin of this IDL file is
  * http://www.whatwg.org/specs/web-apps/current-work/#the-iframe-element
  * http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
+[HTMLConstructor]
 interface HTMLIFrameElement : HTMLElement {
   [SetterThrows, Pure]
            attribute DOMString src;
   [SetterThrows, Pure]
            attribute DOMString srcdoc;
   [SetterThrows, Pure]
            attribute DOMString name;
   [PutForwards=value] readonly attribute DOMTokenList sandbox;
--- a/dom/webidl/HTMLImageElement.webidl
+++ b/dom/webidl/HTMLImageElement.webidl
@@ -11,17 +11,18 @@
  * and create derivative works of this document.
  */
 
 interface imgINotificationObserver;
 interface imgIRequest;
 interface URI;
 interface nsIStreamListener;
 
-[NamedConstructor=Image(optional unsigned long width, optional unsigned long height)]
+[HTMLConstructor,
+ NamedConstructor=Image(optional unsigned long width, optional unsigned long height)]
 interface HTMLImageElement : HTMLElement {
            [SetterThrows]
            attribute DOMString alt;
            [SetterThrows]
            attribute DOMString src;
            [SetterThrows]
            attribute DOMString srcset;
            [SetterThrows]
--- a/dom/webidl/HTMLInputElement.webidl
+++ b/dom/webidl/HTMLInputElement.webidl
@@ -16,16 +16,17 @@ enum SelectionMode {
   "select",
   "start",
   "end",
   "preserve",
 };
 
 interface nsIControllers;
 
+[HTMLConstructor]
 interface HTMLInputElement : HTMLElement {
   [Pure, SetterThrows]
            attribute DOMString accept;
   [Pure, SetterThrows]
            attribute DOMString alt;
   [Pure, SetterThrows]
            attribute DOMString autocomplete;
   [Pure, SetterThrows]
--- a/dom/webidl/HTMLLIElement.webidl
+++ b/dom/webidl/HTMLLIElement.webidl
@@ -8,16 +8,17 @@
  * http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
 // http://www.whatwg.org/specs/web-apps/current-work/#the-li-element
+[HTMLConstructor]
 interface HTMLLIElement : HTMLElement {
            [SetterThrows, Pure]
            attribute long value;
 };
 
 // http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
 partial interface HTMLLIElement {
            [SetterThrows, Pure]
--- a/dom/webidl/HTMLLabelElement.webidl
+++ b/dom/webidl/HTMLLabelElement.webidl
@@ -6,13 +6,14 @@
  * The origin of this IDL file is
  * http://www.whatwg.org/specs/web-apps/current-work/
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
+[HTMLConstructor]
 interface HTMLLabelElement : HTMLElement {
   readonly attribute HTMLFormElement? form;
            attribute DOMString htmlFor;
   readonly attribute HTMLElement? control;
 };
--- a/dom/webidl/HTMLLegendElement.webidl
+++ b/dom/webidl/HTMLLegendElement.webidl
@@ -8,16 +8,17 @@
  * http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
 // http://www.whatwg.org/specs/web-apps/current-work/#the-legend-element
+[HTMLConstructor]
 interface HTMLLegendElement : HTMLElement {
   readonly attribute HTMLFormElement? form;
 };
 
 // http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
 partial interface HTMLLegendElement {
            [SetterThrows]
            attribute DOMString align;
--- a/dom/webidl/HTMLLinkElement.webidl
+++ b/dom/webidl/HTMLLinkElement.webidl
@@ -7,16 +7,17 @@
  * http://www.whatwg.org/specs/web-apps/current-work/#the-link-element
  * http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
 // http://www.whatwg.org/specs/web-apps/current-work/#the-link-element
+[HTMLConstructor]
 interface HTMLLinkElement : HTMLElement {
   [Pure]
            attribute boolean disabled;
   [SetterThrows, Pure]
            attribute DOMString href;
   [SetterThrows, Pure]
            attribute DOMString? crossOrigin;
   [SetterThrows, Pure]
--- a/dom/webidl/HTMLMapElement.webidl
+++ b/dom/webidl/HTMLMapElement.webidl
@@ -6,16 +6,17 @@
  * The origin of this IDL file is
  * http://www.whatwg.org/specs/web-apps/current-work/#the-map-element
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
 // http://www.whatwg.org/specs/web-apps/current-work/#the-map-element
+[HTMLConstructor]
 interface HTMLMapElement : HTMLElement {
   [SetterThrows, Pure]
            attribute DOMString name;
   [Constant]
   readonly attribute HTMLCollection areas;
   // Not supported yet.
   //readonly attribute HTMLCollection images;
 };
--- a/dom/webidl/HTMLMenuElement.webidl
+++ b/dom/webidl/HTMLMenuElement.webidl
@@ -10,16 +10,17 @@
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
 interface MenuBuilder;
 
 // http://www.whatwg.org/specs/web-apps/current-work/#the-menu-element
+[HTMLConstructor]
 interface HTMLMenuElement : HTMLElement {
            [SetterThrows]
            attribute DOMString type;
            [SetterThrows]
            attribute DOMString label;
 };
 
 // http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
--- a/dom/webidl/HTMLMenuItemElement.webidl
+++ b/dom/webidl/HTMLMenuItemElement.webidl
@@ -7,16 +7,17 @@
  * http://www.whatwg.org/specs/web-apps/current-work/#the-menuitem-element
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
 // http://www.whatwg.org/specs/web-apps/current-work/#the-menuitem-element
+[HTMLConstructor]
 interface HTMLMenuItemElement : HTMLElement {
            [SetterThrows]
            attribute DOMString type;
            [SetterThrows]
            attribute DOMString label;
            [SetterThrows]
            attribute DOMString icon;
            [SetterThrows]
--- a/dom/webidl/HTMLMetaElement.webidl
+++ b/dom/webidl/HTMLMetaElement.webidl
@@ -7,16 +7,17 @@
  * http://www.whatwg.org/specs/web-apps/current-work/#the-meta-element
  * http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
 // http://www.whatwg.org/specs/web-apps/current-work/#the-meta-element
+[HTMLConstructor]
 interface HTMLMetaElement : HTMLElement {
   [SetterThrows, Pure]
            attribute DOMString name;
   [SetterThrows, Pure]
            attribute DOMString httpEquiv;
   [SetterThrows, Pure]
            attribute DOMString content;
 };
--- a/dom/webidl/HTMLMeterElement.webidl
+++ b/dom/webidl/HTMLMeterElement.webidl
@@ -7,16 +7,17 @@
  * http://www.whatwg.org/specs/web-apps/current-work/#the-meter-element	
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
 // http://www.whatwg.org/specs/web-apps/current-work/#the-meter-element
+[HTMLConstructor]
 interface HTMLMeterElement : HTMLElement {
            [SetterThrows]
            attribute double value;
            [SetterThrows]
            attribute double min;
            [SetterThrows]
            attribute double max;
            [SetterThrows]
--- a/dom/webidl/HTMLModElement.webidl
+++ b/dom/webidl/HTMLModElement.webidl
@@ -6,14 +6,15 @@
  * The origin of this IDL file is
  * http://www.whatwg.org/specs/web-apps/current-work/#attributes-common-to-ins-and-del-elements
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
 // http://www.whatwg.org/specs/web-apps/current-work/#attributes-common-to-ins-and-del-elements
+[HTMLConstructor]
 interface HTMLModElement : HTMLElement {
   [SetterThrows, Pure]
            attribute DOMString cite;
   [SetterThrows, Pure]
            attribute DOMString dateTime;
 };
--- a/dom/webidl/HTMLOListElement.webidl
+++ b/dom/webidl/HTMLOListElement.webidl
@@ -8,16 +8,17 @@
  * http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
 // http://www.whatwg.org/specs/web-apps/current-work/#the-ol-element
+[HTMLConstructor]
 interface HTMLOListElement : HTMLElement {
            [SetterThrows]
            attribute boolean reversed;
            [SetterThrows]
            attribute long start;
            [SetterThrows]
            attribute DOMString type;
 };
--- a/dom/webidl/HTMLObjectElement.webidl
+++ b/dom/webidl/HTMLObjectElement.webidl
@@ -8,17 +8,17 @@
  * http://www.whatwg.org/specs/web-apps/current-work/#HTMLObjectElement-partial
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
 // http://www.whatwg.org/specs/web-apps/current-work/#the-object-element
-[NeedResolve, UnsafeInPrerendering]
+[HTMLConstructor, NeedResolve, UnsafeInPrerendering]
 interface HTMLObjectElement : HTMLElement {
   [Pure, SetterThrows]
            attribute DOMString data;
   [Pure, SetterThrows]
            attribute DOMString type;
   [Pure, SetterThrows]
            attribute boolean typeMustMatch;
   [Pure, SetterThrows]
--- a/dom/webidl/HTMLOptGroupElement.webidl
+++ b/dom/webidl/HTMLOptGroupElement.webidl
@@ -6,14 +6,15 @@
  * The origin of this IDL file is
  * http://www.whatwg.org/specs/web-apps/current-work/#the-optgroup-element
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
+[HTMLConstructor]
 interface HTMLOptGroupElement : HTMLElement {
            [SetterThrows]
            attribute boolean disabled;
            [SetterThrows]
            attribute DOMString label;
 };
--- a/dom/webidl/HTMLOptionElement.webidl
+++ b/dom/webidl/HTMLOptionElement.webidl
@@ -6,17 +6,17 @@
  * The origin of this IDL file is
  * http://www.whatwg.org/specs/web-apps/current-work/#the-option-element
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
-[NamedConstructor=Option(optional DOMString text, optional DOMString value, optional boolean defaultSelected, optional boolean selected)]
+[HTMLConstructor, NamedConstructor=Option(optional DOMString text, optional DOMString value, optional boolean defaultSelected, optional boolean selected)]
 interface HTMLOptionElement : HTMLElement {
            [SetterThrows]
            attribute boolean disabled;
   readonly attribute HTMLFormElement? form;
            [SetterThrows]
            attribute DOMString label;
            [SetterThrows]
            attribute boolean defaultSelected;
--- a/dom/webidl/HTMLOutputElement.webidl
+++ b/dom/webidl/HTMLOutputElement.webidl
@@ -7,16 +7,17 @@
  * http://www.whatwg.org/specs/web-apps/current-work/#the-output-element
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
 // http://www.whatwg.org/specs/web-apps/current-work/#the-output-element
+[HTMLConstructor]
 interface HTMLOutputElement : HTMLElement {
   [PutForwards=value, Constant]
   readonly attribute DOMTokenList htmlFor;
   readonly attribute HTMLFormElement? form;
   [SetterThrows, Pure]
            attribute DOMString name;
 
   [Constant]
--- a/dom/webidl/HTMLParagraphElement.webidl
+++ b/dom/webidl/HTMLParagraphElement.webidl
@@ -7,16 +7,17 @@
  * http://www.whatwg.org/specs/web-apps/current-work/#the-p-element
  * http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
 // http://www.whatwg.org/specs/web-apps/current-work/#the-p-element
+[HTMLConstructor]
 interface HTMLParagraphElement : HTMLElement {
 };
 
 // http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
 partial interface HTMLParagraphElement {
            [SetterThrows]
            attribute DOMString align;
 };
--- a/dom/webidl/HTMLParamElement.webidl
+++ b/dom/webidl/HTMLParamElement.webidl
@@ -8,16 +8,17 @@
  * http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
 // http://www.whatwg.org/specs/web-apps/current-work/#the-param-element
+[HTMLConstructor]
 interface HTMLParamElement : HTMLElement {
            [SetterThrows, Pure]
            attribute DOMString name;
            [SetterThrows, Pure]
            attribute DOMString value;
 };
 
 // http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
--- a/dom/webidl/HTMLPictureElement.webidl
+++ b/dom/webidl/HTMLPictureElement.webidl
@@ -1,8 +1,9 @@
 /* -*- 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/.
  */
 
+[HTMLConstructor]
 interface HTMLPictureElement : HTMLElement {
 };
--- a/dom/webidl/HTMLPreElement.webidl
+++ b/dom/webidl/HTMLPreElement.webidl
@@ -7,16 +7,17 @@
  * http://www.whatwg.org/specs/web-apps/current-work/#the-pre-element
  * http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
 // http://www.whatwg.org/specs/web-apps/current-work/#the-pre-element
+[HTMLConstructor]
 interface HTMLPreElement : HTMLElement {
 };
 
 // http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
 partial interface HTMLPreElement {
            [SetterThrows]
            attribute long width;
 };
--- a/dom/webidl/HTMLProgressElement.webidl
+++ b/dom/webidl/HTMLProgressElement.webidl
@@ -6,16 +6,17 @@
  * The origin of this IDL file is
  * http://www.whatwg.org/specs/web-apps/current-work/#the-progress-element
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
+[HTMLConstructor]
 interface HTMLProgressElement : HTMLElement {
            [SetterThrows]
            attribute double value;
            [SetterThrows]
            attribute double max;
   readonly attribute double position;
 
   /**
--- a/dom/webidl/HTMLQuoteElement.webidl
+++ b/dom/webidl/HTMLQuoteElement.webidl
@@ -7,13 +7,14 @@
  * http://www.whatwg.org/specs/web-apps/current-work/#the-blockquote-element
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
 // http://www.whatwg.org/specs/web-apps/current-work/#the-blockquote-element
+[HTMLConstructor]
 interface HTMLQuoteElement : HTMLElement {
            [SetterThrows, Pure]
            attribute DOMString cite;
 };
 
--- a/dom/webidl/HTMLScriptElement.webidl
+++ b/dom/webidl/HTMLScriptElement.webidl
@@ -3,16 +3,17 @@
  * 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/.
  *
  * The origin of this IDL file is
  * http://www.whatwg.org/specs/web-apps/current-work/#the-script-element
  * http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
  */
 
+[HTMLConstructor]
 interface HTMLScriptElement : HTMLElement {
   [SetterThrows]
   attribute DOMString src;
   [SetterThrows]
   attribute DOMString type;
   [SetterThrows]
   attribute DOMString charset;
   [SetterThrows]
--- a/dom/webidl/HTMLSelectElement.webidl
+++ b/dom/webidl/HTMLSelectElement.webidl
@@ -2,16 +2,17 @@
 /* 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/.
  *
  * The origin of this IDL file is
  * http://www.whatwg.org/html/#the-select-element
  */
 
+[HTMLConstructor]
 interface HTMLSelectElement : HTMLElement {
   [SetterThrows, Pure]
            attribute boolean autofocus;
   [Pref="dom.forms.autocomplete.experimental", SetterThrows, Pure]
            attribute DOMString autocomplete;
   [SetterThrows, Pure]
            attribute boolean disabled;
   [Pure]
--- a/dom/webidl/HTMLSourceElement.webidl
+++ b/dom/webidl/HTMLSourceElement.webidl
@@ -6,16 +6,17 @@
  * The origin of this IDL file is
  * http://www.whatwg.org/specs/web-apps/current-work/#the-source-element
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
+[HTMLConstructor]
 interface HTMLSourceElement : HTMLElement {
            [SetterThrows]
            attribute DOMString src;
            [SetterThrows]
            attribute DOMString type;
 };
 
 partial interface HTMLSourceElement {
--- a/dom/webidl/HTMLSpanElement.webidl
+++ b/dom/webidl/HTMLSpanElement.webidl
@@ -7,9 +7,10 @@
  * http://www.whatwg.org/specs/web-apps/current-work/#the-span-element
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
 // http://www.whatwg.org/specs/web-apps/current-work/#the-span-element
+[HTMLConstructor]
 interface HTMLSpanElement : HTMLElement {};
--- a/dom/webidl/HTMLStyleElement.webidl
+++ b/dom/webidl/HTMLStyleElement.webidl
@@ -3,16 +3,17 @@
  * 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/.
  *
  * The origin of this IDL file is
  * http://www.whatwg.org/specs/web-apps/current-work/#the-style-element
  * http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
  */
 
+[HTMLConstructor]
 interface HTMLStyleElement : HTMLElement {
            [Pure]
            attribute boolean disabled;
            [SetterThrows, Pure]
            attribute DOMString media;
            [SetterThrows, Pure]
            attribute DOMString type;
            [SetterThrows, Pure]
--- a/dom/webidl/HTMLTableCaptionElement.webidl
+++ b/dom/webidl/HTMLTableCaptionElement.webidl
@@ -6,14 +6,15 @@
  * The origin of this IDL file is
  * http://www.whatwg.org/specs/web-apps/current-work/
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
+[HTMLConstructor]
 interface HTMLTableCaptionElement : HTMLElement {};
 
 partial interface HTMLTableCaptionElement {
            [SetterThrows]
            attribute DOMString align;
 };
--- a/dom/webidl/HTMLTableCellElement.webidl
+++ b/dom/webidl/HTMLTableCellElement.webidl
@@ -6,16 +6,17 @@
  * The origin of this IDL file is
  * http://www.whatwg.org/specs/web-apps/current-work/
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
+[HTMLConstructor]
 interface HTMLTableCellElement : HTMLElement {
            [SetterThrows]
            attribute unsigned long colSpan;
            [SetterThrows]
            attribute unsigned long rowSpan;
   //[PutForwards=value] readonly attribute DOMTokenList headers;
            [SetterThrows]
            attribute DOMString headers;
--- a/dom/webidl/HTMLTableColElement.webidl
+++ b/dom/webidl/HTMLTableColElement.webidl
@@ -6,16 +6,17 @@
  * The origin of this IDL file is
  * http://www.whatwg.org/specs/web-apps/current-work/
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
+[HTMLConstructor]
 interface HTMLTableColElement : HTMLElement {
            [SetterThrows]
            attribute unsigned long span;
 };
 
 partial interface HTMLTableColElement {
            [SetterThrows]
            attribute DOMString align;
--- a/dom/webidl/HTMLTableElement.webidl
+++ b/dom/webidl/HTMLTableElement.webidl
@@ -6,16 +6,17 @@
  * The origin of this IDL file is
  * http://www.whatwg.org/specs/web-apps/current-work/
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
+[HTMLConstructor]
 interface HTMLTableElement : HTMLElement {
            [SetterThrows]
            attribute HTMLTableCaptionElement? caption;
   HTMLElement createCaption();
   void deleteCaption();
            [SetterThrows]
            attribute HTMLTableSectionElement? tHead;
   HTMLElement createTHead();
--- a/dom/webidl/HTMLTableRowElement.webidl
+++ b/dom/webidl/HTMLTableRowElement.webidl
@@ -6,16 +6,17 @@
  * The origin of this IDL file is
  * http://www.whatwg.org/specs/web-apps/current-work/
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
+[HTMLConstructor]
 interface HTMLTableRowElement : HTMLElement {
   readonly attribute long rowIndex;
   readonly attribute long sectionRowIndex;
   readonly attribute HTMLCollection cells;
   [Throws]
   HTMLElement insertCell(optional long index = -1);
   [Throws]
   void deleteCell(long index);
--- a/dom/webidl/HTMLTableSectionElement.webidl
+++ b/dom/webidl/HTMLTableSectionElement.webidl
@@ -6,16 +6,17 @@
  * The origin of this IDL file is
  * http://www.whatwg.org/specs/web-apps/current-work/
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
+[HTMLConstructor]
 interface HTMLTableSectionElement : HTMLElement {
   readonly attribute HTMLCollection rows;
   [Throws]
   HTMLElement insertRow(optional long index = -1);
   [Throws]
   void deleteRow(long index);
 };
 
--- a/dom/webidl/HTMLTemplateElement.webidl
+++ b/dom/webidl/HTMLTemplateElement.webidl
@@ -4,12 +4,13 @@
  *
  * The origin of this IDL file is
  * https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/templates/index.html
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
+[HTMLConstructor]
 interface HTMLTemplateElement : HTMLElement {
     readonly attribute DocumentFragment content;
 };
 
--- a/dom/webidl/HTMLTextAreaElement.webidl
+++ b/dom/webidl/HTMLTextAreaElement.webidl
@@ -9,16 +9,17 @@
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
 interface nsIEditor;
 interface MozControllers;
 
+[HTMLConstructor]
 interface HTMLTextAreaElement : HTMLElement {
            // attribute DOMString autocomplete;
   [SetterThrows, Pure]
            attribute boolean autofocus;
   [SetterThrows, Pure]
            attribute unsigned long cols;
            // attribute DOMString dirName;
   [SetterThrows, Pure]
--- a/dom/webidl/HTMLTimeElement.webidl
+++ b/dom/webidl/HTMLTimeElement.webidl
@@ -2,12 +2,13 @@
 /* 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/.
  *
  * The origin of this IDL file is
  * http://www.whatwg.org/specs/web-apps/current-work/multipage/text-level-semantics.html#the-time-element
  */
 
+[HTMLConstructor]
 interface HTMLTimeElement : HTMLElement {
            [SetterThrows]
            attribute DOMString dateTime;
 };
--- a/dom/webidl/HTMLTitleElement.webidl
+++ b/dom/webidl/HTMLTitleElement.webidl
@@ -2,12 +2,13 @@
 /* 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/.
  *
  * The origin of this IDL file is
  * http://www.whatwg.org/specs/web-apps/current-work/#the-title-element
  */
 
+[HTMLConstructor]
 interface HTMLTitleElement : HTMLElement {
            [Throws]
            attribute DOMString text;
 };
--- a/dom/webidl/HTMLTrackElement.webidl
+++ b/dom/webidl/HTMLTrackElement.webidl
@@ -2,16 +2,17 @@
 /* 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/.
  *
  * The origin of this IDL file is
  * http://www.whatwg.org/specs/web-apps/current-work/#the-track-element
  */
 
+[HTMLConstructor]
 interface HTMLTrackElement : HTMLElement {
   [SetterThrows, Pure]
   attribute DOMString kind;
   [SetterThrows, Pure]
   attribute DOMString src;
   [SetterThrows, Pure]
   attribute DOMString srclang;
   [SetterThrows, Pure]
--- a/dom/webidl/HTMLUListElement.webidl
+++ b/dom/webidl/HTMLUListElement.webidl
@@ -8,16 +8,17 @@
  * http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
 // http://www.whatwg.org/specs/web-apps/current-work/#the-ul-element
+[HTMLConstructor]
 interface HTMLUListElement : HTMLElement {
 };
 
 // http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
 partial interface HTMLUListElement {
            [SetterThrows]
            attribute boolean compact;
            [SetterThrows]
--- a/dom/webidl/HTMLVideoElement.webidl
+++ b/dom/webidl/HTMLVideoElement.webidl
@@ -6,16 +6,17 @@
  * The origin of this IDL file is
  * http://www.whatwg.org/specs/web-apps/current-work/#the-video-element
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
+[HTMLConstructor]
 interface HTMLVideoElement : HTMLMediaElement {
   [SetterThrows]
            attribute unsigned long width;
   [SetterThrows]
            attribute unsigned long height;
   readonly attribute unsigned long videoWidth;
   readonly attribute unsigned long videoHeight;
   [SetterThrows]
--- a/dom/webidl/IIRFilterNode.webidl
+++ b/dom/webidl/IIRFilterNode.webidl
@@ -4,15 +4,21 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  *
  * The origin of this IDL file is https://www.w3.org/TR/webaudio
  *
  * Copyright © 2016 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
-[Pref="dom.webaudio.enabled"]
+dictionary IIRFilterOptions : AudioNodeOptions {
+    required sequence<double> feedforward;
+    required sequence<double> feedback;
+};
+
+[Pref="dom.webaudio.enabled",
+Constructor(AudioContext context, IIRFilterOptions options)]
 interface IIRFilterNode : AudioNode {
     void getFrequencyResponse(Float32Array frequencyHz, Float32Array magResponse, Float32Array phaseResponse);
 };
 
 // Mozilla extension
 IIRFilterNode implements AudioNodePassThrough;
--- a/dom/webidl/MediaElementAudioSourceNode.webidl
+++ b/dom/webidl/MediaElementAudioSourceNode.webidl
@@ -5,16 +5,21 @@
  *
  * The origin of this IDL file is
  * https://webaudio.github.io/web-audio-api/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
-[Pref="dom.webaudio.enabled"]
+dictionary MediaElementAudioSourceOptions {
+    required HTMLMediaElement mediaElement;
+};
+
+[Pref="dom.webaudio.enabled",
+ Constructor(AudioContext context, MediaElementAudioSourceOptions options)]
 interface MediaElementAudioSourceNode : AudioNode {
 
 };
 
 // Mozilla extensions
 MediaElementAudioSourceNode implements AudioNodePassThrough;
 
--- a/dom/webidl/MediaStreamAudioDestinationNode.webidl
+++ b/dom/webidl/MediaStreamAudioDestinationNode.webidl
@@ -5,14 +5,13 @@
  *
  * The origin of this IDL file is
  * https://webaudio.github.io/web-audio-api/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
-[Pref="dom.webaudio.enabled"]
+[Pref="dom.webaudio.enabled",
+ Constructor(AudioContext context, optional AudioNodeOptions options)]
 interface MediaStreamAudioDestinationNode : AudioNode {
-
     readonly attribute MediaStream stream;
-
 };
--- a/dom/webidl/MediaStreamAudioSourceNode.webidl
+++ b/dom/webidl/MediaStreamAudioSourceNode.webidl
@@ -5,16 +5,21 @@
  *
  * The origin of this IDL file is
  * https://webaudio.github.io/web-audio-api/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
-[Pref="dom.webaudio.enabled"]
+dictionary MediaStreamAudioSourceOptions {
+    required MediaStream mediaStream;
+};
+
+[Pref="dom.webaudio.enabled",
+ Constructor(AudioContext context, MediaStreamAudioSourceOptions options)]
 interface MediaStreamAudioSourceNode : AudioNode {
 
 };
 
 // Mozilla extensions
 MediaStreamAudioSourceNode implements AudioNodePassThrough;
 
--- a/dom/webidl/OscillatorNode.webidl
+++ b/dom/webidl/OscillatorNode.webidl
@@ -13,17 +13,25 @@
 enum OscillatorType {
   "sine",
   "square",
   "sawtooth",
   "triangle",
   "custom"
 };
 
-[Pref="dom.webaudio.enabled"]
+dictionary OscillatorOptions : AudioNodeOptions {
+             OscillatorType type = "sine";
+             float          frequency = 440;
+             float          detune = 0;
+             PeriodicWave   periodicWave;
+};
+
+[Pref="dom.webaudio.enabled",
+ Constructor(AudioContext context, optional OscillatorOptions options)]
 interface OscillatorNode : AudioNode {
 
     [SetterThrows]
     attribute OscillatorType type;
 
     readonly attribute AudioParam frequency; // in Hertz
     readonly attribute AudioParam detune; // in Cents
 
@@ -34,9 +42,8 @@ interface OscillatorNode : AudioNode {
     void setPeriodicWave(PeriodicWave periodicWave);
 
     attribute EventHandler onended;
 
 };
 
 // Mozilla extensions
 OscillatorNode implements AudioNodePassThrough;
-
--- a/dom/webidl/PannerNode.webidl
+++ b/dom/webidl/PannerNode.webidl
@@ -16,17 +16,35 @@ enum PanningModelType {
 };
 
 enum DistanceModelType {
   "linear",
   "inverse",
   "exponential"
 };
 
-[Pref="dom.webaudio.enabled"]
+dictionary PannerOptions : AudioNodeOptions {
+             PanningModelType  panningModel = "equalpower";
+             DistanceModelType distanceModel = "inverse";
+             float             positionX = 0;
+             float             positionY = 0;
+             float             positionZ = 0;
+             float             orientationX = 1;
+             float             orientationY = 0;
+             float             orientationZ = 0;
+             double            refDistance = 1;
+             double            maxDistance = 10000;
+             double            rolloffFactor = 1;
+             double            coneInnerAngle = 360;
+             double            coneOuterAngle = 360;
+             double            coneOuterGain = 0;
+};
+
+[Pref="dom.webaudio.enabled",
+ Constructor(AudioContext context, optional PannerOptions options)]
 interface PannerNode : AudioNode {
 
     // Default for stereo is equalpower
     attribute PanningModelType panningModel;
 
     // Uses a 3D cartesian coordinate system
     void setPosition(double x, double y, double z);
     void setOrientation(double x, double y, double z);
--- a/dom/webidl/PeriodicWave.webidl
+++ b/dom/webidl/PeriodicWave.webidl
@@ -5,13 +5,17 @@
  *
  * The origin of this IDL file is
  * https://webaudio.github.io/web-audio-api/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
-[Pref="dom.webaudio.enabled"]
-interface PeriodicWave {
-
+dictionary PeriodicWaveOptions : PeriodicWaveConstraints {
+             sequence<float> real;
+             sequence<float> imag;
 };
 
+[Pref="dom.webaudio.enabled",
+ Constructor(AudioContext context, PeriodicWaveOptions options)]
+interface PeriodicWave {
+};
--- a/dom/webidl/StereoPannerNode.webidl
+++ b/dom/webidl/StereoPannerNode.webidl
@@ -5,16 +5,21 @@
  *
  * The origin of this IDL file is
  * https://webaudio.github.io/web-audio-api/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
-[Pref="dom.webaudio.enabled"]
+dictionary StereoPannerOptions : AudioNodeOptions {
+             float pan = 0;
+};
+
+[Pref="dom.webaudio.enabled",
+ Constructor(AudioContext context, optional StereoPannerOptions options)]
 interface StereoPannerNode : AudioNode {
   readonly attribute AudioParam pan;
 };
 
 // Mozilla extension
 StereoPannerNode implements AudioNodePassThrough;
 
--- a/dom/webidl/WaveShaperNode.webidl
+++ b/dom/webidl/WaveShaperNode.webidl
@@ -11,20 +11,26 @@
  */
 
 enum OverSampleType {
   "none",
   "2x",
   "4x"
 };
 
-[Pref="dom.webaudio.enabled"]
+dictionary WaveShaperOptions : AudioNodeOptions {
+             sequence<float> curve;
+             OverSampleType  oversample = "none";
+};
+
+[Pref="dom.webaudio.enabled",
+ Constructor(AudioContext context, optional WaveShaperOptions options)]
 interface WaveShaperNode : AudioNode {
 
-      [SetterThrows]
+      [Cached, Pure, SetterThrows]
       attribute Float32Array? curve;
       attribute OverSampleType oversample;
 
 };
 
 // Mozilla extension
 WaveShaperNode implements AudioNodePassThrough;
 
--- a/dom/xul/nsXULElement.cpp
+++ b/dom/xul/nsXULElement.cpp
@@ -1604,20 +1604,20 @@ nsXULElement::LoadSrc()
         return NS_OK;
     }
     RefPtr<nsFrameLoader> frameLoader = GetFrameLoader();
     if (!frameLoader) {
         // Check if we have an opener we need to be setting
         nsXULSlots* slots = static_cast<nsXULSlots*>(Slots());
         nsCOMPtr<nsPIDOMWindowOuter> opener = do_QueryInterface(slots->mFrameLoaderOrOpener);
         if (!opener) {
-            // If we are a content-primary xul-browser, we want to take the opener property!
+            // If we are a primary xul-browser, we want to take the opener property!
             nsCOMPtr<nsIDOMChromeWindow> chromeWindow = do_QueryInterface(OwnerDoc()->GetWindow());
-            if (AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
-                            NS_LITERAL_STRING("content-primary"), eIgnoreCase) &&
+            if (AttrValueIs(kNameSpaceID_None, nsGkAtoms::primary,
+                            nsGkAtoms::_true, eIgnoreCase) &&
                 chromeWindow) {
                 nsCOMPtr<mozIDOMWindowProxy> wp;
                 chromeWindow->TakeOpenerForInitialContentBrowser(getter_AddRefs(wp));
                 opener = nsPIDOMWindowOuter::From(wp);
             }
         }
 
         // false as the last parameter so that xul:iframe/browser/editor
--- a/editor/composer/test/test_bug434998.xul
+++ b/editor/composer/test/test_bug434998.xul
@@ -14,17 +14,18 @@ https://bugzilla.mozilla.org/show_bug.cg
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
 
   <body xmlns="http://www.w3.org/1999/xhtml">
   <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=434998"
      target="_blank">Mozilla Bug 434998</a>
   <p/>
   <editor xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
           id="editor"
-          type="content-primary"
+          type="content"
+          primary="true"
           editortype="html"
           style="width: 400px; height: 100px; border: thin solid black"/>
   <p/>
   <pre id="test">
   </pre>
   </body>
   <script class="testbody" type="application/javascript">
   <![CDATA[
--- a/editor/libeditor/HTMLEditorDataTransfer.cpp
+++ b/editor/libeditor/HTMLEditorDataTransfer.cpp
@@ -1837,31 +1837,43 @@ HTMLEditor::InsertAsPlaintextQuotation(c
   // Protect the edit rules object from dying
   nsCOMPtr<nsIEditRules> rules(mRules);
   nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   NS_ENSURE_SUCCESS(rv, rv);
   if (cancel || handled) {
     return NS_OK; // rules canceled the operation
   }
 
-  // Wrap the inserted quote in a <span> so we can distinguish it.
+  // Wrap the inserted quote in a <span> so we can distinguish it. If we're
+  // inserting into the <body>, we use a <span> which is displayed as a block
+  // and sized to the screen using 98 viewport width units.
+  // We could use 100vw, but 98vw avoids a horizontal scroll bar where possible.
+  // All this is done to wrap overlong lines to the screen and not to the
+  // container element, the width-restricted body.
   nsCOMPtr<Element> newNode =
     DeleteSelectionAndCreateElement(*nsGkAtoms::span);
 
   // If this succeeded, then set selection inside the pre
   // so the inserted text will end up there.
   // If it failed, we don't care what the return value was,
   // but we'll fall through and try to insert the text anyway.
   if (newNode) {
     // Add an attribute on the pre node so we'll know it's a quotation.
     newNode->SetAttr(kNameSpaceID_None, nsGkAtoms::mozquote,
                      NS_LITERAL_STRING("true"), true);
     // Allow wrapping on spans so long lines get wrapped to the screen.
-    newNode->SetAttr(kNameSpaceID_None, nsGkAtoms::style,
-                     NS_LITERAL_STRING("white-space: pre-wrap;"), true);
+    nsCOMPtr<nsINode> parent = newNode->GetParentNode();
+    if (parent && parent->IsHTMLElement(nsGkAtoms::body)) {
+      newNode->SetAttr(kNameSpaceID_None, nsGkAtoms::style,
+        NS_LITERAL_STRING("white-space: pre-wrap; display: block; width: 98vw;"),
+        true);
+    } else {
+      newNode->SetAttr(kNameSpaceID_None, nsGkAtoms::style,
+        NS_LITERAL_STRING("white-space: pre-wrap;"), true);
+    }
 
     // and set the selection inside it:
     selection->Collapse(newNode, 0);
   }
 
   if (aAddCites) {
     rv = TextEditor::InsertAsQuotation(aQuotedText, aNodeInserted);
   } else {
--- a/editor/libeditor/tests/test_bug607584.xul
+++ b/editor/libeditor/tests/test_bug607584.xul
@@ -14,17 +14,18 @@ https://bugzilla.mozilla.org/show_bug.cg
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
 
   <body xmlns="http://www.w3.org/1999/xhtml">
   <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=607584"
      target="_blank">Mozilla Bug 607584</a>
   <p/>
   <editor xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
           id="editor"
-          type="content-primary"
+          type="content"
+          primary="true"
           editortype="html"
           style="width: 400px; height: 100px; border: thin solid black"/>
   <p/>
   <pre id="test">
   </pre>
   </body>
   <script class="testbody" type="application/javascript">
   <![CDATA[
--- a/editor/libeditor/tests/test_bug780908.xul
+++ b/editor/libeditor/tests/test_bug780908.xul
@@ -16,17 +16,18 @@ adapted from test_bug607584.xul by Kent 
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
 
   <body xmlns="http://www.w3.org/1999/xhtml">
   <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=780908"
      target="_blank">Mozilla Bug 780908</a>
   <p/>
   <editor xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
           id="editor"
-          type="content-primary"
+          type="content"
+          primary="true"
           editortype="html"
           style="width: 400px; height: 100px; border: thin solid black"/>
   <p/>
   <pre id="test">
   </pre>
   </body>
   <script class="testbody" type="application/javascript">
   <![CDATA[
--- a/embedding/browser/nsDocShellTreeOwner.cpp
+++ b/embedding/browser/nsDocShellTreeOwner.cpp
@@ -269,20 +269,20 @@ nsDocShellTreeOwner::EnsureContentTreeOw
 
   if (mWebBrowser) {
     mContentTreeOwner->WebBrowser(mWebBrowser);
   }
 }
 
 NS_IMETHODIMP
 nsDocShellTreeOwner::ContentShellAdded(nsIDocShellTreeItem* aContentShell,
-                                       bool aPrimary, const nsAString& aID)
+                                       bool aPrimary)
 {
   if (mTreeOwner)
-    return mTreeOwner->ContentShellAdded(aContentShell, aPrimary, aID);
+    return mTreeOwner->ContentShellAdded(aContentShell, aPrimary);
 
   EnsureContentTreeOwner();
   aContentShell->SetTreeOwner(mContentTreeOwner);
 
   if (aPrimary) {
     mPrimaryContentShell = aContentShell;
     mPrimaryTabParent = nullptr;
   }
--- a/gfx/2d/Logging.h
+++ b/gfx/2d/Logging.h
@@ -125,16 +125,17 @@ enum class LogReason : int {
   InvalidCommandList,
   AsyncTransactionTimeout, // 30
   TextureCreation,
   InvalidCacheSurface,
   AlphaWithBasicClient,
   UnbalancedClipStack,
   ProcessingError,
   InvalidDrawTarget,
+  NativeFontResourceNotFound,
   // End
   MustBeLessThanThis = 101,
 };
 
 struct BasicLogger
 {
   // For efficiency, this method exists and copies the logic of the
   // OutputMessage below.  If making any changes here, also make it
--- a/gfx/2d/RecordedEvent.cpp
+++ b/gfx/2d/RecordedEvent.cpp
@@ -1639,16 +1639,22 @@ RecordedFontDescriptor::RecordedFontDesc
   mData.resize(size);
   aStream.read((char*)&mData[0], size);
 }
 
 bool
 RecordedScaledFontCreation::PlayEvent(Translator *aTranslator) const
 {
   NativeFontResource *fontResource = aTranslator->LookupNativeFontResource(mFontDataKey);
+  if (!fontResource) {
+    gfxDevCrash(LogReason::NativeFontResourceNotFound) <<
+      "NativeFontResource lookup failed for key |" << hexa(mFontDataKey) << "|.";
+    return false;
+  }
+
   RefPtr<ScaledFont> scaledFont = fontResource->CreateScaledFont(mIndex, mGlyphSize);
   aTranslator->AddScaledFont(mRefPtr, scaledFont);
   return true;
 }
 
 void
 RecordedScaledFontCreation::RecordToStream(std::ostream &aStream) const
 {
--- a/gfx/skia/skia/src/ports/SkScalerContext_win_dw.cpp
+++ b/gfx/skia/skia/src/ports/SkScalerContext_win_dw.cpp
@@ -214,17 +214,23 @@ SkScalerContext_DW::SkScalerContext_DW(D
 #if SK_HAS_DWRITE_2_H
     fTypeface->fFactory->QueryInterface<IDWriteFactory2>(&fFactory2);
 
     SkTScopedComPtr<IDWriteFontFace2> fontFace2;
     fTypeface->fDWriteFontFace->QueryInterface<IDWriteFontFace2>(&fontFace2);
     fIsColorFont = fFactory2.get() && fontFace2.get() && fontFace2->IsColorFont();
 #endif
 
-    // In general, all glyphs should use CLEARTYPE_NATURAL_SYMMETRIC
+    IDWriteFactory* factory = sk_get_dwrite_factory();
+    if (factory != nullptr) {
+        HRVM(factory->CreateRenderingParams(&fDefaultRenderingParams),
+        "Could not create default rendering params");
+    }
+
+    // In general, all glyphs should DWriteFontFace::GetRecommendedRenderingMode
     // except when bi-level rendering is requested or there are embedded
     // bi-level bitmaps (and the embedded bitmap flag is set and no rotation).
     //
     // DirectWrite's IDWriteFontFace::GetRecommendedRenderingMode does not do
     // this. As a result, determine the actual size of the text and then see if
     // there are any embedded bi-level bitmaps of that size. If there are, then
     // force bitmaps by requesting bi-level rendering.
     //
@@ -310,23 +316,36 @@ SkScalerContext_DW::SkScalerContext_DW(D
     // drop out control in the y direction in order to be legible.
     } else if (is_hinted_without_gasp(typeface)) {
         fTextSizeRender = gdiTextSize;
         fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL;
         fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
         fTextSizeMeasure = realTextSize;
         fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
 
-    // The normal case is to use natural symmetric rendering and linear metrics.
+    // The normal case is to use the recommended rendering mode
     } else {
         fTextSizeRender = realTextSize;
-        fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
         fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
         fTextSizeMeasure = realTextSize;
         fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
+
+        if (!SUCCEEDED(fTypeface->fDWriteFontFace->GetRecommendedRenderingMode(
+                fTextSizeRender,
+                1.0f,
+                fMeasuringMode,
+                fDefaultRenderingParams.get(),
+                &fRenderingMode))) {
+            fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
+        }
+
+        // We don't support outline mode right now.
+        if (fRenderingMode == DWRITE_RENDERING_MODE_OUTLINE) {
+            fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
+        }
     }
 
     if (this->isSubpixel()) {
         fTextSizeMeasure = realTextSize;
         fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
     }
 }
 
--- a/gfx/skia/skia/src/ports/SkScalerContext_win_dw.h
+++ b/gfx/skia/skia/src/ports/SkScalerContext_win_dw.h
@@ -70,15 +70,16 @@ private:
     SkScalar fTextSizeRender;
     /** The text size to measure with. */
     SkScalar fTextSizeMeasure;
     SkAutoTUnref<DWriteFontTypeface> fTypeface;
     int fGlyphCount;
     DWRITE_RENDERING_MODE fRenderingMode;
     DWRITE_TEXTURE_TYPE fTextureType;
     DWRITE_MEASURING_MODE fMeasuringMode;
+    SkTScopedComPtr<IDWriteRenderingParams> fDefaultRenderingParams;
 #if SK_HAS_DWRITE_2_H
     SkTScopedComPtr<IDWriteFactory2> fFactory2;
     bool fIsColorFont;
 #endif
 };
 
 #endif
--- a/gfx/thebes/gfxPlatformGtk.h
+++ b/gfx/thebes/gfxPlatformGtk.h
@@ -128,21 +128,16 @@ public:
     // maximum number of fonts to substitute for a generic
     uint32_t MaxGenericSubstitions();
 
     bool SupportsPluginDirectBitmapDrawing() override {
       return true;
     }
 
     bool AccelerateLayersByDefault() override {
-#ifdef NIGHTLY_BUILD
-      // Only enable the GL compositor on Nightly for now until we have
-      // sufficient data for blocklisting.
-      return true;
-#endif
       return false;
     }
 
 #ifdef GL_PROVIDER_GLX
     already_AddRefed<mozilla::gfx::VsyncSource> CreateHardwareVsyncSource() override;
 #endif
 
 #ifdef MOZ_X11
--- a/js/ipc/JavaScriptShared.cpp
+++ b/js/ipc/JavaScriptShared.cpp
@@ -96,17 +96,17 @@ IdToObjectMap::empty() const
 
 #ifdef DEBUG
 bool
 IdToObjectMap::has(const ObjectId& id, const JSObject* obj) const
 {
     auto p = table_.lookup(id);
     if (!p)
         return false;
-    return p->value().unbarrieredGet() == obj;
+    return p->value() == obj;
 }
 #endif
 
 bool
 ObjectToIdMap::init()
 {
     return table_.initialized() || table_.init(32);
 }
--- a/js/public/RootingAPI.h
+++ b/js/public/RootingAPI.h
@@ -139,20 +139,16 @@ class PersistentRootedBase {};
 static void* const ConstNullValue = nullptr;
 
 namespace gc {
 struct Cell;
 template<typename T>
 struct PersistentRootedMarker;
 } /* namespace gc */
 
-#define DECLARE_POINTER_COMPARISON_OPS(T)                                                         \
-    bool operator==(const T& other) const { return get() == other; }                              \
-    bool operator!=(const T& other) const { return get() != other; }
-
 // Important: Return a reference so passing a Rooted<T>, etc. to
 // something that takes a |const T&| is not a GC hazard.
 #define DECLARE_POINTER_CONSTREF_OPS(T)                                                           \
     operator const T&() const { return get(); }                                                   \
     const T& operator->() const { return get(); }
 
 // Assignment operators on a base class are hidden by the implicitly defined
 // operator= on the derived class. Thus, define the operator= directly on the
@@ -228,16 +224,18 @@ AssertGCThingIsNotAnObjectSubclass(js::g
  */
 template <typename T>
 class Heap : public js::HeapBase<T>
 {
     // Please note: this can actually also be used by nsXBLMaybeCompiled<T>, for legacy reasons.
     static_assert(js::IsHeapConstructibleType<T>::value,
                   "Type T must be a public GC pointer type");
   public:
+    using ElementType = T;
+
     Heap() {
         static_assert(sizeof(T) == sizeof(Heap<T>),
                       "Heap<T> must be binary compatible with T.");
         init(GCPolicy<T>::initial());
     }
     explicit Heap(const T& p) { init(p); }
 
     /*
@@ -367,26 +365,25 @@ ScriptIsMarkedGray(const Heap<JSScript*>
  *  - It is invalid for a TenuredHeap<T> to refer to a non-tenured thing.
  *  - It is however valid for a Heap<T> to refer to a tenured thing.
  *  - It is not possible to store flag bits in a Heap<T>.
  */
 template <typename T>
 class TenuredHeap : public js::HeapBase<T>
 {
   public:
+    using ElementType = T;
+
     TenuredHeap() : bits(0) {
         static_assert(sizeof(T) == sizeof(TenuredHeap<T>),
                       "TenuredHeap<T> must be binary compatible with T.");
     }
     explicit TenuredHeap(T p) : bits(0) { setPtr(p); }
     explicit TenuredHeap(const TenuredHeap<T>& p) : bits(0) { setPtr(p.getPtr()); }
 
-    bool operator==(const TenuredHeap<T>& other) { return bits == other.bits; }
-    bool operator!=(const TenuredHeap<T>& other) { return bits != other.bits; }
-
     void setPtr(T newPtr) {
         MOZ_ASSERT((reinterpret_cast<uintptr_t>(newPtr) & flagsMask) == 0);
         if (newPtr)
             AssertGCThingMustBeTenured(newPtr);
         bits = (bits & flagsMask) | reinterpret_cast<uintptr_t>(newPtr);
     }
 
     void setFlags(uintptr_t flagsToSet) {
@@ -453,16 +450,18 @@ class TenuredHeap : public js::HeapBase<
  * specialization, define a HandleBase<T> specialization containing them.
  */
 template <typename T>
 class MOZ_NONHEAP_CLASS Handle : public js::HandleBase<T>
 {
     friend class JS::MutableHandle<T>;
 
   public:
+    using ElementType = T;
+
     /* Creates a handle from a handle of a type convertible to T. */
     template <typename S>
     MOZ_IMPLICIT Handle(Handle<S> handle,
                         typename mozilla::EnableIf<mozilla::IsConvertible<S, T>::value, int>::Type dummy = 0)
     {
         static_assert(sizeof(Handle<T>) == sizeof(T*),
                       "Handle must be binary compatible with T*.");
         ptr = reinterpret_cast<const T*>(handle.address());
@@ -513,17 +512,16 @@ class MOZ_NONHEAP_CLASS Handle : public 
                         typename mozilla::EnableIf<mozilla::IsConvertible<S, T>::value, int>::Type dummy = 0);
 
     /* Construct a read only handle from a mutable handle. */
     template <typename S>
     inline
     MOZ_IMPLICIT Handle(MutableHandle<S>& root,
                         typename mozilla::EnableIf<mozilla::IsConvertible<S, T>::value, int>::Type dummy = 0);
 
-    DECLARE_POINTER_COMPARISON_OPS(T);
     DECLARE_POINTER_CONSTREF_OPS(T);
     DECLARE_NONPOINTER_ACCESSOR_METHODS(*ptr);
 
   private:
     Handle() {}
     DELETE_ASSIGNMENT_OPS(Handle, T);
 
     enum Disambiguator { DeliberatelyChoosingThisOverload = 42 };
@@ -540,16 +538,18 @@ class MOZ_NONHEAP_CLASS Handle : public 
  * If you want to add additional methods to MutableHandle for a specific
  * specialization, define a MutableHandleBase<T> specialization containing
  * them.
  */
 template <typename T>
 class MOZ_STACK_CLASS MutableHandle : public js::MutableHandleBase<T>
 {
   public:
+    using ElementType = T;
+
     inline MOZ_IMPLICIT MutableHandle(Rooted<T>* root);
     inline MOZ_IMPLICIT MutableHandle(PersistentRooted<T>* root);
 
   private:
     // Disallow nullptr for overloading purposes.
     MutableHandle(decltype(nullptr)) = delete;
 
   public:
@@ -769,16 +769,18 @@ class MOZ_RAII Rooted : public js::Roote
         MOZ_ASSERT(cx->isJSContext);
         return cx->roots.stackRoots_;
     }
     inline js::RootedListHeads& rootLists(JSContext* cx) {
         return rootLists(js::ContextFriendFields::get(cx));
     }
 
   public:
+    using ElementType = T;
+
     template <typename RootingContext>
     explicit Rooted(const RootingContext& cx)
       : ptr(GCPolicy<T>::initial())
     {
         registerWithRootLists(rootLists(cx));
     }
 
     template <typename RootingContext, typename S>
@@ -798,17 +800,16 @@ class MOZ_RAII Rooted : public js::Roote
     /*
      * This method is public for Rooted so that Codegen.py can use a Rooted
      * interchangeably with a MutableHandleValue.
      */
     void set(const T& value) {
         ptr = value;
     }
 
-    DECLARE_POINTER_COMPARISON_OPS(T);
     DECLARE_POINTER_CONSTREF_OPS(T);
     DECLARE_POINTER_ASSIGN_OPS(Rooted, T);
     DECLARE_NONPOINTER_ACCESSOR_METHODS(ptr);
     DECLARE_NONPOINTER_MUTABLE_ACCESSOR_METHODS(ptr);
 
   private:
     /*
      * These need to be templated on void* to avoid aliasing issues between, for
@@ -875,23 +876,24 @@ class HandleBase<JSObject*>
     JS::Handle<U*> as() const;
 };
 
 /** Interface substitute for Rooted<T> which does not root the variable's memory. */
 template <typename T>
 class MOZ_RAII FakeRooted : public RootedBase<T>
 {
   public:
+    using ElementType = T;
+
     template <typename CX>
     explicit FakeRooted(CX* cx) : ptr(JS::GCPolicy<T>::initial()) {}
 
     template <typename CX>
     FakeRooted(CX* cx, T initial) : ptr(initial) {}
 
-    DECLARE_POINTER_COMPARISON_OPS(T);
     DECLARE_POINTER_CONSTREF_OPS(T);
     DECLARE_POINTER_ASSIGN_OPS(FakeRooted, T);
     DECLARE_NONPOINTER_ACCESSOR_METHODS(ptr);
     DECLARE_NONPOINTER_MUTABLE_ACCESSOR_METHODS(ptr);
 
   private:
     T ptr;
 
@@ -902,16 +904,18 @@ class MOZ_RAII FakeRooted : public Roote
     FakeRooted(const FakeRooted&) = delete;
 };
 
 /** Interface substitute for MutableHandle<T> which is not required to point to rooted memory. */
 template <typename T>
 class FakeMutableHandle : public js::MutableHandleBase<T>
 {
   public:
+    using ElementType = T;
+
     MOZ_IMPLICIT FakeMutableHandle(T* t) {
         ptr = t;
     }
 
     MOZ_IMPLICIT FakeMutableHandle(FakeRooted<T>* root) {
         ptr = root->address();
     }
 
@@ -1092,16 +1096,18 @@ class PersistentRooted : public js::Pers
         MOZ_ASSERT(cx->isJSContext);
         return cx->roots;
     }
 
     // Disallow ExclusiveContext*.
     js::RootLists& rootLists(js::ContextFriendFields* cx) = delete;
 
   public:
+    using ElementType = T;
+
     PersistentRooted() : ptr(GCPolicy<T>::initial()) {}
 
     template <typename RootingContext>
     explicit PersistentRooted(const RootingContext& cx)
       : ptr(GCPolicy<T>::initial())
     {
         registerWithRootLists(rootLists(cx));
     }
@@ -1145,17 +1151,16 @@ class PersistentRooted : public js::Pers
 
     void reset() {
         if (initialized()) {
             set(GCPolicy<T>::initial());
             ListBase::remove();
         }
     }
 
-    DECLARE_POINTER_COMPARISON_OPS(T);
     DECLARE_POINTER_CONSTREF_OPS(T);
     DECLARE_POINTER_ASSIGN_OPS(PersistentRooted, T);
     DECLARE_NONPOINTER_ACCESSOR_METHODS(ptr);
 
     // These are the same as DECLARE_NONPOINTER_MUTABLE_ACCESSOR_METHODS, except
     // they check that |this| is initialized in case the caller later stores
     // something in |ptr|.
     T* address() {
@@ -1182,16 +1187,18 @@ class PersistentRooted : public js::Pers
     MaybeWrapped ptr;
 } JS_HAZ_ROOTED;
 
 class JS_PUBLIC_API(ObjectPtr)
 {
     Heap<JSObject*> value;
 
   public:
+    using ElementType = JSObject*;
+
     ObjectPtr() : value(nullptr) {}
 
     explicit ObjectPtr(JSObject* obj) : value(obj) {}
 
     /* Always call finalize before the destructor. */
     ~ObjectPtr() { MOZ_ASSERT(!value); }
 
     void finalize(JSRuntime* rt);
@@ -1303,11 +1310,182 @@ Swap(JS::TenuredHeap<T>& aX, JS::Tenured
 {
     T tmp = aX;
     aX = aY;
     aY = tmp;
 }
 
 } /* namespace mozilla */
 
+namespace js {
+namespace detail {
+
+// DefineComparisonOps is a trait which selects which wrapper classes to define
+// operator== and operator!= for. It supplies a getter function to extract the
+// value to compare. This is used to avoid triggering the automatic read
+// barriers where appropriate.
+//
+// If DefineComparisonOps is not specialized for a particular wrapper you may
+// get errors such as 'invalid operands to binary expression' or 'no match for
+// operator==' when trying to compare against instances of the wrapper.
+
+template <typename T>
+struct DefineComparisonOps : mozilla::FalseType {};
+
+template <typename T>
+struct DefineComparisonOps<JS::Heap<T>> : mozilla::TrueType {
+    static const T& get(const JS::Heap<T>& v) { return v.unbarrieredGet(); }
+};
+
+template <typename T>
+struct DefineComparisonOps<JS::TenuredHeap<T>> : mozilla::TrueType {
+    static const T get(const JS::TenuredHeap<T>& v) { return v.unbarrieredGetPtr(); }
+};
+
+template <>
+struct DefineComparisonOps<JS::ObjectPtr> : mozilla::TrueType {
+    static const JSObject* get(const JS::ObjectPtr& v) { return v.unbarrieredGet(); }
+};
+
+template <typename T>
+struct DefineComparisonOps<JS::Rooted<T>> : mozilla::TrueType {
+    static const T& get(const JS::Rooted<T>& v) { return v.get(); }
+};
+
+template <typename T>
+struct DefineComparisonOps<JS::Handle<T>> : mozilla::TrueType {
+    static const T& get(const JS::Handle<T>& v) { return v.get(); }
+};
+
+template <typename T>
+struct DefineComparisonOps<JS::MutableHandle<T>> : mozilla::TrueType {
+    static const T& get(const JS::MutableHandle<T>& v) { return v.get(); }
+};
+
+template <typename T>
+struct DefineComparisonOps<JS::PersistentRooted<T>> : mozilla::TrueType {
+    static const T& get(const JS::PersistentRooted<T>& v) { return v.get(); }
+};
+
+template <typename T>
+struct DefineComparisonOps<js::FakeRooted<T>> : mozilla::TrueType {
+    static const T& get(const js::FakeRooted<T>& v) { return v.get(); }
+};
+
+template <typename T>
+struct DefineComparisonOps<js::FakeMutableHandle<T>> : mozilla::TrueType {
+    static const T& get(const js::FakeMutableHandle<T>& v) { return v.get(); }
+};
+
+} /* namespace detail */
+} /* namespace js */
+
+// Overload operator== and operator!= for all types with the DefineComparisonOps
+// trait using the supplied getter.
+//
+// There are four cases:
+
+// Case 1: comparison between two wrapper objects.
+
+template <typename T, typename U>
+typename mozilla::EnableIf<js::detail::DefineComparisonOps<T>::value &&
+                           js::detail::DefineComparisonOps<U>::value, bool>::Type
+operator==(const T& a, const U& b) {
+    return js::detail::DefineComparisonOps<T>::get(a) == js::detail::DefineComparisonOps<U>::get(b);
+}
+
+template <typename T, typename U>
+typename mozilla::EnableIf<js::detail::DefineComparisonOps<T>::value &&
+                           js::detail::DefineComparisonOps<U>::value, bool>::Type
+operator!=(const T& a, const U& b) {
+    return !(a == b);
+}
+
+// Case 2: comparison between a wrapper object and its unwrapped element type.
+
+template <typename T>
+typename mozilla::EnableIf<js::detail::DefineComparisonOps<T>::value, bool>::Type
+operator==(const T& a, const typename T::ElementType& b) {
+    return js::detail::DefineComparisonOps<T>::get(a) == b;
+}
+
+template <typename T>
+typename mozilla::EnableIf<js::detail::DefineComparisonOps<T>::value, bool>::Type
+operator!=(const T& a, const typename T::ElementType& b) {
+    return !(a == b);
+}
+
+template <typename T>
+typename mozilla::EnableIf<js::detail::DefineComparisonOps<T>::value, bool>::Type
+operator==(const typename T::ElementType& a, const T& b) {
+    return a == js::detail::DefineComparisonOps<T>::get(b);
+}
+
+template <typename T>
+typename mozilla::EnableIf<js::detail::DefineComparisonOps<T>::value, bool>::Type
+operator!=(const typename T::ElementType& a, const T& b) {
+    return !(a == b);
+}
+
+// Case 3: For pointer wrappers, comparison between the wrapper and a const
+// element pointer.
+
+template <typename T>
+typename mozilla::EnableIf<js::detail::DefineComparisonOps<T>::value &&
+                           mozilla::IsPointer<typename T::ElementType>::value, bool>::Type
+operator==(const typename mozilla::RemovePointer<typename T::ElementType>::Type* a, const T& b) {
+    return a == js::detail::DefineComparisonOps<T>::get(b);
+}
+
+template <typename T>
+typename mozilla::EnableIf<js::detail::DefineComparisonOps<T>::value &&
+                           mozilla::IsPointer<typename T::ElementType>::value, bool>::Type
+operator!=(const typename mozilla::RemovePointer<typename T::ElementType>::Type* a, const T& b) {
+    return !(a == b);
+}
+
+template <typename T>
+typename mozilla::EnableIf<js::detail::DefineComparisonOps<T>::value &&
+                           mozilla::IsPointer<typename T::ElementType>::value, bool>::Type
+operator==(const T& a, const typename mozilla::RemovePointer<typename T::ElementType>::Type* b) {
+    return js::detail::DefineComparisonOps<T>::get(a) == b;
+}
+
+template <typename T>
+typename mozilla::EnableIf<js::detail::DefineComparisonOps<T>::value &&
+                           mozilla::IsPointer<typename T::ElementType>::value, bool>::Type
+operator!=(const T& a, const typename mozilla::RemovePointer<typename T::ElementType>::Type* b) {
+    return !(a == b);
+}
+
+// Case 4: For pointer wrappers, comparison between the wrapper and nullptr.
+
+template <typename T>
+typename mozilla::EnableIf<js::detail::DefineComparisonOps<T>::value &&
+                           mozilla::IsPointer<typename T::ElementType>::value, bool>::Type
+operator==(std::nullptr_t a, const T& b) {
+    return a == js::detail::DefineComparisonOps<T>::get(b);
+}
+
+template <typename T>
+typename mozilla::EnableIf<js::detail::DefineComparisonOps<T>::value &&
+                           mozilla::IsPointer<typename T::ElementType>::value, bool>::Type
+operator!=(std::nullptr_t a, const T& b) {
+    return !(a == b);
+}
+
+template <typename T>
+typename mozilla::EnableIf<js::detail::DefineComparisonOps<T>::value &&
+                           mozilla::IsPointer<typename T::ElementType>::value, bool>::Type
+operator==(const T& a, std::nullptr_t b) {
+    return js::detail::DefineComparisonOps<T>::get(a) == b;
+}
+
+template <typename T>
+typename mozilla::EnableIf<js::detail::DefineComparisonOps<T>::value &&
+                           mozilla::IsPointer<typename T::ElementType>::value, bool>::Type
+operator!=(const T& a, std::nullptr_t b) {
+    return !(a == b);
+}
+
 #undef DELETE_ASSIGNMENT_OPS
 
 #endif  /* js_RootingAPI_h */
--- a/js/src/ds/PageProtectingVector.h
+++ b/js/src/ds/PageProtectingVector.h
@@ -10,111 +10,178 @@
 #include "mozilla/Vector.h"
 
 #include "ds/MemoryProtectionExceptionHandler.h"
 #include "gc/Memory.h"
 
 namespace js {
 
 /*
- * PageProtectingVector is a vector that can only grow or be cleared, and marks
- * all of its fully used memory pages as read-only. It can be used to detect
- * heap corruption in important buffers, since anything that tries to write
- * into its protected pages will crash.
+ * PageProtectingVector is a vector that can only grow or be cleared, restricts
+ * access to memory pages that haven't been used yet, and marks all of its fully
+ * used memory pages as read-only. It can be used to detect heap corruption in
+ * important buffers, since anything that tries to write into its protected
+ * pages will crash. On Nightly and Aurora, these crashes will additionally be
+ * annotated with a moz crash reason using MemoryProtectionExceptionHandler.
  *
  * PageProtectingVector's protection is limited to full pages. If the front
  * of its buffer is not aligned on a page boundary, bytes preceding the first
  * page boundary will not be protected. Similarly, the end of the buffer will
  * not be fully protected unless it is aligned on a page boundary. Altogether,
  * up to two pages of memory may not be protected.
  */
 template<typename T,
          size_t MinInlineCapacity = 0,
-         class AllocPolicy = mozilla::MallocAllocPolicy>
+         class AllocPolicy = mozilla::MallocAllocPolicy,
+         bool ProtectUsed = true,
+         bool ProtectUnused = true,
+         size_t InitialLowerBound = 0>
 class PageProtectingVector final
 {
     mozilla::Vector<T, MinInlineCapacity, AllocPolicy> vector;
 
     size_t pageSize;
     size_t pageMask;
 
     /*
      * The number of bytes between the start of the buffer being used by
      * |vector| and the first page we can protect. With jemalloc, this number
      * should always be 0 for vectors with a buffer larger than |pageSize / 2|
      * bytes, but with other allocators large buffers may not be page-aligned.
      */
     size_t offsetToPage;
 
+    /*
+     * The offset in bytes of the first completely unused page in the buffer.
+     * Note: this page might extend or even begin past the end of the buffer.
+     */
+    size_t firstUnusedPage;
+
     /* The number of currently protected bytes (a multiple of pageSize). */
     size_t protectedBytes;
+    size_t protectedUnusedBytes;
 
     /*
-     * The number of bytes that are currently unprotected, but could be.
+     * The number of used bytes that are currently unprotected, but could be.
      * This number starts at |-offsetToPage|, since any bytes before
      * |vector.begin() + offsetToPage| can never be protected (as we do not own
      * the whole page). As a result, if |unprotectedBytes >= pageSize|, we know
      * we can protect at least one more page, and |unprotectedBytes & ~pageMask|
      * is always the number of additional bytes we can protect. Put another way,
-     * |offsetToPage + protectedBytes + unprotectedBytes == vector.length()|
+     * |offsetToPage + protectedBytes + unprotectedBytes == [size in bytes]|
      * always holds, and if |protectedBytes != 0| then |unprotectedBytes >= 0|.
      */
     intptr_t unprotectedBytes;
 
     /*
+     * The number of unprotected, unused pages. Note that this value
+     * may not be up to date if unused page protection is disabled.
+     */
+    intptr_t unprotectedUnusedPages;
+
+    /*
      * The size in bytes that a buffer needs to be before its pages will be
      * protected. This is intended to reduce churn for small vectors while
      * still offering protection when they grow large enough.
      */
     size_t protectionLowerBound;
 
-    bool protectionEnabled;
+    bool protectUsedEnabled;
+    bool protectUnusedEnabled;
     bool regionUnprotected;
+    bool protectionDisabled;
 
-    void updateOffsetToPage() {
+    void updateProtectUsedOffsets() {
+        MOZ_ASSERT(!protectedBytes);
         unprotectedBytes += offsetToPage;
         offsetToPage = (pageSize - (uintptr_t(vector.begin()) & pageMask)) & pageMask;
         unprotectedBytes -= offsetToPage;
-#ifndef RELEASE_OR_BETA
-        protectionEnabled = vector.capacity() >= protectionLowerBound &&
-                            vector.capacity() >= pageSize + offsetToPage;
-#endif
+        protectUsedEnabled = ProtectUsed && !protectionDisabled &&
+                             vector.capacity() * sizeof(T) >= protectionLowerBound &&
+                             vector.capacity() * sizeof(T) >= pageSize + offsetToPage;
+    }
+
+    void updateProtectUnusedOffsets() {
+        MOZ_ASSERT(!protectedUnusedBytes);
+        firstUnusedPage = ((vector.length() * sizeof(T) - offsetToPage - 1) | pageMask) +
+                          offsetToPage + 1;
+        if (MOZ_LIKELY(vector.capacity() * sizeof(T) >= firstUnusedPage))
+            unprotectedUnusedPages = (vector.capacity() * sizeof(T) - firstUnusedPage) / pageSize;
+        else
+            unprotectedUnusedPages = 0;
+        protectUnusedEnabled = ProtectUnused && !protectionDisabled &&
+                               vector.capacity() * sizeof(T) >= protectionLowerBound &&
+                               vector.capacity() * sizeof(T) >= pageSize + offsetToPage;
+    }
+
+    void updateProtectionOffsets() {
+        updateProtectUsedOffsets();
+        updateProtectUnusedOffsets();
     }
 
     void protect() {
-        if (!regionUnprotected && protectionEnabled && unprotectedBytes >= intptr_t(pageSize)) {
+        MOZ_ASSERT(!regionUnprotected);
+        if (MOZ_UNLIKELY(protectUsedEnabled && unprotectedBytes >= intptr_t(pageSize))) {
             size_t toProtect = size_t(unprotectedBytes) & ~pageMask;
             uintptr_t addr = uintptr_t(vector.begin()) + offsetToPage + protectedBytes;
             gc::MakePagesReadOnly(reinterpret_cast<void*>(addr), toProtect);
             unprotectedBytes -= toProtect;
             protectedBytes += toProtect;
         }
     }
 
+    void protectUnused() {
+        MOZ_ASSERT(!protectedUnusedBytes);
+        if (protectUnusedEnabled && unprotectedUnusedPages) {
+            size_t toProtect = unprotectedUnusedPages * pageSize;
+            uintptr_t addr = uintptr_t(vector.begin()) + firstUnusedPage;
+            gc::ProtectPages(reinterpret_cast<void*>(addr), toProtect);
+            unprotectedUnusedPages = 0;
+            protectedUnusedBytes = toProtect;
+        }
+    }
+
+    void protectNewBuffer() {
+        updateProtectionOffsets();
+        if (protectUsedEnabled || protectUnusedEnabled)
+            MemoryProtectionExceptionHandler::addRegion(vector.begin(),
+                                                        vector.capacity() * sizeof(T));
+        protect();
+        protectUnused();
+    }
+
     void unprotect() {
-        MOZ_ASSERT_IF(!protectionEnabled, !protectedBytes);
-        if (!regionUnprotected && protectedBytes) {
+        MOZ_ASSERT(!regionUnprotected);
+        MOZ_ASSERT_IF(!protectUsedEnabled, !protectedBytes);
+        if (protectedBytes) {
             uintptr_t addr = uintptr_t(vector.begin()) + offsetToPage;
             gc::UnprotectPages(reinterpret_cast<void*>(addr), protectedBytes);
             unprotectedBytes += protectedBytes;
             protectedBytes = 0;
         }
     }
 
-    void protectNewBuffer() {
-        updateOffsetToPage();
-        if (protectionEnabled)
-            MemoryProtectionExceptionHandler::addRegion(vector.begin(), vector.capacity());
-        protect();
+    void unprotectUnused(size_t newSize) {
+        MOZ_ASSERT_IF(!protectUnusedEnabled, !protectedUnusedBytes);
+        if (MOZ_UNLIKELY(protectedUnusedBytes && newSize > firstUnusedPage)) {
+            size_t toUnprotect = ((newSize - firstUnusedPage) + pageMask) & ~pageMask;
+            if (toUnprotect > protectedUnusedBytes)
+                toUnprotect = protectedUnusedBytes;
+            uintptr_t addr = uintptr_t(vector.begin()) + firstUnusedPage;
+            gc::UnprotectPages(reinterpret_cast<void*>(addr), toUnprotect);
+            firstUnusedPage += toUnprotect;
+            protectedUnusedBytes -= toUnprotect;
+        }
     }
 
     void unprotectOldBuffer() {
-        if (protectionEnabled)
+        unprotect();
+        unprotectUnused(vector.capacity() * sizeof(T));
+        if (protectUsedEnabled || protectUnusedEnabled)
             MemoryProtectionExceptionHandler::removeRegion(vector.begin());
-        unprotect();
     }
 
     bool anyProtected(size_t first, size_t last) {
         return last >= offsetToPage && first < offsetToPage + protectedBytes;
     }
 
     void setContainingRegion(size_t first, size_t last, uintptr_t* addr, size_t* size) {
         if (first < offsetToPage)
@@ -123,21 +190,26 @@ class PageProtectingVector final
             last = offsetToPage + protectedBytes - 1;
         uintptr_t firstAddr = uintptr_t(vector.begin());
         uintptr_t firstPage = (firstAddr + first) & ~pageMask;
         uintptr_t lastPage = (firstAddr + last) & ~pageMask;
         *size = pageSize + (lastPage - firstPage);
         *addr = firstPage;
     }
 
-    void increaseElemsUsed(size_t used) {
-        unprotectedBytes += used * sizeof(T);
+    void protectAfterUsing(size_t size) {
+        unprotectedBytes += size * sizeof(T);
         protect();
     }
 
+    void unprotectBeforeUsing(size_t size) {
+        MOZ_ASSERT(vector.length() + size <= vector.capacity());
+        unprotectUnused((vector.length() + size) * sizeof(T));
+    }
+
     /* A helper class to simplify unprotecting and reprotecting when needed. */
     class AutoUnprotect
     {
         PageProtectingVector* vector;
 
       public:
         AutoUnprotect() : vector(nullptr) {};
 
@@ -157,24 +229,42 @@ class PageProtectingVector final
     };
 
   public:
     explicit PageProtectingVector(AllocPolicy policy = AllocPolicy())
       : vector(policy),
         pageSize(gc::SystemPageSize()),
         pageMask(pageSize - 1),
         offsetToPage(0),
+        firstUnusedPage(0),
         protectedBytes(0),
+        protectedUnusedBytes(0),
         unprotectedBytes(0),
-        protectionLowerBound(0),
-        protectionEnabled(false),
-        regionUnprotected(false) { protectNewBuffer(); }
+        unprotectedUnusedPages(0),
+        protectionLowerBound(InitialLowerBound),
+        protectUsedEnabled(false),
+        protectUnusedEnabled(false),
+        regionUnprotected(false),
+        protectionDisabled(false) { protectNewBuffer(); }
 
     ~PageProtectingVector() { unprotectOldBuffer(); }
 
+    void disableProtection() {
+        MOZ_ASSERT(!protectionDisabled);
+        unprotectOldBuffer();
+        protectionDisabled = true;
+        updateProtectionOffsets();
+    }
+
+    void enableProtection() {
+        MOZ_ASSERT(protectionDisabled);
+        protectionDisabled = false;
+        protectNewBuffer();
+    }
+
     /*
      * Sets the lower bound on the size, in bytes, that this vector's underlying
      * capacity has to be before its used pages will be protected.
      */
     void setLowerBoundForProtection(size_t bytes) {
         if (protectionLowerBound != bytes) {
             unprotectOldBuffer();
             protectionLowerBound = bytes;
@@ -228,30 +318,33 @@ class PageProtectingVector final
         AutoUnprotect guard;
         if (size > vector.capacity())
             guard.emplace(this);
         return vector.reserve(size);
     }
 
     template<typename U>
     MOZ_ALWAYS_INLINE void infallibleAppend(const U* values, size_t size) {
+        unprotectBeforeUsing(size);
         vector.infallibleAppend(values, size);
-        increaseElemsUsed(size);
+        protectAfterUsing(size);
     }
 
     template<typename U>
     MOZ_MUST_USE bool append(const U* values, size_t size) {
         bool ret;
         {
             AutoUnprotect guard;
             if (MOZ_UNLIKELY(vector.length() + size > vector.capacity()))
                 guard.emplace(this);
+            else
+                unprotectBeforeUsing(size);
             ret = vector.append(values, size);
         }
         if (ret)
-            increaseElemsUsed(size);
+            protectAfterUsing(size);
         return ret;
     }
 };
 
 } /* namespace js */
 
 #endif /* ds_PageProtectingVector_h */
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -347,17 +347,18 @@ class BarrieredBase : public BarrieredBa
 template <class T>
 class WriteBarrieredBase : public BarrieredBase<T>
 {
   protected:
     // WriteBarrieredBase is not directly instantiable.
     explicit WriteBarrieredBase(const T& v) : BarrieredBase<T>(v) {}
 
   public:
-    DECLARE_POINTER_COMPARISON_OPS(T);
+    using ElementType = T;
+
     DECLARE_POINTER_CONSTREF_OPS(T);
 
     // Use this if the automatic coercion to T isn't working.
     const T& get() const { return this->value; }
 
     // Use this if you want to change the value without invoking barriers.
     // Obviously this is dangerous unless you know the barrier is not needed.
     void unsafeSet(const T& v) { this->value = v; }
@@ -444,20 +445,16 @@ class GCPtr : public WriteBarrieredBase<
 
     void init(const T& v) {
         this->value = v;
         this->post(JS::GCPolicy<T>::initial(), v);
     }
 
     DECLARE_POINTER_ASSIGN_OPS(GCPtr, T);
 
-    T unbarrieredGet() const {
-        return this->value;
-    }
-
   private:
     void set(const T& v) {
         this->pre();
         T tmp = this->value;
         this->value = v;
         this->post(tmp, this->value);
     }
 
@@ -598,34 +595,33 @@ class ReadBarriered : public ReadBarrier
 
     ReadBarriered& operator=(const ReadBarriered& v) {
         T prior = this->value;
         this->value = v.value;
         this->post(prior, v.value);
         return *this;
     }
 
-    const T get() const {
-        if (!InternalBarrierMethods<T>::isMarkable(this->value))
-            return JS::GCPolicy<T>::initial();
-        this->read();
+    const T& get() const {
+        if (InternalBarrierMethods<T>::isMarkable(this->value))
+            this->read();
         return this->value;
     }
 
-    const T unbarrieredGet() const {
+    const T& unbarrieredGet() const {
         return this->value;
     }
 
     explicit operator bool() const {
         return bool(this->value);
     }
 
-    operator const T() const { return get(); }
+    operator const T&() const { return get(); }
 
-    const T operator->() const { return get(); }
+    const T& operator->() const { return get(); }
 
     T* unsafeGet() { return &this->value; }
     T const* unsafeGet() const { return &this->value; }
 
     void set(const T& v)
     {
         T tmp = this->value;
         this->value = v;
@@ -946,11 +942,40 @@ typedef ReadBarriered<Shape*> ReadBarrie
 typedef ReadBarriered<jit::JitCode*> ReadBarrieredJitCode;
 typedef ReadBarriered<ObjectGroup*> ReadBarrieredObjectGroup;
 typedef ReadBarriered<JS::Symbol*> ReadBarrieredSymbol;
 typedef ReadBarriered<WasmInstanceObject*> ReadBarrieredWasmInstanceObject;
 typedef ReadBarriered<WasmTableObject*> ReadBarrieredWasmTableObject;
 
 typedef ReadBarriered<Value> ReadBarrieredValue;
 
+namespace detail {
+
+template <typename T>
+struct DefineComparisonOps<PreBarriered<T>> : mozilla::TrueType {
+    static const T& get(const PreBarriered<T>& v) { return v.get(); }
+};
+
+template <typename T>
+struct DefineComparisonOps<GCPtr<T>> : mozilla::TrueType {
+    static const T& get(const GCPtr<T>& v) { return v.get(); }
+};
+
+template <typename T>
+struct DefineComparisonOps<HeapPtr<T>> : mozilla::TrueType {
+    static const T& get(const HeapPtr<T>& v) { return v.get(); }
+};
+
+template <typename T>
+struct DefineComparisonOps<ReadBarriered<T>> : mozilla::TrueType {
+    static const T& get(const ReadBarriered<T>& v) { return v.unbarrieredGet(); }
+};
+
+template <>
+struct DefineComparisonOps<HeapSlot> : mozilla::TrueType {
+    static const Value& get(const HeapSlot& v) { return v.get(); }
+};
+
+} /* namespace detail */
+
 } /* namespace js */
 
 #endif /* gc_Barrier_h */
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1322932.js
@@ -0,0 +1,12 @@
+// |jit-test| error: ReferenceError
+
+(function() {
+    for (var i = 0; i < 4; ++i) {
+        if (i % 3 == 0) {
+            for (var x in y) {}
+        } else {
+            continue;
+        }
+    }
+})()
+
--- a/js/src/jit/BaselineCacheIR.cpp
+++ b/js/src/jit/BaselineCacheIR.cpp
@@ -1707,16 +1707,70 @@ BaselineCacheIRCompiler::emitLoadDenseEl
     // Hole check. After that it's safe to clobber R0.
     BaseObjectElementIndex element(scratch, index);
     masm.branchTestMagic(Assembler::Equal, element, failure->label());
     masm.loadValue(element, R0);
     return true;
 }
 
 bool
+BaselineCacheIRCompiler::emitGuardNoDenseElements()
+{
+    Register obj = allocator.useRegister(masm, reader.objOperandId());
+    AutoScratchRegister scratch(allocator, masm);
+
+    FailurePath* failure;
+    if (!addFailurePath(&failure))
+        return false;
+
+    // Load obj->elements.
+    masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratch);
+
+    // Make sure there are no dense elements.
+    Address initLength(scratch, ObjectElements::offsetOfInitializedLength());
+    masm.branch32(Assembler::NotEqual, initLength, Imm32(0), failure->label());
+    return true;
+}
+
+bool
+BaselineCacheIRCompiler::emitLoadDenseElementHoleResult()
+{
+    Register obj = allocator.useRegister(masm, reader.objOperandId());
+    Register index = allocator.useRegister(masm, reader.int32OperandId());
+    AutoScratchRegister scratch(allocator, masm);
+
+    FailurePath* failure;
+    if (!addFailurePath(&failure))
+        return false;
+
+    // Make sure the index is nonnegative.
+    masm.branch32(Assembler::LessThan, index, Imm32(0), failure->label());
+
+    // Load obj->elements.
+    masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratch);
+
+    // Guard on the initialized length.
+    Label hole;
+    Address initLength(scratch, ObjectElements::offsetOfInitializedLength());
+    masm.branch32(Assembler::BelowOrEqual, initLength, index, &hole);
+
+    // Load the value.
+    Label done;
+    masm.loadValue(BaseObjectElementIndex(scratch, index), R0);
+    masm.branchTestMagic(Assembler::NotEqual, R0, &done);
+
+    // Load undefined for the hole.
+    masm.bind(&hole);
+    masm.moveValue(UndefinedValue(), R0);
+
+    masm.bind(&done);
+    return true;
+}
+
+bool
 BaselineCacheIRCompiler::emitLoadUnboxedArrayElementResult()
 {
     Register obj = allocator.useRegister(masm, reader.objOperandId());
     Register index = allocator.useRegister(masm, reader.int32OperandId());
     JSValueType elementType = reader.valueType();
     AutoScratchRegister scratch(allocator, masm);
 
     FailurePath* failure;
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -91,16 +91,18 @@ GetPropIRGenerator::tryAttachStub()
             ValOperandId indexId = getElemKeyValueId();
             if (tryAttachTypedElement(obj, objId, indexId))
                 return true;
         }
         if (idVal_.isInt32()) {
             ValOperandId indexId = getElemKeyValueId();
             if (tryAttachDenseElement(obj, objId, indexId))
                 return true;
+            if (tryAttachDenseElementHole(obj, objId, indexId))
+                return true;
             if (tryAttachUnboxedArrayElement(obj, objId, indexId))
                 return true;
             if (tryAttachArgumentsObjectArg(obj, objId, indexId))
                 return true;
             return false;
         }
         return false;
     }
@@ -903,16 +905,98 @@ GetPropIRGenerator::tryAttachDenseElemen
     writer.guardShape(objId, obj->as<NativeObject>().lastProperty());
 
     Int32OperandId int32IndexId = writer.guardIsInt32(indexId);
     writer.loadDenseElementResult(objId, int32IndexId);
     writer.typeMonitorResult();
     return true;
 }
 
+static bool
+CanAttachDenseElementHole(JSObject* obj)
+{
+    // Make sure this object already has dense elements.
+    if (obj->as<NativeObject>().getDenseInitializedLength() == 0)
+        return false;
+
+    // Now we have to make sure the objects on the prototype don't
+    // have any int32 properties or that such properties can't appear
+    // without a shape change.
+    // Otherwise returning undefined for holes would obviously be incorrect,
+    // because we would have to lookup a property on the prototype instead.
+    do {
+        if (obj->isIndexed())
+            return false;
+
+        if (ClassCanHaveExtraProperties(obj->getClass()))
+            return false;
+
+        JSObject* proto = obj->staticPrototype();
+        if (!proto)
+            break;
+
+        if (!proto->isNative())
+            return false;
+
+        // Make sure objects on the prototype don't have dense elements.
+        if (proto->as<NativeObject>().getDenseInitializedLength() != 0)
+            return false;
+
+        obj = proto;
+    } while (true);
+
+    return true;
+}
+
+bool
+GetPropIRGenerator::tryAttachDenseElementHole(HandleObject obj, ObjOperandId objId,
+                                              ValOperandId indexId)
+{
+    MOZ_ASSERT(idVal_.isInt32());
+
+    if (idVal_.toInt32() < 0)
+        return false;
+
+    if (!obj->isNative() || !CanAttachDenseElementHole(obj))
+        return false;
+
+    // Guard on the shape, to prevent non-dense elements from appearing.
+    writer.guardShape(objId, obj->as<NativeObject>().lastProperty());
+
+    if (obj->hasUncacheableProto()) {
+        // If the shape does not imply the proto, emit an explicit proto guard.
+        writer.guardProto(objId, obj->staticPrototype());
+    }
+
+    JSObject* pobj = obj->staticPrototype();
+    while (pobj) {
+        ObjOperandId protoId = writer.loadObject(pobj);
+
+        // Non-singletons with uncacheable protos can change their proto
+        // without a shape change, so also guard on the group (which determines
+        // the proto) in this case.
+        if (pobj->hasUncacheableProto() && !pobj->isSingleton())
+            writer.guardGroup(protoId, pobj->group());
+
+        // Make sure the shape matches, to avoid non-dense elements or anything
+        // else that is being checked by CanAttachDenseElementHole.
+        writer.guardShape(protoId, pobj->as<NativeObject>().lastProperty());
+
+        // Also make sure there are no dense elements.
+        writer.guardNoDenseElements(protoId);
+
+        pobj = pobj->staticPrototype();
+    }
+
+    Int32OperandId int32IndexId = writer.guardIsInt32(indexId);
+    writer.loadDenseElementHoleResult(objId, int32IndexId);
+    writer.typeMonitorResult();
+    return true;
+}
+
 bool
 GetPropIRGenerator::tryAttachUnboxedArrayElement(HandleObject obj, ObjOperandId objId,
                                                  ValOperandId indexId)
 {
     MOZ_ASSERT(idVal_.isInt32());
 
     if (!obj->is<UnboxedArrayObject>())
         return false;
--- a/js/src/jit/CacheIR.h
+++ b/js/src/jit/CacheIR.h
@@ -140,31 +140,33 @@ enum class CacheKind : uint8_t
     _(GuardIsProxy)                       \
     _(GuardNotDOMProxy)                   \
     _(GuardSpecificObject)                \
     _(GuardSpecificAtom)                  \
     _(GuardSpecificSymbol)                \
     _(GuardNoDetachedTypedObjects)        \
     _(GuardMagicValue)                    \
     _(GuardFrameHasNoArgumentsObject)     \
+    _(GuardNoDenseElements)               \
     _(GuardNoUnboxedExpando)              \
     _(GuardAndLoadUnboxedExpando)         \
     _(LoadObject)                         \
     _(LoadProto)                          \
                                           \
     _(LoadDOMExpandoValue)                \
     _(GuardDOMExpandoObject)              \
     _(GuardDOMExpandoGeneration)          \
                                           \
     /* The *Result ops load a value into the cache's result register. */ \
     _(LoadFixedSlotResult)                \
     _(LoadDynamicSlotResult)              \
     _(LoadUnboxedPropertyResult)          \
     _(LoadTypedObjectResult)              \
     _(LoadDenseElementResult)             \
+    _(LoadDenseElementHoleResult)         \
     _(LoadUnboxedArrayElementResult)      \
     _(LoadTypedElementResult)             \
     _(LoadInt32ArrayLengthResult)         \
     _(LoadUnboxedArrayLengthResult)       \
     _(LoadArgumentsObjectArgResult)       \
     _(LoadArgumentsObjectLengthResult)    \
     _(LoadStringCharResult)               \
     _(LoadStringLengthResult)             \
@@ -445,16 +447,19 @@ class MOZ_RAII CacheIRWriter : public JS
         writeOp(CacheOp::LoadFrameCalleeResult);
     }
     void loadFrameNumActualArgsResult() {
         writeOp(CacheOp::LoadFrameNumActualArgsResult);
     }
     void loadFrameArgumentResult(Int32OperandId index) {
         writeOpWithOperandId(CacheOp::LoadFrameArgumentResult, index);
     }
+    void guardNoDenseElements(ObjOperandId obj) {
+        writeOpWithOperandId(CacheOp::GuardNoDenseElements, obj);
+    }
     void guardNoUnboxedExpando(ObjOperandId obj) {
         writeOpWithOperandId(CacheOp::GuardNoUnboxedExpando, obj);
     }
     ObjOperandId guardAndLoadUnboxedExpando(ObjOperandId obj) {
         ObjOperandId res(nextOperandId_++);
         writeOpWithOperandId(CacheOp::GuardAndLoadUnboxedExpando, obj);
         writeOperandId(res);
         return res;
@@ -529,16 +534,20 @@ class MOZ_RAII CacheIRWriter : public JS
     void loadArgumentsObjectArgResult(ObjOperandId obj, Int32OperandId index) {
         writeOpWithOperandId(CacheOp::LoadArgumentsObjectArgResult, obj);
         writeOperandId(index);
     }
     void loadDenseElementResult(ObjOperandId obj, Int32OperandId index) {
         writeOpWithOperandId(CacheOp::LoadDenseElementResult, obj);
         writeOperandId(index);
     }
+    void loadDenseElementHoleResult(ObjOperandId obj, Int32OperandId index) {
+        writeOpWithOperandId(CacheOp::LoadDenseElementHoleResult, obj);
+        writeOperandId(index);
+    }
     void loadUnboxedArrayElementResult(ObjOperandId obj, Int32OperandId index, JSValueType elementType) {
         writeOpWithOperandId(CacheOp::LoadUnboxedArrayElementResult, obj);
         writeOperandId(index);
         buffer_.writeByte(uint32_t(elementType));
     }
     void loadTypedElementResult(ObjOperandId obj, Int32OperandId index, TypedThingLayout layout,
                                 Scalar::Type elementType) {
         writeOpWithOperandId(CacheOp::LoadTypedElementResult, obj);
@@ -677,16 +686,17 @@ class MOZ_RAII GetPropIRGenerator
     bool tryAttachStringChar(ValOperandId valId, ValOperandId indexId);
     bool tryAttachStringLength(ValOperandId valId, HandleId id);
     bool tryAttachMagicArgumentsName(ValOperandId valId, HandleId id);
 
     bool tryAttachMagicArgument(ValOperandId valId, ValOperandId indexId);
     bool tryAttachArgumentsObjectArg(HandleObject obj, ObjOperandId objId, ValOperandId indexId);
 
     bool tryAttachDenseElement(HandleObject obj, ObjOperandId objId, ValOperandId indexId);
+    bool tryAttachDenseElementHole(HandleObject obj, ObjOperandId objId, ValOperandId indexId);
     bool tryAttachUnboxedArrayElement(HandleObject obj, ObjOperandId objId, ValOperandId indexId);
     bool tryAttachTypedElement(HandleObject obj, ObjOperandId objId, ValOperandId indexId);
 
     ValOperandId getElemKeyValueId() const {
         MOZ_ASSERT(cacheKind_ == CacheKind::GetElem);
         return ValOperandId(1);
     }
 
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -4507,16 +4507,17 @@ jit::MarkLoopBlocks(MIRGraph& graph, MBa
                    "Reached the end of the graph while searching for the loop header");
         MBasicBlock* block = *i;
         // If we've reached the loop header, we're done.
         if (block == header)
             break;
         // A block not marked by the time we reach it is not in the loop.
         if (!block->isMarked())
             continue;
+
         // This block is in the loop; trace to its predecessors.
         for (size_t p = 0, e = block->numPredecessors(); p != e; ++p) {
             MBasicBlock* pred = block->getPredecessor(p);
             if (pred->isMarked())
                 continue;
 
             // Blocks dominated by the OSR entry are not part of the loop
             // (unless they aren't reachable from the normal entry).
@@ -4541,17 +4542,17 @@ jit::MarkLoopBlocks(MIRGraph& graph, MBa
                 if (!innerBackedge->isMarked()) {
                     // Mark its backedge so that we add all of its blocks to the
                     // outer loop as we walk upwards.
                     innerBackedge->mark();
                     ++numMarked;
 
                     // If the nested loop is not contiguous, we may have already
                     // passed its backedge. If this happens, back up.
-                    if (backedge->id() > block->id()) {
+                    if (innerBackedge->id() > block->id()) {
                         i = graph.poBegin(innerBackedge);
                         --i;
                     }
                 }
             }
         }
     }
 
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -1502,18 +1502,20 @@ IonBuilder::visitBlock(const CFGBlock* c
     cfgCurrent = cfgblock;
     pc = cfgblock->startPc();
 
     if (mblock->pc() && script()->hasScriptCounts())
         mblock->setHitCount(script()->getHitCount(mblock->pc()));
 
     // Optimization to move a predecessor that only has this block as successor
     // just before this block.
-    if (mblock->numPredecessors() == 1 && mblock->getPredecessor(0)->numSuccessors() == 1)
-        graph().moveBlockToEnd(mblock->getPredecessor(0));
+    if (mblock->numPredecessors() == 1 && mblock->getPredecessor(0)->numSuccessors() == 1) {
+        graph().removeBlockFromList(mblock->getPredecessor(0));
+        graph().addBlock(mblock->getPredecessor(0));
+    }
 
     if (!setCurrentAndSpecializePhis(mblock))
         return false;
     graph().addBlock(mblock);
 
     while (pc < cfgblock->stopPc()) {
         if (!alloc().ensureBallast()) {
             abortReason_ = AbortReason_Alloc;
--- a/js/src/jit/arm/MacroAssembler-arm.h
+++ b/js/src/jit/arm/MacroAssembler-arm.h
@@ -1531,20 +1531,16 @@ class MacroAssemblerARMCompat : public M
         ma_ldr(Address(WasmTlsReg, offsetof(wasm::TlsData, memoryBase)), HeapReg, scratch);
         ma_ldr(Address(WasmTlsReg, offsetof(wasm::TlsData, globalData)), GlobalReg, scratch);
         ma_add(Imm32(WasmGlobalRegBias), GlobalReg, scratch);
     }
 
     // Instrumentation for entering and leaving the profiler.
     void profilerEnterFrame(Register framePtr, Register scratch);
     void profilerExitFrame();
-
-    struct AutoPrepareForPatching {
-        explicit AutoPrepareForPatching(MacroAssemblerARMCompat&) {}
-    };
 };
 
 typedef MacroAssemblerARMCompat MacroAssemblerSpecific;
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_arm_MacroAssembler_arm_h */
--- a/js/src/jit/arm64/MacroAssembler-arm64.h
+++ b/js/src/jit/arm64/MacroAssembler-arm64.h
@@ -2311,20 +2311,16 @@ class MacroAssemblerCompat : public vixl
     }
 
     // FIXME: Should be in Assembler?
     // FIXME: Should be const?
     uint32_t currentOffset() const {
         return nextOffset().getOffset();
     }
 
<