Merge inbound to m-c a=merge
authorWes Kocher <wkocher@mozilla.com>
Tue, 27 Jan 2015 16:02:52 -0800
changeset 226088 c261ba48dacbafa809c0614a0ef9c984f1e4ed46
parent 226039 dece5291d037f46d46bcd8a882a01878fcc52b18 (current diff)
parent 226087 d0116a0ee30dff3a4cdeecbb2681a6d195692c2c (diff)
child 226089 b2b10231606bb89fb75d11596abc49a9b6eecbc2
push id28185
push userkwierso@gmail.com
push dateWed, 28 Jan 2015 00:05:15 +0000
treeherdermozilla-central@b2b10231606b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone38.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to m-c a=merge
xpcom/tests/TestAtoms.cpp
xpcom/tests/TestUTF.cpp
xpcom/tests/UTFStrings.h
--- a/browser/devtools/webconsole/console-output.js
+++ b/browser/devtools/webconsole/console-output.js
@@ -891,23 +891,26 @@ Messages.Simple.prototype = Heritage.ext
    * @private
    * @return Element
    */
   _renderBody: function()
   {
     let body = this.document.createElementNS(XHTML_NS, "span");
     body.className = "message-body-wrapper message-body devtools-monospace";
 
-    let anchor, container = body;
+    let bodyInner = this.document.createElementNS(XHTML_NS, "span");
+    body.appendChild(bodyInner);
+
+    let anchor, container = bodyInner;
     if (this._link || this._linkCallback) {
       container = anchor = this.document.createElementNS(XHTML_NS, "a");
       anchor.href = this._link || "#";
       anchor.draggable = false;
       this._addLinkCallback(anchor, this._linkCallback);
-      body.appendChild(anchor);
+      bodyInner.appendChild(anchor);
     }
 
     if (typeof this._message == "function") {
       container.appendChild(this._message(this));
     } else if (this._message instanceof Ci.nsIDOMNode) {
       container.appendChild(this._message);
     } else {
       container.textContent = this._message;
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_587617_output_copy.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_587617_output_copy.js
@@ -52,17 +52,19 @@ function consoleOpened(aHud) {
     HUD.ui.output.selectMessage(msg);
 
     outputNode.focus();
 
     goUpdateCommand("cmd_copy");
     controller = top.document.commandDispatcher.getControllerForCommand("cmd_copy");
     is(controller.isCommandEnabled("cmd_copy"), true, "cmd_copy is enabled");
 
-    let selection = HUD.iframeWindow.getSelection() + "";
+    // Remove new lines since getSelection() includes one between message and line
+    // number, but the clipboard doesn't (see bug 1119503)
+    let selection = (HUD.iframeWindow.getSelection() + "").replace(/\r?\n|\r/g, " ");
     isnot(selection.indexOf("bug587617"), -1,
           "selection text includes 'bug587617'");
 
     waitForClipboard((str) => { return selection.trim() == str.trim(); },
       () => { goDoCommand("cmd_copy") },
       deferred.resolve, deferred.resolve);
   });
   return deferred.promise;
@@ -75,17 +77,19 @@ function testContextMenuCopy() {
 
   let contextMenuId = outputNode.parentNode.getAttribute("context");
   let contextMenu = HUD.ui.document.getElementById(contextMenuId);
   ok(contextMenu, "the output node has a context menu");
 
   let copyItem = contextMenu.querySelector("*[command='cmd_copy']");
   ok(copyItem, "the context menu on the output node has a \"Copy\" item");
 
-  let selection = HUD.iframeWindow.getSelection() + "";
+  // Remove new lines since getSelection() includes one between message and line
+  // number, but the clipboard doesn't (see bug 1119503)
+  let selection = (HUD.iframeWindow.getSelection() + "").replace(/\r?\n|\r/g, " ");
 
   copyItem.doCommand();
 
   waitForClipboard((str) => { return selection.trim() == str.trim(); },
     () => { goDoCommand("cmd_copy") },
     deferred.resolve, deferred.resolve);
   HUD = outputNode = null;
 
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_613280_jsterm_copy.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_613280_jsterm_copy.js
@@ -64,15 +64,17 @@ function performTest(HUD, [result]) {
   HUD.outputNode.focus();
 
   goUpdateCommand("cmd_copy");
 
   controller = top.document.commandDispatcher.
                getControllerForCommand("cmd_copy");
   is(controller.isCommandEnabled("cmd_copy"), true, "cmd_copy is enabled");
 
-  let selectionText = HUD.iframeWindow.getSelection() + "";
+  // Remove new lines since getSelection() includes one between message and line
+  // number, but the clipboard doesn't (see bug 1119503)
+  let selectionText = (HUD.iframeWindow.getSelection() + "").replace(/\r?\n|\r/g, " ");
   isnot(selectionText.indexOf("foobarBazBug613280"), -1,
         "selection text includes 'foobarBazBug613280'");
 
   waitForClipboard((str) => { return str.trim() == selectionText.trim(); },
                    clipboard_setup, clipboard_copy_done, clipboard_copy_done);
 }
--- a/browser/themes/shared/devtools/webconsole.inc.css
+++ b/browser/themes/shared/devtools/webconsole.inc.css
@@ -112,17 +112,17 @@ a {
 .message-location > .line-number {
   flex: none;
 }
 
 .message-flex-body {
   display: flex;
 }
 
-.message-body {
+.message-body > * {
   white-space: pre-wrap;
   word-wrap: break-word;
 }
 
 .message-flex-body > .message-body {
   display: block;
   flex: 1 1 auto;
   vertical-align: middle;
--- a/configure.in
+++ b/configure.in
@@ -4101,33 +4101,16 @@ fi
 if test "$BUILDING_RELEASE"; then
   # Override value in defines.sh, if any
   EARLY_BETA_OR_EARLIER=
 elif test "$EARLY_BETA_OR_EARLIER"; then
   AC_DEFINE(EARLY_BETA_OR_EARLIER)
 fi
 AC_SUBST(EARLY_BETA_OR_EARLIER)
 
-# Allow the application to provide a subconfigure script
-if test -f "${srcdir}/${MOZ_BUILD_APP}/configure.in" ; then
-  do_output_subdirs() {
-    if test -n "$_subconfigure_subdirs"; then
-      AC_MSG_ERROR([Cannot specify more than one sub-sub-configure])
-     fi
-    _subconfigure_subdir="$1"
-    _subconfigure_config_args="$ac_configure_args"
-  }
-  tmpscript=`$PYTHON -c 'import os, tempfile; print tempfile.mktemp(prefix="subscript.").replace(os.sep, "/")'` || exit 1
-  m4 "${srcdir}/build/autoconf/subconfigure.m4" \
-     "${srcdir}/build/autoconf/altoptions.m4" \
-     "${srcdir}/${MOZ_BUILD_APP}/configure.in" > $tmpscript
-  . $tmpscript
-  rm -f $tmpscript
-fi
-
 # Allow someone to change MOZ_APP_NAME and MOZ_APP_BASENAME in mozconfig
 MOZ_ARG_WITH_STRING(app-name,
 [--with-app-name=APPNAME sets MOZ_APP_NAME to APPNAME],
 WITH_APP_NAME=$withval,
 )
 
 if test -n "$WITH_APP_NAME" ; then
     MOZ_APP_NAME="$WITH_APP_NAME"
@@ -5959,17 +5942,16 @@ if test -n "$MOZ_ANGLE_RENDERER"; then
           MOZ_DIRECTX_SDK_PATH=
         fi
       else
         AC_MSG_RESULT([Couldn't determine the D3DX9 version for the DirectX SDK.])
         MOZ_DIRECTX_SDK_PATH=
       fi
     else
       AC_MSG_RESULT([Couldn't find an acceptable DirectX SDK for ANGLE, needed for d3dcompiler_43.])
-      AC_MSG_RESULT([  Either ignore, install DirectX SDK (June 2010 version or newer), or reconfigure with --disable-webgl.])
     fi
   fi
 
   ######################################
   # Check that we found what we needed.
   MOZ_FOUND_A_D3D_COMPILER=
   MOZ_FOUND_BOTH_D3D_COMPILERS=1
 
@@ -5985,17 +5967,16 @@ if test -n "$MOZ_ANGLE_RENDERER"; then
     AC_MSG_RESULT([Found d3dcompiler DLL for XP: $MOZ_D3DCOMPILER_XP_DLL])
   else
     MOZ_FOUND_BOTH_D3D_COMPILERS=
   fi
 
   if test -z "$CROSS_COMPILE"; then
     if test -z "MOZ_FOUND_A_D3D_COMPILER"; then
       AC_MSG_ERROR([Couldn't find an acceptable D3D compiler DLL.])
-      AC_MSG_ERROR([  Either install Windows SDK 8.0+, install DirectX SDK (June 2010 version or newer), or reconfigure with --disable-webgl.])
     fi
 
     if test -n "$MOZ_REQUIRE_ALL_D3DCS" -a -z "$MOZ_FOUND_BOTH_D3D_COMPILERS"; then
       AC_MSG_ERROR([Both D3D compilers _43 and _46+ are required by --enable-require-d3d-compilers.])
       AC_MSG_ERROR([  Install Windows SDK 8.0+, as well as DirectX SDK (June 2010 version or newer), or reconfigure without this flag.])
     fi
   fi
 fi
@@ -7252,16 +7233,35 @@ fi # MOZ_MEMORY
 AC_SUBST(MOZ_MEMORY)
 AC_SUBST(MOZ_JEMALLOC3)
 AC_SUBST(MOZ_NATIVE_JEMALLOC)
 AC_SUBST(MOZ_CRT)
 export MOZ_CRT
 AC_SUBST(MOZ_GLUE_IN_PROGRAM)
 AC_SUBST_LIST(WIN32_CRT_LIBS)
 
+# Allow the application to provide a subconfigure script.
+# This should be after 'export MOZ_NO_DEBUG_RTL=1' since
+# ldap/c-sdk/configure refers to the enviroment value.
+if test -f "${srcdir}/${MOZ_BUILD_APP}/configure.in" ; then
+  do_output_subdirs() {
+    if test -n "$_subconfigure_subdirs"; then
+      AC_MSG_ERROR([Cannot specify more than one sub-sub-configure])
+     fi
+    _subconfigure_subdir="$1"
+    _subconfigure_config_args="$ac_configure_args"
+  }
+  tmpscript=`$PYTHON -c 'import os, tempfile; print tempfile.mktemp(prefix="subscript.").replace(os.sep, "/")'` || exit 1
+  m4 "${srcdir}/build/autoconf/subconfigure.m4" \
+     "${srcdir}/build/autoconf/altoptions.m4" \
+     "${srcdir}/${MOZ_BUILD_APP}/configure.in" > $tmpscript
+  . $tmpscript
+  rm -f $tmpscript
+fi
+
 dnl We need to wrap dlopen and related functions on Android because we use
 dnl our own linker.
 if test "$OS_TARGET" = Android; then
     MOZ_GLUE_WRAP_LDFLAGS="${MOZ_GLUE_WRAP_LDFLAGS} -Wl,--wrap=PR_GetEnv,--wrap=PR_SetEnv"
     if test "$MOZ_WIDGET_TOOLKIT" = gonk -a -n "$MOZ_NUWA_PROCESS"; then
         MOZ_GLUE_WRAP_LDFLAGS="${MOZ_GLUE_WRAP_LDFLAGS} -Wl,--wrap=pthread_create,--wrap=epoll_wait,--wrap=poll,--wrap=pthread_cond_timedwait,--wrap=pthread_cond_wait,--wrap=epoll_create,--wrap=epoll_ctl,--wrap=close,--wrap=pthread_key_create,--wrap=pthread_key_delete,--wrap=socketpair,--wrap=pthread_self,--wrap=pthread_mutex_lock,--wrap=pthread_join,--wrap=pipe,--wrap=pipe2"
     fi
     if test "$MOZ_WIDGET_TOOLKIT" = android; then
--- a/dom/base/nsCopySupport.cpp
+++ b/dom/base/nsCopySupport.cpp
@@ -92,17 +92,18 @@ SelectionCopyHelper(nsISelection *aSel, 
   // is. if it is a selection into input/textarea element or in a html content
   // with pre-wrap style : text/plain. Otherwise text/html.
   // see nsHTMLCopyEncoder::SetSelection
   nsAutoString mimeType;
   mimeType.AssignLiteral(kUnicodeMime);
 
   // Do the first and potentially trial encoding as preformatted and raw.
   uint32_t flags = aFlags | nsIDocumentEncoder::OutputPreformatted
-                          | nsIDocumentEncoder::OutputRaw;
+                          | nsIDocumentEncoder::OutputRaw
+                          | nsIDocumentEncoder::OutputForPlainTextClipboardCopy;
 
   nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(aDoc);
   NS_ASSERTION(domDoc, "Need a document");
 
   rv = docEncoder->Init(domDoc, mimeType, flags);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = docEncoder->SetSelection(aSel);
--- a/dom/base/nsIDocumentEncoder.idl
+++ b/dom/base/nsIDocumentEncoder.idl
@@ -223,16 +223,23 @@ interface nsIDocumentEncoder : nsISuppor
   const unsigned long OutputNonTextContentAsPlaceholder = (1 << 23);
 
   /**
    * Don't Strip ending spaces from a line (only for serializing to plaintext).
    */
   const unsigned long OutputDontRemoveLineEndingSpaces = (1 << 24);
 
   /**
+   * Serialize in a way that is suitable for copying a plaintext version of the
+   * document to the clipboard.  This can for example cause line endings to be
+   * injected at preformatted block element boundaries.
+   */
+  const unsigned long OutputForPlainTextClipboardCopy = (1 << 25);
+
+  /**
    * Initialize with a pointer to the document and the mime type.
    * @param aDocument Document to encode.
    * @param aMimeType MimeType to use. May also be set by SetMimeType.
    * @param aFlags Flags to use while encoding. May also be set by SetFlags.
    */
   void init(in nsIDOMDocument aDocument,
             in AString aMimeType,
             in unsigned long aFlags);
--- a/dom/base/nsJSUtils.h
+++ b/dom/base/nsJSUtils.h
@@ -12,16 +12,17 @@
  * The goal of the utility functions is to cut down on the size of
  * the generated code itself.
  */
 
 #include "mozilla/Assertions.h"
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
+#include "js/Conversions.h"
 #include "nsString.h"
 
 class nsIScriptContext;
 class nsIScriptGlobalObject;
 
 namespace mozilla {
 namespace dom {
 class AutoJSAPI;
--- a/dom/base/nsPlainTextSerializer.cpp
+++ b/dom/base/nsPlainTextSerializer.cpp
@@ -86,16 +86,18 @@ nsPlainTextSerializer::nsPlainTextSerial
   mCurrentLineWidth = 0;
 
   // Flow
   mEmptyLines = 1; // The start of the document is an "empty line" in itself,
   mInWhitespace = false;
   mPreFormatted = false;
   mStartedOutput = false;
 
+  mPreformattedBlockBoundary = false;
+
   // initialize the tag stack to zero:
   // The stack only ever contains pointers to static atoms, so they don't
   // need refcounting.
   mTagStack = new nsIAtom*[TagStackSize];
   mTagStackIndex = 0;
   mIgnoreAboveIndex = (uint32_t)kNotFound;
 
   // initialize the OL stack, where numbers for ordered lists are kept
@@ -162,16 +164,18 @@ nsPlainTextSerializer::Init(uint32_t aFl
   else {
     // Platform/default
     mLineBreak.AssignLiteral(NS_LINEBREAK);
   }
 
   mLineBreakDue = false;
   mFloatingLines = -1;
 
+  mPreformattedBlockBoundary = false;
+
   if (mFlags & nsIDocumentEncoder::OutputFormatted) {
     // Get some prefs that controls how we do formatted output
     mStructs = Preferences::GetBool(PREF_STRUCTS, mStructs);
 
     mHeaderStrategy =
       Preferences::GetInt(PREF_HEADER_STRATEGY, mHeaderStrategy);
 
     // DontWrapAnyQuotes is set according to whether plaintext mail
@@ -432,16 +436,26 @@ nsPlainTextSerializer::DoOpenContainer(n
       // Serialize current node as placeholder character
       Write(NS_LITERAL_STRING("\xFFFC"));
     }
     // Ignore child nodes.
     mIgnoredChildNodeLevel++;
     return NS_OK;
   }
 
+  if (mFlags & nsIDocumentEncoder::OutputForPlainTextClipboardCopy) {
+    if (mPreformattedBlockBoundary && DoOutput()) {
+      // Should always end a line, but get no more whitespace
+      if (mFloatingLines < 0)
+        mFloatingLines = 0;
+      mLineBreakDue = true;
+    }
+    mPreformattedBlockBoundary = false;
+  }
+
   if (mFlags & nsIDocumentEncoder::OutputRaw) {
     // Raw means raw.  Don't even think about doing anything fancy
     // here like indenting, adding line breaks or any other
     // characters such as list item bullets, quote characters
     // around <q>, etc.  I mean it!  Don't make me smack you!
 
     return NS_OK;
   }
@@ -665,17 +679,17 @@ nsPlainTextSerializer::DoOpenContainer(n
     }
   }
   else if (aTag == nsGkAtoms::q) {
     Write(NS_LITERAL_STRING("\""));
   }
 
   // Else make sure we'll separate block level tags,
   // even if we're about to leave, before doing any other formatting.
-  else if (nsContentUtils::IsHTMLBlock(aTag)) {
+  else if (IsElementBlock(mElement)) {
     EnsureVerticalSpace(0);
   }
 
   //////////////////////////////////////////////////////////////
   if (!(mFlags & nsIDocumentEncoder::OutputFormatted)) {
     return NS_OK;
   }
   //////////////////////////////////////////////////////////////
@@ -762,16 +776,24 @@ nsPlainTextSerializer::DoOpenContainer(n
 nsresult
 nsPlainTextSerializer::DoCloseContainer(nsIAtom* aTag)
 {
   if (ShouldReplaceContainerWithPlaceholder(mElement->Tag())) {
     mIgnoredChildNodeLevel--;
     return NS_OK;
   }
 
+  if (mFlags & nsIDocumentEncoder::OutputForPlainTextClipboardCopy) {
+    if (DoOutput() && IsInPre() && IsElementBlock(mElement)) {
+      // If we're closing a preformatted block element, output a line break
+      // when we find a new container.
+      mPreformattedBlockBoundary = true;
+    }
+  }
+
   if (mFlags & nsIDocumentEncoder::OutputRaw) {
     // Raw means raw.  Don't even think about doing anything fancy
     // here like indenting, adding line breaks or any other
     // characters such as list item bullets, quote characters
     // around <q>, etc.  I mean it!  Don't make me smack you!
 
     return NS_OK;
   }
@@ -882,18 +904,17 @@ nsPlainTextSerializer::DoCloseContainer(
       mIndent -= kTabSize;
       mFloatingLines = 1;
     }
     mLineBreakDue = true;
   }
   else if (aTag == nsGkAtoms::q) {
     Write(NS_LITERAL_STRING("\""));
   }
-  else if (nsContentUtils::IsHTMLBlock(aTag)
-           && aTag != nsGkAtoms::script) {
+  else if (IsElementBlock(mElement) && aTag != nsGkAtoms::script) {
     // All other blocks get 1 vertical space after them
     // in formatted mode, otherwise 0.
     // This is hard. Sometimes 0 is a better number, but
     // how to know?
     if (mFlags & nsIDocumentEncoder::OutputFormatted)
       EnsureVerticalSpace(1);
     else {
       if (mFloatingLines < 0)
@@ -1032,16 +1053,18 @@ nsPlainTextSerializer::DoAddText(bool aI
     mURL.Truncate();
   }
   Write(aText);
 }
 
 nsresult
 nsPlainTextSerializer::DoAddLeaf(nsIAtom* aTag)
 {
+  mPreformattedBlockBoundary = false;
+
   // If we don't want any output, just return
   if (!DoOutput()) {
     return NS_OK;
   }
 
   if (mLineBreakDue)
     EnsureVerticalSpace(mFloatingLines);
 
@@ -1773,16 +1796,30 @@ nsPlainTextSerializer::IsElementPreforma
   if (styleContext) {
     const nsStyleText* textStyle = styleContext->StyleText();
     return textStyle->WhiteSpaceOrNewlineIsSignificant();
   }
   // Fall back to looking at the tag, in case there is no style information.
   return GetIdForContent(aElement) == nsGkAtoms::pre;
 }
 
+bool
+nsPlainTextSerializer::IsElementBlock(Element* aElement)
+{
+  nsRefPtr<nsStyleContext> styleContext =
+    nsComputedDOMStyle::GetStyleContextForElementNoFlush(aElement, nullptr,
+                                                         nullptr);
+  if (styleContext) {
+    const nsStyleDisplay* displayStyle = styleContext->StyleDisplay();
+    return displayStyle->IsBlockOutsideStyle();
+  }
+  // Fall back to looking at the tag, in case there is no style information.
+  return nsContentUtils::IsHTMLBlock(GetIdForContent(aElement));
+}
+
 /**
  * This method is required only to identify LI's inside OL.
  * Returns TRUE if we are inside an OL tag and FALSE otherwise.
  */
 bool
 nsPlainTextSerializer::IsInOL()
 {
   int32_t i = mTagStackIndex;
--- a/dom/base/nsPlainTextSerializer.h
+++ b/dom/base/nsPlainTextSerializer.h
@@ -110,16 +110,17 @@ private:
   bool GetLastBool(const nsTArray<bool>& aStack);
   void SetLastBool(nsTArray<bool>& aStack, bool aValue);
   void PushBool(nsTArray<bool>& aStack, bool aValue);
   bool PopBool(nsTArray<bool>& aStack);
 
   bool ShouldReplaceContainerWithPlaceholder(nsIAtom* aTag);
 
   bool IsElementPreformatted(mozilla::dom::Element* aElement);
+  bool IsElementBlock(mozilla::dom::Element* aElement);
 
 private:
   nsString         mCurrentLine;
   uint32_t         mHeadLevel;
   bool             mAtFirstColumn;
 
   // Handling of quoted text (for mail):
   // Quotes need to be wrapped differently from non-quoted text,
@@ -164,17 +165,19 @@ private:
 
   bool             mInWhitespace;
   bool             mPreFormatted;
   bool             mStartedOutput; // we've produced at least a character
 
   // While handling a new tag, this variable should remind if any line break
   // is due because of a closing tag. Setting it to "TRUE" while closing the tags.
   // Hence opening tags are guaranteed to start with appropriate line breaks.
-  bool             mLineBreakDue; 
+  bool             mLineBreakDue;
+
+  bool             mPreformattedBlockBoundary;
 
   nsString         mURL;
   int32_t          mHeaderStrategy;    /* Header strategy (pref)
                                           0 = no indention
                                           1 = indention, increased with
                                               header level (default)
                                           2 = numbering and slight indention */
   int32_t          mHeaderCounter[7];  /* For header-numbering:
--- a/dom/base/test/TestPlainTextSerializer.cpp
+++ b/dom/base/test/TestPlainTextSerializer.cpp
@@ -133,16 +133,41 @@ TestPreElement()
     return NS_ERROR_FAILURE;
   }
 
   passed("prettyprinted HTML to text serialization test");
   return NS_OK;
 }
 
 nsresult
+TestBlockElement()
+{
+  nsString test;
+  test.AppendLiteral(
+    "<html>" NS_LINEBREAK
+    "<body>" NS_LINEBREAK
+    "<div>" NS_LINEBREAK
+    "  first" NS_LINEBREAK
+    "</div>" NS_LINEBREAK
+    "<div>" NS_LINEBREAK
+    "  second" NS_LINEBREAK
+    "</div>" NS_LINEBREAK
+    "</body>" NS_LINEBREAK "</html>");
+
+  ConvertBufToPlainText(test, 0);
+  if (!test.EqualsLiteral("first" NS_LINEBREAK "second" NS_LINEBREAK)) {
+    fail("Wrong prettyprinted html to text serialization");
+    return NS_ERROR_FAILURE;
+  }
+
+  passed("prettyprinted HTML to text serialization test");
+  return NS_OK;
+}
+
+nsresult
 TestPlainTextSerializer()
 {
   nsString test;
   test.AppendLiteral("<html><base>base</base><head><span>span</span></head>"
                      "<body>body</body></html>");
   ConvertBufToPlainText(test, 0);
   if (!test.EqualsLiteral("basespanbody")) {
     fail("Wrong html to text serialization");
@@ -158,16 +183,19 @@ TestPlainTextSerializer()
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = TestPrettyPrintedHtml();
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = TestPreElement();
   NS_ENSURE_SUCCESS(rv, rv);
 
+  rv = TestBlockElement();
+  NS_ENSURE_SUCCESS(rv, rv);
+
   // Add new tests here...
   return NS_OK;
 }
 
 int main(int argc, char** argv)
 {
   ScopedXPCOM xpcom("PlainTextSerializer");
   if (xpcom.failed())
--- a/dom/base/test/test_bug116083.html
+++ b/dom/base/test/test_bug116083.html
@@ -15,16 +15,24 @@ https://bugzilla.mozilla.org/show_bug.cg
 <div style="white-space: pre">foo  bar</div>
 <div style="white-space: pre-wrap">foo  bar</div>
 <div style="white-space: pre-line">foo  bar</div>
 <div style="white-space: -moz-pre-space">foo  bar</div>
 <div data-result="bar  baz"><span style="white-space: pre">bar  </span>baz</div>
 <div data-result="bar  baz"><span style="white-space: pre-wrap">bar  </span>baz</div>
 <div data-result="bar  baz"><span style="white-space: pre-line">bar  </span>baz</div>
 <div data-result="bar  baz"><span style="white-space: -moz-pre-space">bar  </span>baz</div>
+<div data-result="foo  &#10;  bar&#10;&#10;!&#10;&#10;&#10;baz" style="white-space: pre"><div>foo  </div><div>  bar</div><div><br></div><div>!</div><div><br><br></div><div>baz</div></div>
+<div data-result="foo &#10; bar&#10;&#10;!&#10;&#10;&#10;baz" style="white-space: pre" contenteditable><div>foo </div><div> bar</div><div><br></div><div>!</div><div><br><br></div><div>baz</div></div>
+<div data-result="foo  &#10;  bar&#10;&#10;!&#10;&#10;&#10;baz" style="white-space: pre-wrap"><div>foo  </div><div>  bar</div><div><br></div><div>!</div><div><br><br></div><div>baz</div></div>
+<div data-result="foo &#10; bar&#10;&#10;!&#10;&#10;&#10;baz" style="white-space: pre-wrap" contenteditable><div>foo </div><div> bar</div><div><br></div><div>!</div><div><br><br></div><div>baz</div></div>
+<div data-result="foo  &#10;  bar&#10;&#10;!&#10;&#10;&#10;baz" style="white-space: pre-line"><div>foo  </div><div>  bar</div><div><br></div><div>!</div><div><br><br></div><div>baz</div></div>
+<div data-result="foo &#10; bar&#10;&#10;!&#10;&#10;&#10;baz" style="white-space: pre-line" contenteditable><div>foo </div><div> bar</div><div><br></div><div>!</div><div><br><br></div><div>baz</div></div>
+<div data-result="foo  &#10;  bar&#10;&#10;!&#10;&#10;&#10;baz" style="white-space: -moz-pre-space"><div>foo  </div><div>  bar</div><div><br></div><div>!</div><div><br><br></div><div>baz</div></div>
+<div data-result="foo &#10; bar&#10;&#10;!&#10;&#10;&#10;baz" style="white-space: -moz-pre-space" contenteditable><div>foo </div><div> bar</div><div><br></div><div>!</div><div><br><br></div><div>baz</div></div>
 <div data-result="&#10;foo bar&#10;">foo  bar</div>
 </div>
 <script type="application/javascript">
 
 SimpleTest.waitForExplicitFinish();
 SimpleTest.waitForFocus(function nextTest() {
   var div = document.querySelector("#content>div");
   if (!div) {
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -4,16 +4,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/. */
 
 #ifndef mozilla_dom_BindingUtils_h__
 #define mozilla_dom_BindingUtils_h__
 
 #include "jsfriendapi.h"
 #include "jswrapper.h"
+#include "js/Conversions.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Alignment.h"
 #include "mozilla/Array.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/CallbackObject.h"
 #include "mozilla/dom/DOMJSClass.h"
 #include "mozilla/dom/DOMJSProxyHandler.h"
--- a/dom/bindings/PrimitiveConversions.h
+++ b/dom/bindings/PrimitiveConversions.h
@@ -11,16 +11,17 @@
 #ifndef mozilla_dom_PrimitiveConversions_h
 #define mozilla_dom_PrimitiveConversions_h
 
 #include <limits>
 #include <math.h>
 #include <stdint.h>
 
 #include "jsapi.h"
+#include "js/Conversions.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/FloatingPoint.h"
 
 namespace mozilla {
 namespace dom {
 
 template<typename T>
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -63,16 +63,17 @@
 #include "nsIMemoryReporter.h"
 #include "nsStyleUtil.h"
 #include "CanvasImageCache.h"
 
 #include <algorithm>
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
+#include "js/Conversions.h"
 
 #include "mozilla/Alignment.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/ImageData.h"
 #include "mozilla/dom/PBrowserParent.h"
@@ -4815,20 +4816,20 @@ CanvasRenderingContext2D::GetImageData(J
     return nullptr;
   }
 
   if (!aSw || !aSh) {
     error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     return nullptr;
   }
 
-  int32_t x = JS_DoubleToInt32(aSx);
-  int32_t y = JS_DoubleToInt32(aSy);
-  int32_t wi = JS_DoubleToInt32(aSw);
-  int32_t hi = JS_DoubleToInt32(aSh);
+  int32_t x = JS::ToInt32(aSx);
+  int32_t y = JS::ToInt32(aSy);
+  int32_t wi = JS::ToInt32(aSw);
+  int32_t hi = JS::ToInt32(aSh);
 
   // Handle negative width and height by flipping the rectangle over in the
   // relevant direction.
   uint32_t w, h;
   if (aSw < 0) {
     w = -wi;
     x -= w;
   } else {
@@ -5011,39 +5012,39 @@ CanvasRenderingContext2D::FillRuleChange
 void
 CanvasRenderingContext2D::PutImageData(ImageData& imageData, double dx,
                                        double dy, ErrorResult& error)
 {
   dom::Uint8ClampedArray arr;
   DebugOnly<bool> inited = arr.Init(imageData.GetDataObject());
   MOZ_ASSERT(inited);
 
-  error = PutImageData_explicit(JS_DoubleToInt32(dx), JS_DoubleToInt32(dy),
+  error = PutImageData_explicit(JS::ToInt32(dx), JS::ToInt32(dy),
                                 imageData.Width(), imageData.Height(),
                                 &arr, false, 0, 0, 0, 0);
 }
 
 void
 CanvasRenderingContext2D::PutImageData(ImageData& imageData, double dx,
                                        double dy, double dirtyX,
                                        double dirtyY, double dirtyWidth,
                                        double dirtyHeight,
                                        ErrorResult& error)
 {
   dom::Uint8ClampedArray arr;
   DebugOnly<bool> inited = arr.Init(imageData.GetDataObject());
   MOZ_ASSERT(inited);
 
-  error = PutImageData_explicit(JS_DoubleToInt32(dx), JS_DoubleToInt32(dy),
+  error = PutImageData_explicit(JS::ToInt32(dx), JS::ToInt32(dy),
                                 imageData.Width(), imageData.Height(),
                                 &arr, true,
-                                JS_DoubleToInt32(dirtyX),
-                                JS_DoubleToInt32(dirtyY),
-                                JS_DoubleToInt32(dirtyWidth),
-                                JS_DoubleToInt32(dirtyHeight));
+                                JS::ToInt32(dirtyX),
+                                JS::ToInt32(dirtyY),
+                                JS::ToInt32(dirtyWidth),
+                                JS::ToInt32(dirtyHeight));
 }
 
 // void putImageData (in ImageData d, in float x, in float y);
 // void putImageData (in ImageData d, in double x, in double y, in double dirtyX, in double dirtyY, in double dirtyWidth, in double dirtyHeight);
 
 nsresult
 CanvasRenderingContext2D::PutImageData_explicit(int32_t x, int32_t y, uint32_t w, uint32_t h,
                                                 dom::Uint8ClampedArray* aArray,
@@ -5201,18 +5202,18 @@ already_AddRefed<ImageData>
 CanvasRenderingContext2D::CreateImageData(JSContext* cx, double sw,
                                           double sh, ErrorResult& error)
 {
   if (!sw || !sh) {
     error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     return nullptr;
   }
 
-  int32_t wi = JS_DoubleToInt32(sw);
-  int32_t hi = JS_DoubleToInt32(sh);
+  int32_t wi = JS::ToInt32(sw);
+  int32_t hi = JS::ToInt32(sh);
 
   uint32_t w = Abs(wi);
   uint32_t h = Abs(hi);
   return mozilla::dom::CreateImageData(cx, this, w, h, error);
 }
 
 already_AddRefed<ImageData>
 CanvasRenderingContext2D::CreateImageData(JSContext* cx,
--- a/dom/events/Event.h
+++ b/dom/events/Event.h
@@ -27,16 +27,22 @@ class nsPresContext;
 namespace mozilla {
 namespace dom {
 
 class EventTarget;
 class WantsPopupControlCheck;
 #define GENERATED_EVENT(EventClass_) class EventClass_;
 #include "mozilla/dom/GeneratedEventList.h"
 #undef GENERATED_EVENT
+// ExtendableEvent and InstallEvent are ServiceWorker events that are not
+// autogenerated since they have some extra methods.
+namespace workers {
+class ExtendableEvent;
+class InstallEvent;
+} // namespace workers
 
 // Dummy class so we can cast through it to get from nsISupports to
 // Event subclasses with only two non-ambiguous static casts.
 class EventBase : public nsIDOMEvent
 {
 };
 
 class Event : public EventBase,
@@ -99,16 +105,28 @@ public:
 #define GENERATED_EVENT(EventClass_) \
   virtual EventClass_* As##EventClass_()  \
   {                                       \
     return nullptr;                       \
   }
 #include "mozilla/dom/GeneratedEventList.h"
 #undef GENERATED_EVENT
 
+  // ExtendableEvent and InstallEvent are ServiceWorker events that are not
+  // autogenerated since they have some extra methods.
+  virtual workers::ExtendableEvent* AsExtendableEvent()
+  {
+    return nullptr;
+  }
+
+  virtual workers::InstallEvent* AsInstallEvent()
+  {
+    return nullptr;
+  }
+
   // nsIDOMEvent Interface
   NS_DECL_NSIDOMEVENT
 
   void InitPresContextData(nsPresContext* aPresContext);
 
   // Returns true if the event should be trusted.
   bool Init(EventTarget* aGlobal);
 
--- a/dom/media/eme/CDMProxy.cpp
+++ b/dom/media/eme/CDMProxy.cpp
@@ -410,16 +410,17 @@ CDMProxy::OnResolveLoadSessionPromise(ui
 }
 
 static dom::MediaKeyMessageType
 ToMediaKeyMessageType(GMPSessionMessageType aMessageType) {
   switch (aMessageType) {
     case kGMPLicenseRequest: return dom::MediaKeyMessageType::License_request;
     case kGMPLicenseRenewal: return dom::MediaKeyMessageType::License_renewal;
     case kGMPLicenseRelease: return dom::MediaKeyMessageType::License_release;
+    case kGMPIndividualizationRequest: return dom::MediaKeyMessageType::Individualization_request;
     default: return dom::MediaKeyMessageType::License_request;
   };
 };
 
 void
 CDMProxy::OnSessionMessage(const nsAString& aSessionId,
                            GMPSessionMessageType aMessageType,
                            nsTArray<uint8_t>& aMessage)
--- a/dom/media/fmp4/MP4Stream.cpp
+++ b/dom/media/fmp4/MP4Stream.cpp
@@ -23,22 +23,25 @@ MP4Stream::~MP4Stream()
   MOZ_ASSERT(mPinCount == 0);
 }
 
 bool
 MP4Stream::BlockingReadIntoCache(int64_t aOffset, size_t aCount, Monitor* aToUnlock)
 {
   MOZ_ASSERT(mPinCount > 0);
   CacheBlock block(aOffset, aCount);
+  if (!block.Init()) {
+    return false;
+  }
 
   uint32_t sum = 0;
   uint32_t bytesRead = 0;
   do {
     uint64_t offset = aOffset + sum;
-    char* buffer = reinterpret_cast<char*>(block.mBuffer.get()) + sum;
+    char* buffer = block.Buffer() + sum;
     uint32_t toRead = aCount - sum;
     MonitorAutoUnlock unlock(*aToUnlock);
     nsresult rv = mResource->ReadAt(offset, buffer, toRead, &bytesRead);
     if (NS_FAILED(rv)) {
       return false;
     }
     sum += bytesRead;
   } while (sum < aCount && bytesRead > 0);
@@ -71,17 +74,17 @@ MP4Stream::ReadAt(int64_t aOffset, void*
 
 bool
 MP4Stream::CachedReadAt(int64_t aOffset, void* aBuffer, size_t aCount,
                         size_t* aBytesRead)
 {
   // First, check our local cache.
   for (size_t i = 0; i < mCache.Length(); ++i) {
     if (mCache[i].mOffset == aOffset && mCache[i].mCount >= aCount) {
-      memcpy(aBuffer, mCache[i].mBuffer, aCount);
+      memcpy(aBuffer, mCache[i].Buffer(), aCount);
       *aBytesRead = aCount;
       return true;
     }
   }
 
   nsresult rv = mResource->ReadFromCache(reinterpret_cast<char*>(aBuffer),
                                          aOffset, aCount);
   if (NS_FAILED(rv)) {
--- a/dom/media/fmp4/MP4Stream.h
+++ b/dom/media/fmp4/MP4Stream.h
@@ -6,16 +6,17 @@
 
 #ifndef MP4_STREAM_H_
 #define MP4_STREAM_H_
 
 #include "mp4_demuxer/mp4_demuxer.h"
 
 #include "MediaResource.h"
 
+#include "mozilla/fallible.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/Monitor.h"
 
 namespace mozilla {
 
 class Monitor;
 
 class MP4Stream : public mp4_demuxer::Stream {
@@ -65,19 +66,33 @@ public:
 
 private:
   nsRefPtr<MediaResource> mResource;
   Maybe<ReadRecord> mFailedRead;
   uint32_t mPinCount;
 
   struct CacheBlock {
     CacheBlock(int64_t aOffset, size_t aCount)
-      : mOffset(aOffset), mCount(aCount), mBuffer(new uint8_t[aCount]) {}
+      : mOffset(aOffset), mCount(aCount), mBuffer(nullptr) {}
     int64_t mOffset;
     size_t mCount;
-    nsAutoArrayPtr<uint8_t> mBuffer;
+
+    bool Init()
+    {
+      mBuffer = new ((fallible_t())) char[mCount];
+      return !!mBuffer;
+    }
+
+    char* Buffer()
+    {
+      MOZ_ASSERT(mBuffer.get());
+      return mBuffer.get();
+    }
+
+  private:
+    nsAutoArrayPtr<char> mBuffer;
   };
   nsTArray<CacheBlock> mCache;
 };
 
 }
 
 #endif
--- a/dom/media/gmp-plugin/fake.info
+++ b/dom/media/gmp-plugin/fake.info
@@ -1,5 +1,5 @@
 Name: fake
 Description: Fake GMP Plugin
 Version: 1.0
-APIs: encode-video[h264], decode-video[h264], eme-decrypt-v3[fake]
+APIs: encode-video[h264], decode-video[h264], eme-decrypt-v4[fake]
 Libraries: dxva2.dll
--- a/dom/media/gmp/gmp-api/gmp-decryption.h
+++ b/dom/media/gmp/gmp-api/gmp-decryption.h
@@ -67,17 +67,18 @@ enum GMPDOMException {
   kGMPQuotaExceededError = 22,
   kGMPTimeoutError = 23
 };
 
 enum GMPSessionMessageType {
   kGMPLicenseRequest = 0,
   kGMPLicenseRenewal = 1,
   kGMPLicenseRelease = 2,
-  kGMPMessageInvalid = 3 // Must always be last.
+  kGMPIndividualizationRequest = 3,
+  kGMPMessageInvalid = 4 // Must always be last.
 };
 
 // Time in milliseconds, as offset from epoch, 1 Jan 1970.
 typedef int64_t GMPTimestamp;
 
 // Capability definitions. The capabilities of the EME GMP are reported
 // to Gecko by calling the GMPDecryptorCallback::SetCapabilities()
 // callback and specifying the logical OR of the GMP_EME_CAP_* flags below.
@@ -214,17 +215,17 @@ public:
 };
 
 enum GMPSessionType {
   kGMPTemporySession = 0,
   kGMPPersistentSession = 1,
   kGMPSessionInvalid = 2 // Must always be last.
 };
 
-#define GMP_API_DECRYPTOR "eme-decrypt-v3"
+#define GMP_API_DECRYPTOR "eme-decrypt-v4"
 
 // API exposed by plugin library to manage decryption sessions.
 // When the Host requests this by calling GMPGetAPIFunc().
 //
 // API name macro: GMP_API_DECRYPTOR
 // Host API: GMPDecryptorHost
 class GMPDecryptor {
 public:
--- a/dom/media/gstreamer/GStreamerReader.h
+++ b/dom/media/gstreamer/GStreamerReader.h
@@ -36,36 +36,36 @@ class AbstractMediaDecoder;
 class GStreamerReader : public MediaDecoderReader
 {
   typedef gfx::IntRect IntRect;
 
 public:
   explicit GStreamerReader(AbstractMediaDecoder* aDecoder);
   virtual ~GStreamerReader();
 
-  virtual nsresult Init(MediaDecoderReader* aCloneDonor);
-  virtual nsresult ResetDecode();
-  virtual bool DecodeAudioData();
+  virtual nsresult Init(MediaDecoderReader* aCloneDonor) MOZ_OVERRIDE;
+  virtual nsresult ResetDecode() MOZ_OVERRIDE;
+  virtual bool DecodeAudioData() MOZ_OVERRIDE;
   virtual bool DecodeVideoFrame(bool &aKeyframeSkip,
-                                int64_t aTimeThreshold);
+                                int64_t aTimeThreshold) MOZ_OVERRIDE;
   virtual nsresult ReadMetadata(MediaInfo* aInfo,
-                                MetadataTags** aTags);
+                                MetadataTags** aTags) MOZ_OVERRIDE;
   virtual nsRefPtr<SeekPromise>
   Seek(int64_t aTime, int64_t aEndTime) MOZ_OVERRIDE;
-  virtual nsresult GetBuffered(dom::TimeRanges* aBuffered);
+  virtual nsresult GetBuffered(dom::TimeRanges* aBuffered) MOZ_OVERRIDE;
 
   virtual void NotifyDataArrived(const char *aBuffer,
                                  uint32_t aLength,
                                  int64_t aOffset) MOZ_OVERRIDE;
 
-  virtual bool HasAudio() {
+  virtual bool HasAudio() MOZ_OVERRIDE {
     return mInfo.HasAudio();
   }
 
-  virtual bool HasVideo() {
+  virtual bool HasVideo() MOZ_OVERRIDE {
     return mInfo.HasVideo();
   }
 
   layers::ImageContainer* GetImageContainer() { return mDecoder->GetImageContainer(); }
 
   virtual bool IsMediaSeekable() MOZ_OVERRIDE;
 
 private:
--- a/dom/plugins/ipc/PluginModuleParent.h
+++ b/dom/plugins/ipc/PluginModuleParent.h
@@ -226,17 +226,17 @@ protected:
     virtual nsresult BeginUpdateBackground(NPP instance,
                                            const nsIntRect& aRect,
                                            gfxContext** aCtx) MOZ_OVERRIDE;
     virtual nsresult EndUpdateBackground(NPP instance,
                                          gfxContext* aCtx,
                                          const nsIntRect& aRect) MOZ_OVERRIDE;
 
 #if defined(XP_UNIX) && !defined(XP_MACOSX) && !defined(MOZ_WIDGET_GONK)
-    virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs, NPError* error);
+    virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs, NPError* error) MOZ_OVERRIDE;
 #else
     virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error) MOZ_OVERRIDE;
 #endif
     virtual nsresult NP_Shutdown(NPError* error) MOZ_OVERRIDE;
 
     virtual nsresult NP_GetMIMEDescription(const char** mimeDesc) MOZ_OVERRIDE;
     virtual nsresult NP_GetValue(void *future, NPPVariable aVariable,
                                  void *aValue, NPError* error) MOZ_OVERRIDE;
--- a/dom/webidl/InstallEvent.webidl
+++ b/dom/webidl/InstallEvent.webidl
@@ -11,11 +11,11 @@
  Exposed=ServiceWorker]
 interface InstallEvent : ExtendableEvent {
   readonly attribute ServiceWorker? activeWorker;
   void replace();
 };
 
 // Should be in the spec soon to satisfy conventions about events.
 // https://github.com/slightlyoff/ServiceWorker/issues/216.
-dictionary InstallEventInit : EventInit {
+dictionary InstallEventInit : ExtendableEventInit {
   ServiceWorker? activeWorker = null;
 };
--- a/dom/webidl/MediaKeyMessageEvent.webidl
+++ b/dom/webidl/MediaKeyMessageEvent.webidl
@@ -8,17 +8,18 @@
  *
  * Copyright © 2014 W3C® (MIT, ERCIM, Keio, Beihang), All Rights Reserved.
  * W3C liability, trademark and document use rules apply.
  */
 
 enum MediaKeyMessageType {
   "license-request",
   "license-renewal",
-  "license-release"
+  "license-release",
+  "individualization-request"
 };
 
 [Pref="media.eme.enabled", Constructor(DOMString type, optional MediaKeyMessageEventInit eventInitDict)]
 interface MediaKeyMessageEvent : Event {
   readonly attribute MediaKeyMessageType messageType;
   [Throws]
   readonly attribute ArrayBuffer message;
 };
--- a/dom/webidl/SVGClipPathElement.webidl
+++ b/dom/webidl/SVGClipPathElement.webidl
@@ -6,14 +6,16 @@
  * The origin of this IDL file is
  * http://www.w3.org/TR/SVG2/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 interface SVGClipPathElement : SVGElement {
+  [Constant]
   readonly attribute SVGAnimatedEnumeration clipPathUnits;
+  [Constant]
   readonly attribute SVGAnimatedTransformList transform;
 };
 
 SVGClipPathElement implements SVGUnitTypes;
 
--- a/dom/webidl/SVGComponentTransferFunctionElement.webidl
+++ b/dom/webidl/SVGComponentTransferFunctionElement.webidl
@@ -14,16 +14,23 @@ interface SVGComponentTransferFunctionEl
   // Component Transfer Types
   const unsigned short SVG_FECOMPONENTTRANSFER_TYPE_UNKNOWN = 0;
   const unsigned short SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY = 1;
   const unsigned short SVG_FECOMPONENTTRANSFER_TYPE_TABLE = 2;
   const unsigned short SVG_FECOMPONENTTRANSFER_TYPE_DISCRETE = 3;
   const unsigned short SVG_FECOMPONENTTRANSFER_TYPE_LINEAR = 4;
   const unsigned short SVG_FECOMPONENTTRANSFER_TYPE_GAMMA = 5;
 
+  [Constant]
   readonly attribute SVGAnimatedEnumeration type;
+  [Constant]
   readonly attribute SVGAnimatedNumberList tableValues;
+  [Constant]
   readonly attribute SVGAnimatedNumber slope;
+  [Constant]
   readonly attribute SVGAnimatedNumber intercept;
+  [Constant]
   readonly attribute SVGAnimatedNumber amplitude;
+  [Constant]
   readonly attribute SVGAnimatedNumber exponent;
+  [Constant]
   readonly attribute SVGAnimatedNumber offset;
 };
--- a/dom/webidl/SVGDocument.webidl
+++ b/dom/webidl/SVGDocument.webidl
@@ -5,11 +5,11 @@
  *
  * The origin of this IDL file is:
  * dom/interfaces/svg/nsIDOMSVGDocument.idl
  */
 
 interface SVGDocument : Document {
   [Throws]
   readonly attribute DOMString domain;
-  [Throws]
+  [Pure, Throws]
   readonly attribute SVGElement? rootElement;
 };
--- a/dom/webidl/SVGElement.webidl
+++ b/dom/webidl/SVGElement.webidl
@@ -10,16 +10,17 @@
  * liability, trademark and document use rules apply.
  */
 
 interface SVGElement : Element {
            attribute DOMString id;
 /*           [SetterThrows]
            attribute DOMString xmlbase; */
 
+  [Constant]
   readonly attribute SVGAnimatedString className;
   [PutForwards=cssText, Constant]
   readonly attribute CSSStyleDeclaration style;
 
   /*[SetterThrows]
   attribute DOMString xmllang;
   [SetterThrows]
   attribute DOMString xmlspace;*/
--- a/dom/webidl/SVGFEBlendElement.webidl
+++ b/dom/webidl/SVGFEBlendElement.webidl
@@ -25,14 +25,17 @@ interface SVGFEBlendElement : SVGElement
   const unsigned short SVG_FEBLEND_MODE_HARD_LIGHT = 9;
   const unsigned short SVG_FEBLEND_MODE_SOFT_LIGHT = 10;
   const unsigned short SVG_FEBLEND_MODE_DIFFERENCE = 11;
   const unsigned short SVG_FEBLEND_MODE_EXCLUSION = 12;
   const unsigned short SVG_FEBLEND_MODE_HUE = 13;
   const unsigned short SVG_FEBLEND_MODE_SATURATION = 14;
   const unsigned short SVG_FEBLEND_MODE_COLOR = 15;
   const unsigned short SVG_FEBLEND_MODE_LUMINOSITY = 16;
+  [Constant]
   readonly attribute SVGAnimatedString in1;
+  [Constant]
   readonly attribute SVGAnimatedString in2;
+  [Constant]
   readonly attribute SVGAnimatedEnumeration mode;
 };
 
 SVGFEBlendElement implements SVGFilterPrimitiveStandardAttributes;
--- a/dom/webidl/SVGFEColorMatrixElement.webidl
+++ b/dom/webidl/SVGFEColorMatrixElement.webidl
@@ -14,14 +14,17 @@ interface SVGFEColorMatrixElement : SVGE
 
   // Color Matrix Types
   const unsigned short SVG_FECOLORMATRIX_TYPE_UNKNOWN = 0;
   const unsigned short SVG_FECOLORMATRIX_TYPE_MATRIX = 1;
   const unsigned short SVG_FECOLORMATRIX_TYPE_SATURATE = 2;
   const unsigned short SVG_FECOLORMATRIX_TYPE_HUEROTATE = 3;
   const unsigned short SVG_FECOLORMATRIX_TYPE_LUMINANCETOALPHA = 4;
 
+  [Constant]
   readonly attribute SVGAnimatedString in1;
+  [Constant]
   readonly attribute SVGAnimatedEnumeration type;
+  [Constant]
   readonly attribute SVGAnimatedNumberList values;
 };
 
 SVGFEColorMatrixElement implements SVGFilterPrimitiveStandardAttributes;
--- a/dom/webidl/SVGFEComponentTransferElement.webidl
+++ b/dom/webidl/SVGFEComponentTransferElement.webidl
@@ -6,12 +6,13 @@
  * The origin of this IDL file is
  * http://www.w3.org/TR/SVG2/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 interface SVGFEComponentTransferElement : SVGElement {
+  [Constant]
   readonly attribute SVGAnimatedString in1;
 };
 
 SVGFEComponentTransferElement implements SVGFilterPrimitiveStandardAttributes;
--- a/dom/webidl/SVGFECompositeElement.webidl
+++ b/dom/webidl/SVGFECompositeElement.webidl
@@ -16,18 +16,25 @@ interface SVGFECompositeElement : SVGEle
   const unsigned short SVG_FECOMPOSITE_OPERATOR_UNKNOWN = 0;
   const unsigned short SVG_FECOMPOSITE_OPERATOR_OVER = 1;
   const unsigned short SVG_FECOMPOSITE_OPERATOR_IN = 2;
   const unsigned short SVG_FECOMPOSITE_OPERATOR_OUT = 3;
   const unsigned short SVG_FECOMPOSITE_OPERATOR_ATOP = 4;
   const unsigned short SVG_FECOMPOSITE_OPERATOR_XOR = 5;
   const unsigned short SVG_FECOMPOSITE_OPERATOR_ARITHMETIC = 6;
 
+  [Constant]
   readonly attribute SVGAnimatedString in1;
+  [Constant]
   readonly attribute SVGAnimatedString in2;
+  [Constant]
   readonly attribute SVGAnimatedEnumeration operator;
+  [Constant]
   readonly attribute SVGAnimatedNumber k1;
+  [Constant]
   readonly attribute SVGAnimatedNumber k2;
+  [Constant]
   readonly attribute SVGAnimatedNumber k3;
+  [Constant]
   readonly attribute SVGAnimatedNumber k4;
 };
 
 SVGFECompositeElement implements SVGFilterPrimitiveStandardAttributes;
--- a/dom/webidl/SVGFEConvolveMatrixElement.webidl
+++ b/dom/webidl/SVGFEConvolveMatrixElement.webidl
@@ -13,23 +13,35 @@
 interface SVGFEConvolveMatrixElement : SVGElement {
 
   // Edge Mode Values
   const unsigned short SVG_EDGEMODE_UNKNOWN = 0;
   const unsigned short SVG_EDGEMODE_DUPLICATE = 1;
   const unsigned short SVG_EDGEMODE_WRAP = 2;
   const unsigned short SVG_EDGEMODE_NONE = 3;
 
+  [Constant]
   readonly attribute SVGAnimatedString in1;
+  [Constant]
   readonly attribute SVGAnimatedInteger orderX;
+  [Constant]
   readonly attribute SVGAnimatedInteger orderY;
+  [Constant]
   readonly attribute SVGAnimatedNumberList kernelMatrix;
+  [Constant]
   readonly attribute SVGAnimatedNumber divisor;
+  [Constant]
   readonly attribute SVGAnimatedNumber bias;
+  [Constant]
   readonly attribute SVGAnimatedInteger targetX;
+  [Constant]
   readonly attribute SVGAnimatedInteger targetY;
+  [Constant]
   readonly attribute SVGAnimatedEnumeration edgeMode;
+  [Constant]
   readonly attribute SVGAnimatedNumber kernelUnitLengthX;
+  [Constant]
   readonly attribute SVGAnimatedNumber kernelUnitLengthY;
+  [Constant]
   readonly attribute SVGAnimatedBoolean preserveAlpha;
 };
 
 SVGFEConvolveMatrixElement implements SVGFilterPrimitiveStandardAttributes;
--- a/dom/webidl/SVGFEDiffuseLightingElement.webidl
+++ b/dom/webidl/SVGFEDiffuseLightingElement.webidl
@@ -6,16 +6,21 @@
  * The origin of this IDL file is
  * http://www.w3.org/TR/SVG2/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 interface SVGFEDiffuseLightingElement : SVGElement {
+  [Constant]
   readonly attribute SVGAnimatedString in1;
+  [Constant]
   readonly attribute SVGAnimatedNumber surfaceScale;
+  [Constant]
   readonly attribute SVGAnimatedNumber diffuseConstant;
+  [Constant]
   readonly attribute SVGAnimatedNumber kernelUnitLengthX;
+  [Constant]
   readonly attribute SVGAnimatedNumber kernelUnitLengthY;
 };
 
 SVGFEDiffuseLightingElement implements SVGFilterPrimitiveStandardAttributes;
--- a/dom/webidl/SVGFEDisplacementMapElement.webidl
+++ b/dom/webidl/SVGFEDisplacementMapElement.webidl
@@ -14,16 +14,21 @@ interface SVGFEDisplacementMapElement : 
 
   // Channel Selectors
   const unsigned short SVG_CHANNEL_UNKNOWN = 0;
   const unsigned short SVG_CHANNEL_R = 1;
   const unsigned short SVG_CHANNEL_G = 2;
   const unsigned short SVG_CHANNEL_B = 3;
   const unsigned short SVG_CHANNEL_A = 4;
 
+  [Constant]
   readonly attribute SVGAnimatedString in1;
+  [Constant]
   readonly attribute SVGAnimatedString in2;
+  [Constant]
   readonly attribute SVGAnimatedNumber scale;
+  [Constant]
   readonly attribute SVGAnimatedEnumeration xChannelSelector;
+  [Constant]
   readonly attribute SVGAnimatedEnumeration yChannelSelector;
 };
 
 SVGFEDisplacementMapElement implements SVGFilterPrimitiveStandardAttributes;
--- a/dom/webidl/SVGFEDistantLightElement.webidl
+++ b/dom/webidl/SVGFEDistantLightElement.webidl
@@ -6,11 +6,13 @@
  * The origin of this IDL file is
  * http://www.w3.org/TR/SVG2/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 interface SVGFEDistantLightElement : SVGElement {
+  [Constant]
   readonly attribute SVGAnimatedNumber azimuth;
+  [Constant]
   readonly attribute SVGAnimatedNumber elevation;
 };
--- a/dom/webidl/SVGFEDropShadowElement.webidl
+++ b/dom/webidl/SVGFEDropShadowElement.webidl
@@ -6,18 +6,23 @@
  * The origin of this IDL file is
  * http://www.w3.org/TR/SVG2/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 interface SVGFEDropShadowElement : SVGElement {
+  [Constant]
   readonly attribute SVGAnimatedString in1;
+  [Constant]
   readonly attribute SVGAnimatedNumber dx;
+  [Constant]
   readonly attribute SVGAnimatedNumber dy;
+  [Constant]
   readonly attribute SVGAnimatedNumber stdDeviationX;
+  [Constant]
   readonly attribute SVGAnimatedNumber stdDeviationY;
 
   void setStdDeviation(float stdDeviationX, float stdDeviationY);
 };
 
 SVGFEDropShadowElement implements SVGFilterPrimitiveStandardAttributes;
--- a/dom/webidl/SVGFEGaussianBlurElement.webidl
+++ b/dom/webidl/SVGFEGaussianBlurElement.webidl
@@ -6,16 +6,19 @@
  * The origin of this IDL file is
  * http://www.w3.org/TR/SVG2/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 interface SVGFEGaussianBlurElement : SVGElement {
+  [Constant]
   readonly attribute SVGAnimatedString in1;
+  [Constant]
   readonly attribute SVGAnimatedNumber stdDeviationX;
+  [Constant]
   readonly attribute SVGAnimatedNumber stdDeviationY;
 
   void setStdDeviation(float stdDeviationX, float stdDeviationY);
 };
 
 SVGFEGaussianBlurElement implements SVGFilterPrimitiveStandardAttributes;
--- a/dom/webidl/SVGFEImageElement.webidl
+++ b/dom/webidl/SVGFEImageElement.webidl
@@ -6,13 +6,14 @@
  * The origin of this IDL file is
  * http://www.w3.org/TR/SVG2/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 interface SVGFEImageElement : SVGElement {
+  [Constant]
   readonly attribute SVGAnimatedPreserveAspectRatio preserveAspectRatio;
 };
 
 SVGFEImageElement implements SVGFilterPrimitiveStandardAttributes;
 SVGFEImageElement implements SVGURIReference;
--- a/dom/webidl/SVGFEMergeNodeElement.webidl
+++ b/dom/webidl/SVGFEMergeNodeElement.webidl
@@ -6,10 +6,11 @@
  * The origin of this IDL file is
  * http://www.w3.org/TR/SVG2/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 interface SVGFEMergeNodeElement : SVGElement {
+  [Constant]
   readonly attribute SVGAnimatedString in1;
 };
--- a/dom/webidl/SVGFEMorphologyElement.webidl
+++ b/dom/webidl/SVGFEMorphologyElement.webidl
@@ -12,15 +12,19 @@
 
 interface SVGFEMorphologyElement : SVGElement {
 
   // Morphology Operators
   const unsigned short SVG_MORPHOLOGY_OPERATOR_UNKNOWN = 0;
   const unsigned short SVG_MORPHOLOGY_OPERATOR_ERODE = 1;
   const unsigned short SVG_MORPHOLOGY_OPERATOR_DILATE = 2;
 
+  [Constant]
   readonly attribute SVGAnimatedString in1;
+  [Constant]
   readonly attribute SVGAnimatedEnumeration operator;
+  [Constant]
   readonly attribute SVGAnimatedNumber radiusX;
+  [Constant]
   readonly attribute SVGAnimatedNumber radiusY;
 };
 
 SVGFEMorphologyElement implements SVGFilterPrimitiveStandardAttributes;
--- a/dom/webidl/SVGFEOffsetElement.webidl
+++ b/dom/webidl/SVGFEOffsetElement.webidl
@@ -6,14 +6,17 @@
  * The origin of this IDL file is
  * http://www.w3.org/TR/SVG2/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 interface SVGFEOffsetElement : SVGElement {
+  [Constant]
   readonly attribute SVGAnimatedString in1;
+  [Constant]
   readonly attribute SVGAnimatedNumber dx;
+  [Constant]
   readonly attribute SVGAnimatedNumber dy;
 };
 
 SVGFEOffsetElement implements SVGFilterPrimitiveStandardAttributes;
--- a/dom/webidl/SVGFEPointLightElement.webidl
+++ b/dom/webidl/SVGFEPointLightElement.webidl
@@ -6,12 +6,15 @@
  * The origin of this IDL file is
  * http://www.w3.org/TR/SVG2/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 interface SVGFEPointLightElement : SVGElement {
+  [Constant]
   readonly attribute SVGAnimatedNumber x;
+  [Constant]
   readonly attribute SVGAnimatedNumber y;
+  [Constant]
   readonly attribute SVGAnimatedNumber z;
 };
--- a/dom/webidl/SVGFESpecularLightingElement.webidl
+++ b/dom/webidl/SVGFESpecularLightingElement.webidl
@@ -6,17 +6,23 @@
  * The origin of this IDL file is
  * http://www.w3.org/TR/SVG2/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 interface SVGFESpecularLightingElement : SVGElement {
+  [Constant]
   readonly attribute SVGAnimatedString in1;
+  [Constant]
   readonly attribute SVGAnimatedNumber surfaceScale;
+  [Constant]
   readonly attribute SVGAnimatedNumber specularConstant;
+  [Constant]
   readonly attribute SVGAnimatedNumber specularExponent;
+  [Constant]
   readonly attribute SVGAnimatedNumber kernelUnitLengthX;
+  [Constant]
   readonly attribute SVGAnimatedNumber kernelUnitLengthY;
 };
 
 SVGFESpecularLightingElement implements SVGFilterPrimitiveStandardAttributes;
--- a/dom/webidl/SVGFESpotLightElement.webidl
+++ b/dom/webidl/SVGFESpotLightElement.webidl
@@ -6,17 +6,25 @@
  * The origin of this IDL file is
  * http://www.w3.org/TR/SVG2/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 interface SVGFESpotLightElement : SVGElement {
+  [Constant]
   readonly attribute SVGAnimatedNumber x;
+  [Constant]
   readonly attribute SVGAnimatedNumber y;
+  [Constant]
   readonly attribute SVGAnimatedNumber z;
+  [Constant]
   readonly attribute SVGAnimatedNumber pointsAtX;
+  [Constant]
   readonly attribute SVGAnimatedNumber pointsAtY;
+  [Constant]
   readonly attribute SVGAnimatedNumber pointsAtZ;
+  [Constant]
   readonly attribute SVGAnimatedNumber specularExponent;
+  [Constant]
   readonly attribute SVGAnimatedNumber limitingConeAngle;
 };
--- a/dom/webidl/SVGFETileElement.webidl
+++ b/dom/webidl/SVGFETileElement.webidl
@@ -6,12 +6,13 @@
  * The origin of this IDL file is
  * http://www.w3.org/TR/SVG2/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 interface SVGFETileElement : SVGElement {
+  [Constant]
   readonly attribute SVGAnimatedString in1;
 };
 
 SVGFETileElement implements SVGFilterPrimitiveStandardAttributes;
--- a/dom/webidl/SVGFETurbulenceElement.webidl
+++ b/dom/webidl/SVGFETurbulenceElement.webidl
@@ -17,17 +17,23 @@ interface SVGFETurbulenceElement : SVGEl
   const unsigned short SVG_TURBULENCE_TYPE_FRACTALNOISE = 1;
   const unsigned short SVG_TURBULENCE_TYPE_TURBULENCE = 2;
 
   // Stitch Options
   const unsigned short SVG_STITCHTYPE_UNKNOWN = 0;
   const unsigned short SVG_STITCHTYPE_STITCH = 1;
   const unsigned short SVG_STITCHTYPE_NOSTITCH = 2;
 
+  [Constant]
   readonly attribute SVGAnimatedNumber baseFrequencyX;
+  [Constant]
   readonly attribute SVGAnimatedNumber baseFrequencyY;
+  [Constant]
   readonly attribute SVGAnimatedInteger numOctaves;
+  [Constant]
   readonly attribute SVGAnimatedNumber seed;
+  [Constant]
   readonly attribute SVGAnimatedEnumeration stitchTiles;
+  [Constant]
   readonly attribute SVGAnimatedEnumeration type;
 };
 
 SVGFETurbulenceElement implements SVGFilterPrimitiveStandardAttributes;
--- a/dom/webidl/SVGFilterElement.webidl
+++ b/dom/webidl/SVGFilterElement.webidl
@@ -6,21 +6,27 @@
  * The origin of this IDL file is
  * http://www.w3.org/TR/SVG2/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 interface SVGFilterElement : SVGElement {
+  [Constant]
   readonly attribute SVGAnimatedEnumeration filterUnits;
+  [Constant]
   readonly attribute SVGAnimatedEnumeration primitiveUnits;
+  [Constant]
   readonly attribute SVGAnimatedLength x;
+  [Constant]
   readonly attribute SVGAnimatedLength y;
+  [Constant]
   readonly attribute SVGAnimatedLength width;
+  [Constant]
   readonly attribute SVGAnimatedLength height;
 
   // ImageData apply(ImageData source);
 };
 
 SVGFilterElement implements SVGURIReference;
 SVGFilterElement implements SVGUnitTypes;
 
--- a/dom/webidl/SVGFilterPrimitiveStandardAttributes.webidl
+++ b/dom/webidl/SVGFilterPrimitiveStandardAttributes.webidl
@@ -7,14 +7,19 @@
  * http://www.w3.org/TR/SVG2/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 [NoInterfaceObject]
 interface SVGFilterPrimitiveStandardAttributes {
+  [Constant]
   readonly attribute SVGAnimatedLength x;
+  [Constant]
   readonly attribute SVGAnimatedLength y;
+  [Constant]
   readonly attribute SVGAnimatedLength width;
+  [Constant]
   readonly attribute SVGAnimatedLength height;
+  [Constant]
   readonly attribute SVGAnimatedString result;
 };
--- a/dom/webidl/SVGGradientElement.webidl
+++ b/dom/webidl/SVGGradientElement.webidl
@@ -13,15 +13,18 @@
 interface SVGGradientElement : SVGElement {
 
   // Spread Method Types
   const unsigned short SVG_SPREADMETHOD_UNKNOWN = 0;
   const unsigned short SVG_SPREADMETHOD_PAD = 1;
   const unsigned short SVG_SPREADMETHOD_REFLECT = 2;
   const unsigned short SVG_SPREADMETHOD_REPEAT = 3;
 
+  [Constant]
   readonly attribute SVGAnimatedEnumeration gradientUnits;
+  [Constant]
   readonly attribute SVGAnimatedTransformList gradientTransform;
+  [Constant]
   readonly attribute SVGAnimatedEnumeration spreadMethod;
 };
 
 SVGGradientElement implements SVGURIReference;
 SVGGradientElement implements SVGUnitTypes;
--- a/dom/webidl/SVGLinearGradientElement.webidl
+++ b/dom/webidl/SVGLinearGradientElement.webidl
@@ -6,13 +6,17 @@
  * The origin of this IDL file is
  * https://svgwg.org/svg2-draft/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 interface SVGLinearGradientElement : SVGGradientElement {
+  [Constant]
   readonly attribute SVGAnimatedLength x1;
+  [Constant]
   readonly attribute SVGAnimatedLength y1;
+  [Constant]
   readonly attribute SVGAnimatedLength x2;
+  [Constant]
   readonly attribute SVGAnimatedLength y2;
 };
--- a/dom/webidl/SVGMarkerElement.webidl
+++ b/dom/webidl/SVGMarkerElement.webidl
@@ -17,22 +17,29 @@ interface SVGMarkerElement : SVGElement 
   const unsigned short SVG_MARKERUNITS_USERSPACEONUSE = 1;
   const unsigned short SVG_MARKERUNITS_STROKEWIDTH = 2;
 
   // Marker Orientation Types
   const unsigned short SVG_MARKER_ORIENT_UNKNOWN = 0;
   const unsigned short SVG_MARKER_ORIENT_AUTO = 1;
   const unsigned short SVG_MARKER_ORIENT_ANGLE = 2;
 
+  [Constant]
   readonly attribute SVGAnimatedLength refX;
+  [Constant]
   readonly attribute SVGAnimatedLength refY;
+  [Constant]
   readonly attribute SVGAnimatedEnumeration markerUnits;
+  [Constant]
   readonly attribute SVGAnimatedLength markerWidth;
+  [Constant]
   readonly attribute SVGAnimatedLength markerHeight;
+  [Constant]
   readonly attribute SVGAnimatedEnumeration orientType;
+  [Constant]
   readonly attribute SVGAnimatedAngle orientAngle;
 
   void setOrientToAuto();
   [Throws]
   void setOrientToAngle(SVGAngle angle);
 };
 
 SVGMarkerElement implements SVGFitToViewBox;
--- a/dom/webidl/SVGMaskElement.webidl
+++ b/dom/webidl/SVGMaskElement.webidl
@@ -11,18 +11,24 @@
  */
 
 interface SVGMaskElement : SVGElement {
 
   // Mask Types
   const unsigned short SVG_MASKTYPE_LUMINANCE = 0;
   const unsigned short SVG_MASKTYPE_ALPHA = 1;
 
+  [Constant]
   readonly attribute SVGAnimatedEnumeration maskUnits;
+  [Constant]
   readonly attribute SVGAnimatedEnumeration maskContentUnits;
+  [Constant]
   readonly attribute SVGAnimatedLength x;
+  [Constant]
   readonly attribute SVGAnimatedLength y;
+  [Constant]
   readonly attribute SVGAnimatedLength width;
+  [Constant]
   readonly attribute SVGAnimatedLength height;
 };
 
 SVGMaskElement implements SVGUnitTypes;
 
--- a/dom/webidl/SVGPathElement.webidl
+++ b/dom/webidl/SVGPathElement.webidl
@@ -6,16 +6,17 @@
  * The origin of this IDL file is
  * http://www.w3.org/TR/SVG2/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 interface SVGPathElement : SVGGraphicsElement {
 
+  [Constant]
   readonly attribute SVGAnimatedNumber pathLength;
 
   float getTotalLength();
   [NewObject, Throws]
   SVGPoint getPointAtLength(float distance);
   unsigned long getPathSegAtLength(float distance);
   [NewObject]
   SVGPathSegClosePath createSVGPathSegClosePath();
--- a/dom/webidl/SVGPathSeg.webidl
+++ b/dom/webidl/SVGPathSeg.webidl
@@ -29,17 +29,19 @@ interface SVGPathSeg {
   const unsigned short PATHSEG_LINETO_HORIZONTAL_REL = 13;
   const unsigned short PATHSEG_LINETO_VERTICAL_ABS = 14;
   const unsigned short PATHSEG_LINETO_VERTICAL_REL = 15;
   const unsigned short PATHSEG_CURVETO_CUBIC_SMOOTH_ABS = 16;
   const unsigned short PATHSEG_CURVETO_CUBIC_SMOOTH_REL = 17;
   const unsigned short PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS = 18;
   const unsigned short PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL = 19;
 
+  [Pure]
   readonly attribute unsigned short pathSegType;
+  [Pure]
   readonly attribute DOMString pathSegTypeAsLetter;
 };
 
 interface SVGPathSegClosePath : SVGPathSeg {
 };
 
 interface SVGPathSegMovetoAbs : SVGPathSeg {
   [SetterThrows]
--- a/dom/webidl/SVGRadialGradientElement.webidl
+++ b/dom/webidl/SVGRadialGradientElement.webidl
@@ -6,15 +6,20 @@
  * The origin of this IDL file is
  * https://svgwg.org/svg2-draft/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 interface SVGRadialGradientElement : SVGGradientElement {
+  [Constant]
   readonly attribute SVGAnimatedLength cx;
+  [Constant]
   readonly attribute SVGAnimatedLength cy;
+  [Constant]
   readonly attribute SVGAnimatedLength r;
+  [Constant]
   readonly attribute SVGAnimatedLength fx;
+  [Constant]
   readonly attribute SVGAnimatedLength fy;
   // readonly attribute SVGAnimatedLength fr;
 };
--- a/dom/webidl/SVGSVGElement.webidl
+++ b/dom/webidl/SVGSVGElement.webidl
@@ -9,19 +9,23 @@
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 interface SVGViewSpec;
 
 interface SVGSVGElement : SVGGraphicsElement {
 
+  [Constant]
   readonly attribute SVGAnimatedLength x;
+  [Constant]
   readonly attribute SVGAnimatedLength y;
+  [Constant]
   readonly attribute SVGAnimatedLength width;
+  [Constant]
   readonly attribute SVGAnimatedLength height;
   // readonly attribute SVGRect viewport;
   [Constant]
   readonly attribute float pixelUnitToMillimeterX;
   [Constant]
   readonly attribute float pixelUnitToMillimeterY;
   [Constant]
   readonly attribute float screenPixelToMillimeterX;
--- a/dom/webidl/SVGStopElement.webidl
+++ b/dom/webidl/SVGStopElement.webidl
@@ -6,11 +6,12 @@
  * The origin of this IDL file is
  * http://www.w3.org/TR/SVG2/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 interface SVGStopElement : SVGElement {
+  [Constant]
   readonly attribute SVGAnimatedNumber offset;
 };
 
--- a/dom/webidl/SVGTextContentElement.webidl
+++ b/dom/webidl/SVGTextContentElement.webidl
@@ -12,17 +12,19 @@
 
 interface SVGTextContentElement : SVGGraphicsElement {
 
   // lengthAdjust Types
   const unsigned short LENGTHADJUST_UNKNOWN = 0;
   const unsigned short LENGTHADJUST_SPACING = 1;
   const unsigned short LENGTHADJUST_SPACINGANDGLYPHS = 2;
 
+  [Constant]
   readonly attribute SVGAnimatedLength textLength;
+  [Constant]
   readonly attribute SVGAnimatedEnumeration lengthAdjust;
 
   long getNumberOfChars();
   float getComputedTextLength();
   [Throws]
   float getSubStringLength(unsigned long charnum, unsigned long nchars);
   [Throws]
   SVGPoint getStartPositionOfChar(unsigned long charnum);
--- a/dom/webidl/SVGTextPathElement.webidl
+++ b/dom/webidl/SVGTextPathElement.webidl
@@ -17,15 +17,18 @@ interface SVGTextPathElement : SVGTextCo
   const unsigned short TEXTPATH_METHODTYPE_ALIGN = 1;
   const unsigned short TEXTPATH_METHODTYPE_STRETCH = 2;
 
   // textPath Spacing Types
   const unsigned short TEXTPATH_SPACINGTYPE_UNKNOWN = 0;
   const unsigned short TEXTPATH_SPACINGTYPE_AUTO = 1;
   const unsigned short TEXTPATH_SPACINGTYPE_EXACT = 2;
 
+  [Constant]
   readonly attribute SVGAnimatedLength startOffset;
+  [Constant]
   readonly attribute SVGAnimatedEnumeration method;
+  [Constant]
   readonly attribute SVGAnimatedEnumeration spacing;
 };
 
 SVGTextPathElement implements SVGURIReference;
 
--- a/dom/webidl/SVGTextPositioningElement.webidl
+++ b/dom/webidl/SVGTextPositioningElement.webidl
@@ -6,15 +6,20 @@
  * The origin of this IDL file is
  * http://www.w3.org/TR/SVG2/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 interface SVGTextPositioningElement : SVGTextContentElement {
+  [Constant]
   readonly attribute SVGAnimatedLengthList x;
+  [Constant]
   readonly attribute SVGAnimatedLengthList y;
+  [Constant]
   readonly attribute SVGAnimatedLengthList dx;
+  [Constant]
   readonly attribute SVGAnimatedLengthList dy;
+  [Constant]
   readonly attribute SVGAnimatedNumberList rotate;
 };
 
--- a/dom/webidl/SVGURIReference.webidl
+++ b/dom/webidl/SVGURIReference.webidl
@@ -7,11 +7,12 @@
  * http://www.w3.org/TR/SVG2/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 [NoInterfaceObject]
 interface SVGURIReference {
+  [Constant]
   readonly attribute SVGAnimatedString href;
 };
 
--- a/dom/workers/ServiceWorkerContainer.cpp
+++ b/dom/workers/ServiceWorkerContainer.cpp
@@ -51,17 +51,20 @@ ServiceWorkerContainer::DisconnectFromOw
 
 void
 ServiceWorkerContainer::RemoveReadyPromise()
 {
   nsCOMPtr<nsPIDOMWindow> window = GetOwner();
   if (window) {
     nsCOMPtr<nsIServiceWorkerManager> swm =
       mozilla::services::GetServiceWorkerManager();
-    MOZ_ASSERT(swm);
+    if (!swm) {
+      // If the browser is shutting down, we don't need to remove the promise.
+      return;
+    }
 
     swm->RemoveReadyPromise(window);
   }
 }
 
 JSObject*
 ServiceWorkerContainer::WrapObject(JSContext* aCx)
 {
--- a/dom/workers/ServiceWorkerEvents.h
+++ b/dom/workers/ServiceWorkerEvents.h
@@ -64,16 +64,21 @@ public:
   WaitUntil(Promise& aPromise);
 
   already_AddRefed<Promise>
   GetPromise() const
   {
     nsRefPtr<Promise> p = mPromise;
     return p.forget();
   }
+
+  virtual ExtendableEvent* AsExtendableEvent() MOZ_OVERRIDE
+  {
+    return this;
+  }
 };
 
 class InstallEvent MOZ_FINAL : public ExtendableEvent
 {
   // FIXME(nsm): Bug 982787 will allow actually populating this.
   nsRefPtr<ServiceWorker> mActiveWorker;
   bool mActivateImmediately;
 
@@ -127,12 +132,17 @@ public:
     mActivateImmediately = true;
   };
 
   bool
   ActivateImmediately() const
   {
     return mActivateImmediately;
   }
+
+  InstallEvent* AsInstallEvent() MOZ_OVERRIDE
+  {
+    return this;
+  }
 };
 
 END_WORKERS_NAMESPACE
 #endif /* mozilla_dom_workers_serviceworkerevents_h__ */
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -101,36 +101,16 @@ ServiceWorkerRegistrationInfo::ServiceWo
 
 ServiceWorkerRegistrationInfo::~ServiceWorkerRegistrationInfo()
 {
   if (IsControllingDocuments()) {
     NS_WARNING("ServiceWorkerRegistrationInfo is still controlling documents. This can be a bug or a leak in ServiceWorker API or in any other API that takes the document alive.");
   }
 }
 
-class QueueFireUpdateFoundRunnable MOZ_FINAL : public nsRunnable
-{
-  nsRefPtr<ServiceWorkerRegistrationInfo> mRegistration;
-public:
-  explicit QueueFireUpdateFoundRunnable(ServiceWorkerRegistrationInfo* aReg)
-    : mRegistration(aReg)
-  {
-    MOZ_ASSERT(aReg);
-  }
-
-  NS_IMETHOD
-  Run()
-  {
-    nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
-    swm->FireEventOnServiceWorkerRegistrations(mRegistration,
-                                               NS_LITERAL_STRING("updatefound"));
-    return NS_OK;
-  }
-};
-
 //////////////////////////
 // ServiceWorkerManager //
 //////////////////////////
 
 NS_IMPL_ADDREF(ServiceWorkerManager)
 NS_IMPL_RELEASE(ServiceWorkerManager)
 
 NS_INTERFACE_MAP_BEGIN(ServiceWorkerManager)
@@ -158,70 +138,118 @@ ServiceWorkerManager::CleanupServiceWork
                                                       void *aUnused)
 {
   aDomainInfo->mServiceWorkerRegistrationInfos.Clear();
   return PL_DHASH_NEXT;
 }
 
 class ServiceWorkerRegisterJob;
 
-class FinishInstallRunnable MOZ_FINAL : public nsRunnable
+class ContinueLifecycleTask : public nsISupports
+{
+  NS_DECL_ISUPPORTS
+
+protected:
+  virtual ~ContinueLifecycleTask()
+  { }
+
+public:
+  virtual void ContinueAfterWorkerEvent(bool aSuccess,
+                                        bool aActivateImmediately) = 0;
+};
+
+NS_IMPL_ISUPPORTS0(ContinueLifecycleTask);
+
+class ContinueInstallTask MOZ_FINAL : public ContinueLifecycleTask
 {
-  nsMainThreadPtrHandle<nsISupports> mJob;
+  nsRefPtr<ServiceWorkerRegisterJob> mJob;
+
+public:
+  explicit ContinueInstallTask(ServiceWorkerRegisterJob* aJob)
+    : mJob(aJob)
+  { }
+
+  void ContinueAfterWorkerEvent(bool aSuccess, bool aActivateImmediately) MOZ_OVERRIDE;
+};
+
+class ContinueActivateTask MOZ_FINAL : public ContinueLifecycleTask
+{
+  nsRefPtr<ServiceWorkerRegistrationInfo> mRegistration;
+
+public:
+  explicit ContinueActivateTask(ServiceWorkerRegistrationInfo* aReg)
+    : mRegistration(aReg)
+  { }
+
+  void
+  ContinueAfterWorkerEvent(bool aSuccess, bool aActivateImmediately /* unused */) MOZ_OVERRIDE
+  {
+    mRegistration->FinishActivate(aSuccess);
+  }
+};
+
+class ContinueLifecycleRunnable MOZ_FINAL : public nsRunnable
+{
+  nsMainThreadPtrHandle<ContinueLifecycleTask> mTask;
   bool mSuccess;
   bool mActivateImmediately;
 
 public:
-  explicit FinishInstallRunnable(const nsMainThreadPtrHandle<nsISupports>& aJob,
-                                 bool aSuccess,
-                                 bool aActivateImmediately)
-    : mJob(aJob)
+  ContinueLifecycleRunnable(const nsMainThreadPtrHandle<ContinueLifecycleTask>& aTask,
+                            bool aSuccess,
+                            bool aActivateImmediately)
+    : mTask(aTask)
     , mSuccess(aSuccess)
     , mActivateImmediately(aActivateImmediately)
   {
     MOZ_ASSERT(!NS_IsMainThread());
   }
 
   NS_IMETHOD
-  Run() MOZ_OVERRIDE;
+  Run() MOZ_OVERRIDE
+  {
+    AssertIsOnMainThread();
+    mTask->ContinueAfterWorkerEvent(mSuccess, mActivateImmediately);
+    return NS_OK;
+  }
 };
 
 /*
  * Fires 'install' event on the ServiceWorkerGlobalScope. Modifies busy count
  * since it fires the event. This is ok since there can't be nested
  * ServiceWorkers, so the parent thread -> worker thread requirement for
  * runnables is satisfied.
  */
-class InstallEventRunnable MOZ_FINAL : public WorkerRunnable
+class LifecycleEventWorkerRunnable MOZ_FINAL : public WorkerRunnable
 {
-  nsMainThreadPtrHandle<nsISupports> mJob;
-  nsCString mScope;
+  nsString mEventName;
+  nsMainThreadPtrHandle<ContinueLifecycleTask> mTask;
 
 public:
-  InstallEventRunnable(WorkerPrivate* aWorkerPrivate,
-                       const nsMainThreadPtrHandle<nsISupports>& aJob,
-                       const nsCString& aScope)
-      : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount),
-        mJob(aJob),
-        mScope(aScope)
+  LifecycleEventWorkerRunnable(WorkerPrivate* aWorkerPrivate,
+                               const nsString& aEventName,
+                               const nsMainThreadPtrHandle<ContinueLifecycleTask>& aTask)
+      : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount)
+      , mEventName(aEventName)
+      , mTask(aTask)
   {
     AssertIsOnMainThread();
     MOZ_ASSERT(aWorkerPrivate);
   }
 
   bool
-  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
+  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
   {
     MOZ_ASSERT(aWorkerPrivate);
-    return DispatchInstallEvent(aCx, aWorkerPrivate);
+    return DispatchLifecycleEvent(aCx, aWorkerPrivate);
   }
 
 private:
   bool
-  DispatchInstallEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate);
+  DispatchLifecycleEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate);
 
 };
 
 class ServiceWorkerUpdateFinishCallback
 {
 protected:
   virtual ~ServiceWorkerUpdateFinishCallback()
   { }
@@ -351,17 +379,17 @@ public:
 
     return true;
   }
 };
 
 class ServiceWorkerRegisterJob MOZ_FINAL : public ServiceWorkerJob,
                                            public nsIStreamLoaderObserver
 {
-  friend class FinishInstallRunnable;
+  friend class ContinueInstallTask;
 
   nsCString mScope;
   nsCString mScriptSpec;
   nsRefPtr<ServiceWorkerRegistrationInfo> mRegistration;
   nsRefPtr<ServiceWorkerUpdateFinishCallback> mCallback;
 
   ~ServiceWorkerRegisterJob()
   { }
@@ -499,18 +527,17 @@ public:
   }
 
   // Public so our error handling code can use it.
   void
   Fail(const ErrorEventInit& aError)
   {
     MOZ_ASSERT(mCallback);
     mCallback->UpdateFailed(aError);
-    mCallback = nullptr;
-    Done(NS_ERROR_DOM_JS_EXCEPTION);
+    FailCommon(NS_ERROR_DOM_JS_EXCEPTION);
   }
 
   // Public so our error handling code can continue with a successful worker.
   void
   ContinueInstall()
   {
     nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
     nsRefPtr<ServiceWorkerManager::ServiceWorkerDomainInfo> domainInfo =
@@ -526,39 +553,38 @@ public:
 
     swm->InvalidateServiceWorkerRegistrationWorker(mRegistration,
                                                    WhichServiceWorker::INSTALLING_WORKER);
     mRegistration->mInstallingWorker = new ServiceWorkerInfo(mRegistration, mRegistration->mScriptSpec);
     mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Installing);
 
     Succeed();
 
-    nsRefPtr<QueueFireUpdateFoundRunnable> upr =
-      new QueueFireUpdateFoundRunnable(mRegistration);
+    nsCOMPtr<nsIRunnable> upr =
+      NS_NewRunnableMethodWithArg<ServiceWorkerRegistrationInfo*>(swm,
+                                                                  &ServiceWorkerManager::FireUpdateFound,
+                                                                  mRegistration);
     NS_DispatchToMainThread(upr);
 
-    // XXXnsm this leads to double fetches right now, ideally we'll be able to
-    // use the persistent cache later.
-    nsRefPtr<ServiceWorkerJob> upcasted = this;
-    nsMainThreadPtrHandle<nsISupports> handle(
-        new nsMainThreadPtrHolder<nsISupports>(upcasted));
+    nsMainThreadPtrHandle<ContinueLifecycleTask> handle(
+        new nsMainThreadPtrHolder<ContinueLifecycleTask>(new ContinueInstallTask(this)));
 
     nsRefPtr<ServiceWorker> serviceWorker;
     nsresult rv =
       swm->CreateServiceWorker(mRegistration->mInstallingWorker->ScriptSpec(),
                                mRegistration->mScope,
                                getter_AddRefs(serviceWorker));
 
     if (NS_WARN_IF(NS_FAILED(rv))) {
       ContinueAfterInstallEvent(false /* success */, false /* activate immediately */);
       return;
     }
 
-    nsRefPtr<InstallEventRunnable> r =
-      new InstallEventRunnable(serviceWorker->GetWorkerPrivate(), handle, mRegistration->mScope);
+    nsRefPtr<LifecycleEventWorkerRunnable> r =
+      new LifecycleEventWorkerRunnable(serviceWorker->GetWorkerPrivate(), NS_LITERAL_STRING("install"), handle);
 
     AutoJSAPI jsapi;
     jsapi.Init();
     r->Dispatch(jsapi.cx());
   }
 
 private:
   void
@@ -629,26 +655,49 @@ private:
   void
   Succeed()
   {
     MOZ_ASSERT(mCallback);
     mCallback->UpdateSucceeded(mRegistration);
     mCallback = nullptr;
   }
 
+  void
+  FailCommon(nsresult aRv)
+  {
+    mCallback = nullptr;
+    MaybeRemoveRegistration();
+    // Ensures that the job can't do anything useful from this point on.
+    mRegistration = nullptr;
+    Done(aRv);
+  }
+
   // This MUST only be called when the job is still performing actions related
   // to registration or update. After the spec resolves the update promise, use
   // Done() with the failure code instead.
   void
-  Fail(nsresult rv)
+  Fail(nsresult aRv)
   {
     MOZ_ASSERT(mCallback);
-    mCallback->UpdateFailed(rv);
-    mCallback = nullptr;
-    Done(rv);
+    mCallback->UpdateFailed(aRv);
+    FailCommon(aRv);
+  }
+
+  void
+  MaybeRemoveRegistration()
+  {
+    MOZ_ASSERT(mRegistration);
+    nsRefPtr<ServiceWorkerInfo> newest = mRegistration->Newest();
+    if (!newest) {
+      nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+      nsRefPtr<ServiceWorkerManager::ServiceWorkerDomainInfo> domainInfo =
+        swm->GetDomainInfo(mRegistration->mScope);
+      MOZ_ASSERT(domainInfo);
+      domainInfo->RemoveRegistration(mRegistration);
+    }
   }
 
   void
   ContinueAfterInstallEvent(bool aSuccess, bool aActivateImmediately)
   {
     // By this point the callback should've been notified about success or fail
     // and nulled.
     MOZ_ASSERT(!mCallback);
@@ -661,16 +710,17 @@ private:
     nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
 
     // "If installFailed is true"
     if (!aSuccess) {
       mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Redundant);
       mRegistration->mInstallingWorker = nullptr;
       swm->InvalidateServiceWorkerRegistrationWorker(mRegistration,
                                                      WhichServiceWorker::INSTALLING_WORKER);
+      MaybeRemoveRegistration();
       return Done(NS_ERROR_DOM_ABORT_ERR);
     }
 
     // "If registration's waiting worker is not null"
     if (mRegistration->mWaitingWorker) {
       // FIXME(nsm): Terminate
       mRegistration->mWaitingWorker->UpdateState(ServiceWorkerState::Redundant);
     }
@@ -700,16 +750,22 @@ ContinueUpdateRunnable::Run()
 {
   AssertIsOnMainThread();
   nsRefPtr<ServiceWorkerJob> job = static_cast<ServiceWorkerJob*>(mJob.get());
   nsRefPtr<ServiceWorkerRegisterJob> upjob = static_cast<ServiceWorkerRegisterJob*>(job.get());
   upjob->ContinueInstall();
   return NS_OK;
 }
 
+void
+ContinueInstallTask::ContinueAfterWorkerEvent(bool aSuccess, bool aActivateImmediately)
+{
+  mJob->ContinueAfterInstallEvent(aSuccess, aActivateImmediately);
+}
+
 // If we return an error code here, the ServiceWorkerContainer will
 // automatically reject the Promise.
 NS_IMETHODIMP
 ServiceWorkerManager::Register(nsIDOMWindow* aWindow,
                                const nsAString& aScope,
                                const nsAString& aScriptURL,
                                nsISupports** aPromise)
 {
@@ -848,82 +904,86 @@ ServiceWorkerManager::Register(nsIDOMWin
   nsRefPtr<ServiceWorkerRegisterJob> job =
     new ServiceWorkerRegisterJob(queue, cleanedScope, spec, cb);
   queue->Append(job);
 
   promise.forget(aPromise);
   return NS_OK;
 }
 
-NS_IMETHODIMP
-FinishInstallRunnable::Run()
+/*
+ * Used to handle ExtendableEvent::waitUntil() and proceed with
+ * installation/activation.
+ */
+class LifecycleEventPromiseHandler MOZ_FINAL : public PromiseNativeHandler
 {
-  AssertIsOnMainThread();
-  nsRefPtr<ServiceWorkerJob> job = static_cast<ServiceWorkerJob*>(mJob.get());
-  nsRefPtr<ServiceWorkerRegisterJob> upjob = static_cast<ServiceWorkerRegisterJob*>(job.get());
-  MOZ_ASSERT(upjob);
-  upjob->ContinueAfterInstallEvent(mSuccess, mActivateImmediately);
-  return NS_OK;
-}
-
-/*
- * Used to handle InstallEvent::waitUntil() and proceed with installation.
- */
-class FinishInstallHandler MOZ_FINAL : public PromiseNativeHandler
-{
-  nsMainThreadPtrHandle<nsISupports> mJob;
+  nsMainThreadPtrHandle<ContinueLifecycleTask> mTask;
   bool mActivateImmediately;
 
   virtual
-  ~FinishInstallHandler()
+  ~LifecycleEventPromiseHandler()
   { }
 
 public:
-  FinishInstallHandler(const nsMainThreadPtrHandle<nsISupports>& aJob,
-                       bool aActivateImmediately)
-    : mJob(aJob)
+  LifecycleEventPromiseHandler(const nsMainThreadPtrHandle<ContinueLifecycleTask>& aTask,
+                               bool aActivateImmediately)
+    : mTask(aTask)
     , mActivateImmediately(aActivateImmediately)
   {
     MOZ_ASSERT(!NS_IsMainThread());
   }
 
   void
   ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) MOZ_OVERRIDE
   {
     WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
     MOZ_ASSERT(workerPrivate);
     workerPrivate->AssertIsOnWorkerThread();
 
-    nsRefPtr<FinishInstallRunnable> r = new FinishInstallRunnable(mJob, true, mActivateImmediately);
+    nsRefPtr<ContinueLifecycleRunnable> r =
+      new ContinueLifecycleRunnable(mTask, true /* success */, mActivateImmediately);
     NS_DispatchToMainThread(r);
   }
 
   void
   RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) MOZ_OVERRIDE
   {
-    nsRefPtr<FinishInstallRunnable> r = new FinishInstallRunnable(mJob, false, mActivateImmediately);
+    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+    MOZ_ASSERT(workerPrivate);
+    workerPrivate->AssertIsOnWorkerThread();
+
+    nsRefPtr<ContinueLifecycleRunnable> r =
+      new ContinueLifecycleRunnable(mTask, false /* success */, mActivateImmediately);
     NS_DispatchToMainThread(r);
   }
 };
 
 bool
-InstallEventRunnable::DispatchInstallEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
+LifecycleEventWorkerRunnable::DispatchLifecycleEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
 {
   aWorkerPrivate->AssertIsOnWorkerThread();
   MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
-  InstallEventInit init;
-  init.mBubbles = false;
-  init.mCancelable = true;
+
+  nsRefPtr<ExtendableEvent> event;
+  nsRefPtr<EventTarget> target = aWorkerPrivate->GlobalScope();
 
-  // FIXME(nsm): Bug 982787 pass previous active worker.
-
-  // FIXME(nsm): Set error handler so we can grab handler errors.
-  nsRefPtr<EventTarget> target = aWorkerPrivate->GlobalScope();
-  nsRefPtr<InstallEvent> event =
-    InstallEvent::Constructor(target, NS_LITERAL_STRING("install"), init);
+  if (mEventName.EqualsASCII("install")) {
+    // FIXME(nsm): Bug 982787 pass previous active worker.
+    InstallEventInit init;
+    init.mBubbles = false;
+    init.mCancelable = true;
+    event = InstallEvent::Constructor(target, mEventName, init);
+  } else if (mEventName.EqualsASCII("activate")) {
+    ExtendableEventInit init;
+    init.mBubbles = false;
+    init.mCancelable = true;
+    event = ExtendableEvent::Constructor(target, mEventName, init);
+  } else {
+    MOZ_CRASH("Unexpected lifecycle event");
+  }
 
   event->SetTrusted(true);
 
   nsRefPtr<Promise> waitUntilPromise;
 
   ErrorResult result;
   result = target->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
 
@@ -947,145 +1007,31 @@ InstallEventRunnable::DispatchInstallEve
     waitUntilPromise = Promise::Reject(sgo, aCx,
                                        JS::UndefinedHandleValue, result);
   }
 
   if (result.Failed()) {
     return false;
   }
 
-  nsRefPtr<FinishInstallHandler> handler =
-    new FinishInstallHandler(mJob, event->ActivateImmediately());
+  // activateimmediately is only relevant to "install" event.
+  bool activateImmediately = false;
+  InstallEvent* installEvent = event->AsInstallEvent();
+  if (installEvent) {
+    activateImmediately = installEvent->ActivateImmediately();
+    // FIXME(nsm): Set activeWorker to the correct thing.
+    // FIXME(nsm): Install error handler for any listener errors.
+  }
+
+  nsRefPtr<LifecycleEventPromiseHandler> handler =
+    new LifecycleEventPromiseHandler(mTask, activateImmediately);
   waitUntilPromise->AppendNativeHandler(handler);
   return true;
 }
 
-class FinishActivationRunnable MOZ_FINAL : public nsRunnable
-{
-  bool mSuccess;
-  nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> mRegistration;
-
-public:
-  FinishActivationRunnable(bool aSuccess,
-                           const nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration)
-    : mSuccess(aSuccess)
-    , mRegistration(aRegistration)
-  {
-    MOZ_ASSERT(!NS_IsMainThread());
-  }
-
-  NS_IMETHODIMP
-  Run()
-  {
-    AssertIsOnMainThread();
-
-    mRegistration->FinishActivate(mSuccess);
-    return NS_OK;
-  }
-};
-
-class FinishActivateHandler MOZ_FINAL : public PromiseNativeHandler
-{
-  nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> mRegistration;
-
-public:
-  explicit FinishActivateHandler(const nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration)
-    : mRegistration(aRegistration)
-  {
-    MOZ_ASSERT(!NS_IsMainThread());
-  }
-
-  virtual
-  ~FinishActivateHandler()
-  { }
-
-  void
-  ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) MOZ_OVERRIDE
-  {
-    nsRefPtr<FinishActivationRunnable> r = new FinishActivationRunnable(true /* success */, mRegistration);
-    NS_DispatchToMainThread(r);
-  }
-
-  void
-  RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) MOZ_OVERRIDE
-  {
-    nsRefPtr<FinishActivationRunnable> r = new FinishActivationRunnable(false /* success */, mRegistration);
-    NS_DispatchToMainThread(r);
-  }
-};
-
-class ActivateEventRunnable : public WorkerRunnable
-{
-  nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> mRegistration;
-
-public:
-  ActivateEventRunnable(WorkerPrivate* aWorkerPrivate,
-                        const nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration)
-      : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount),
-        mRegistration(aRegistration)
-  {
-    MOZ_ASSERT(aWorkerPrivate);
-  }
-
-  bool
-  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
-  {
-    MOZ_ASSERT(aWorkerPrivate);
-    return DispatchActivateEvent(aCx, aWorkerPrivate);
-  }
-
-private:
-  bool
-  DispatchActivateEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
-  {
-    MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
-    nsRefPtr<EventTarget> target = do_QueryObject(aWorkerPrivate->GlobalScope());
-
-    // FIXME(nsm): Set activeWorker to the correct thing.
-    EventInit init;
-    init.mBubbles = false;
-    init.mCancelable = true;
-    nsRefPtr<ExtendableEvent> event =
-      ExtendableEvent::Constructor(target, NS_LITERAL_STRING("activate"), init);
-
-    event->SetTrusted(true);
-
-    nsRefPtr<Promise> waitUntilPromise;
-
-    // FIXME(nsm): Install error handler for any listener errors.
-    ErrorResult result;
-    result = target->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
-    WidgetEvent* internalEvent = event->GetInternalNSEvent();
-    if (!result.Failed() && !internalEvent->mFlags.mExceptionHasBeenRisen) {
-      waitUntilPromise = event->GetPromise();
-      if (!waitUntilPromise) {
-        nsCOMPtr<nsIGlobalObject> global =
-          do_QueryObject(aWorkerPrivate->GlobalScope());
-        waitUntilPromise =
-          Promise::Resolve(global,
-                           aCx, JS::UndefinedHandleValue, result);
-      }
-    } else {
-      nsCOMPtr<nsIGlobalObject> global =
-        do_QueryObject(aWorkerPrivate->GlobalScope());
-      // Continue with a canceled install.
-      waitUntilPromise = Promise::Reject(global, aCx,
-                                         JS::UndefinedHandleValue, result);
-    }
-
-    if (result.Failed()) {
-      return false;
-    }
-
-    nsRefPtr<FinishActivateHandler> handler = new FinishActivateHandler(mRegistration);
-    waitUntilPromise->AppendNativeHandler(handler);
-    return true;
-  }
-};
-
 void
 ServiceWorkerRegistrationInfo::TryToActivate()
 {
   mWaitingToActivate = true;
   if (!IsControllingDocuments()) {
     Activate();
   }
 }
@@ -1133,21 +1079,21 @@ ServiceWorkerRegistrationInfo::Activate(
     swm->CreateServiceWorker(mActiveWorker->ScriptSpec(),
                              mScope,
                              getter_AddRefs(serviceWorker));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     FinishActivate(false /* success */);
     return;
   }
 
-  nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> handle(
-    new nsMainThreadPtrHolder<ServiceWorkerRegistrationInfo>(this));
+  nsMainThreadPtrHandle<ContinueLifecycleTask> handle(
+    new nsMainThreadPtrHolder<ContinueLifecycleTask>(new ContinueActivateTask(this)));
 
-  nsRefPtr<ActivateEventRunnable> r =
-    new ActivateEventRunnable(serviceWorker->GetWorkerPrivate(), handle);
+  nsRefPtr<LifecycleEventWorkerRunnable> r =
+    new LifecycleEventWorkerRunnable(serviceWorker->GetWorkerPrivate(), NS_LITERAL_STRING("activate"), handle);
 
   AutoJSAPI jsapi;
   jsapi.Init();
   r->Dispatch(jsapi.cx());
 }
 
 /*
  * Implements the async aspects of the getRegistrations algorithm.
@@ -1470,95 +1416,127 @@ ServiceWorkerManager::CheckReadyPromise(
       new ServiceWorkerRegistration(aWindow, scope);
     aPromise->MaybeResolve(swr);
     return true;
   }
 
   return false;
 }
 
+class ServiceWorkerUnregisterJob MOZ_FINAL : public ServiceWorkerJob
+{
+  nsRefPtr<ServiceWorkerRegistrationInfo> mRegistration;
+  const nsCString mScope;
+  nsCOMPtr<nsIServiceWorkerUnregisterCallback> mCallback;
+
+  ~ServiceWorkerUnregisterJob()
+  { }
+
+public:
+  ServiceWorkerUnregisterJob(ServiceWorkerJobQueue* aQueue,
+                             const nsACString& aScope,
+                             nsIServiceWorkerUnregisterCallback* aCallback)
+    : ServiceWorkerJob(aQueue)
+    , mScope(aScope)
+    , mCallback(aCallback)
+  {
+    AssertIsOnMainThread();
+  }
+
+  void
+  Start() MOZ_OVERRIDE
+  {
+    AssertIsOnMainThread();
+    nsCOMPtr<nsIRunnable> r =
+      NS_NewRunnableMethod(this, &ServiceWorkerUnregisterJob::UnregisterAndDone);
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(r)));
+  }
+
+private:
+  // You probably want UnregisterAndDone().
+  nsresult
+  Unregister()
+  {
+    AssertIsOnMainThread();
+
+    nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+
+    nsRefPtr<ServiceWorkerManager::ServiceWorkerDomainInfo> domainInfo =
+      swm->GetDomainInfo(mScope);
+    MOZ_ASSERT(domainInfo);
+
+    // "Let registration be the result of running [[Get Registration]]
+    // algorithm passing scope as the argument."
+    nsRefPtr<ServiceWorkerRegistrationInfo> registration;
+    if (!domainInfo->mServiceWorkerRegistrationInfos.Get(mScope,
+                                                         getter_AddRefs(registration))) {
+      // "If registration is null, then, resolve promise with false."
+      return mCallback->UnregisterSucceeded(false);
+    }
+
+    MOZ_ASSERT(registration);
+
+    // "Set registration's uninstalling flag."
+    registration->mPendingUninstall = true;
+    // "Resolve promise with true"
+    nsresult rv = mCallback->UnregisterSucceeded(true);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    // "If no service worker client is using registration..."
+    if (!registration->IsControllingDocuments()) {
+      // "If registration's uninstalling flag is set.."
+      if (!registration->mPendingUninstall) {
+        return NS_OK;
+      }
+
+      // "Invoke [[Clear Registration]]..."
+      registration->Clear();
+      domainInfo->RemoveRegistration(registration);
+    }
+
+    return NS_OK;
+  }
+
+  // The unregister job is done irrespective of success or failure of any sort.
+  void
+  UnregisterAndDone()
+  {
+    Done(Unregister());
+  }
+};
+
 NS_IMETHODIMP
 ServiceWorkerManager::Unregister(nsIServiceWorkerUnregisterCallback* aCallback,
                                  const nsAString& aScope)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(aCallback);
 
+// This is not accessible by content, and callers should always ensure scope is
+// a correct URI, so this is wrapped in DEBUG
+#ifdef DEBUG
   nsCOMPtr<nsIURI> scopeURI;
   nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, nullptr);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
-
-  /*
-   * Implements the async aspects of the unregister algorithm.
-   */
-  class UnregisterRunnable : public nsRunnable
-  {
-    nsCOMPtr<nsIServiceWorkerUnregisterCallback> mCallback;
-    nsCOMPtr<nsIURI> mScopeURI;
-
-  public:
-    UnregisterRunnable(nsIServiceWorkerUnregisterCallback* aCallback,
-                       nsIURI* aScopeURI)
-      : mCallback(aCallback), mScopeURI(aScopeURI)
-    {
-      AssertIsOnMainThread();
-    }
-
-    NS_IMETHODIMP
-    Run()
-    {
-      AssertIsOnMainThread();
-
-      nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
-
-      nsRefPtr<ServiceWorkerManager::ServiceWorkerDomainInfo> domainInfo =
-        swm->GetDomainInfo(mScopeURI);
-      MOZ_ASSERT(domainInfo);
-
-      nsCString spec;
-      nsresult rv = mScopeURI->GetSpecIgnoringRef(spec);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return mCallback->UnregisterFailed();
-      }
+#endif
 
-      nsRefPtr<ServiceWorkerRegistrationInfo> registration;
-      if (!domainInfo->mServiceWorkerRegistrationInfos.Get(spec,
-                                                           getter_AddRefs(registration))) {
-        return mCallback->UnregisterSucceeded(false);
-      }
-
-      MOZ_ASSERT(registration);
-
-      registration->mPendingUninstall = true;
-      rv = mCallback->UnregisterSucceeded(true);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
+  NS_ConvertUTF16toUTF8 scope(aScope);
+  nsRefPtr<ServiceWorkerManager::ServiceWorkerDomainInfo> domainInfo =
+    GetDomainInfo(scope);
+  ServiceWorkerJobQueue* queue = domainInfo->GetOrCreateJobQueue(scope);
+  MOZ_ASSERT(queue);
 
-      // The "Wait until no document is using registration" can actually be
-      // handled by [[HandleDocumentUnload]] in Bug 1041340, so we simply check
-      // if the document is currently in use here.
-      if (!registration->IsControllingDocuments()) {
-        if (!registration->mPendingUninstall) {
-          return NS_OK;
-        }
-
-        registration->Clear();
-        domainInfo->RemoveRegistration(registration);
-      }
-
-      return NS_OK;
-    }
-  };
-
-  nsRefPtr<nsIRunnable> unregisterRunnable =
-    new UnregisterRunnable(aCallback, scopeURI);
-  return NS_DispatchToCurrentThread(unregisterRunnable);
+  nsRefPtr<ServiceWorkerUnregisterJob> job =
+    new ServiceWorkerUnregisterJob(queue, scope, aCallback);
+  queue->Append(job);
+  return NS_OK;
 }
 
 /* static */
 already_AddRefed<ServiceWorkerManager>
 ServiceWorkerManager::GetInstance()
 {
   nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
   nsRefPtr<ServiceWorkerManager> concrete = do_QueryObject(swm);
@@ -2242,18 +2220,18 @@ ServiceWorkerManager::Update(const nsASt
   }
 
   ServiceWorkerJobQueue* queue = domainInfo->GetOrCreateJobQueue(scope);
   MOZ_ASSERT(queue);
 
   nsRefPtr<ServiceWorkerUpdateFinishCallback> cb =
     new ServiceWorkerUpdateFinishCallback();
 
-  nsRefPtr<ServiceWorkerRegisterJob> job
-    = new ServiceWorkerRegisterJob(queue, registration, cb);
+  nsRefPtr<ServiceWorkerRegisterJob> job =
+    new ServiceWorkerRegisterJob(queue, registration, cb);
   queue->Append(job);
   return NS_OK;
 }
 
 namespace {
 
 class MOZ_STACK_CLASS FilterRegistrationData
 {
--- a/dom/workers/ServiceWorkerManager.h
+++ b/dom/workers/ServiceWorkerManager.h
@@ -270,17 +270,17 @@ class ServiceWorkerManager MOZ_FINAL : p
 {
   friend class ActivationRunnable;
   friend class ServiceWorkerRegistrationInfo;
   friend class ServiceWorkerRegisterJob;
   friend class GetReadyPromiseRunnable;
   friend class GetRegistrationsRunnable;
   friend class GetRegistrationRunnable;
   friend class QueueFireUpdateFoundRunnable;
-  friend class UnregisterRunnable;
+  friend class ServiceWorkerUnregisterJob;
 
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSISERVICEWORKERMANAGER
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_SERVICEWORKERMANAGER_IMPL_IID)
 
   static ServiceWorkerManager* FactoryCreate()
   {
@@ -457,16 +457,23 @@ private:
   QueueFireEventOnServiceWorkerRegistrations(ServiceWorkerRegistrationInfo* aRegistration,
                                              const nsAString& aName);
 
   void
   FireEventOnServiceWorkerRegistrations(ServiceWorkerRegistrationInfo* aRegistration,
                                         const nsAString& aName);
 
   void
+  FireUpdateFound(ServiceWorkerRegistrationInfo* aRegistration)
+  {
+    FireEventOnServiceWorkerRegistrations(aRegistration,
+                                          NS_LITERAL_STRING("updatefound"));
+  }
+
+  void
   FireControllerChange(ServiceWorkerRegistrationInfo* aRegistration);
 
   void
   StorePendingReadyPromise(nsPIDOMWindow* aWindow, nsIURI* aURI, Promise* aPromise);
 
   void
   CheckPendingReadyPromises();
 
--- a/dom/workers/ServiceWorkerRegistration.cpp
+++ b/dom/workers/ServiceWorkerRegistration.cpp
@@ -159,32 +159,33 @@ ServiceWorkerRegistration::Unregister(Er
   nsCOMPtr<nsIDocument> document = GetOwner()->GetExtantDoc();
   if (!document) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   nsCOMPtr<nsIURI> scopeURI;
   nsCOMPtr<nsIURI> baseURI = document->GetBaseURI();
+  // "If the origin of scope is not client's origin..."
   nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), mScope, nullptr, baseURI);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return nullptr;
   }
 
   nsCOMPtr<nsIPrincipal> documentPrincipal = document->NodePrincipal();
   rv = documentPrincipal->CheckMayLoad(scopeURI, true /* report */,
                                        false /* allowIfInheritsPrinciple */);
   if (NS_FAILED(rv)) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return nullptr;
   }
 
   nsAutoCString uriSpec;
-  aRv = scopeURI->GetSpec(uriSpec);
+  aRv = scopeURI->GetSpecIgnoringRef(uriSpec);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   nsCOMPtr<nsIServiceWorkerManager> swm =
     do_GetService(SERVICEWORKERMANAGER_CONTRACTID, &rv);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     aRv.Throw(rv);
--- a/dom/workers/test/serviceworkers/mochitest.ini
+++ b/dom/workers/test/serviceworkers/mochitest.ini
@@ -22,14 +22,13 @@ support-files =
 [test_unregister.html]
 skip-if = true # bug 1094375
 [test_installation_simple.html]
 skip-if = true # bug 1094375
 [test_get_serviced.html]
 [test_install_event.html]
 [test_navigator.html]
 [test_scopes.html]
-skip-if = true # bug 1124743
 [test_controller.html]
 [test_workerUpdate.html]
 skip-if = true # Enable after Bug 982726 postMessage is landed.
 [test_workerUnregister.html]
 skip-if = true # Enable after Bug 982726 postMessage is landed.
--- a/dom/workers/test/serviceworkers/test_installation_simple.html
+++ b/dom/workers/test/serviceworkers/test_installation_simple.html
@@ -101,16 +101,20 @@
         ok(e.name === "NetworkError", "Should fail with NetworkError");
       });
   }
 
   function parseError() {
     var p = navigator.serviceWorker.register("parse_error_worker.js", { scope: "parse_error/" });
     return p.then(function(wr) {
       ok(false, "Registration should fail with parse error");
+      return navigator.serviceWorker.getRegistration("parse_error/").then(function(swr) {
+        // See https://github.com/slightlyoff/ServiceWorker/issues/547
+        is(swr, undefined, "A failed registration for a scope with no prior controllers should clear itself");
+      });
     }, function(e) {
     info("NSM " + e.name);
       ok(e instanceof Error, "Registration should fail with parse error");
     });
   }
 
   // FIXME(nsm): test for parse error when Update step doesn't happen (directly from register).
 
--- a/dom/workers/test/serviceworkers/test_unregister.html
+++ b/dom/workers/test/serviceworkers/test_unregister.html
@@ -81,35 +81,19 @@
 
     return testPromise.then(function() {
       div.removeChild(ifr);
     });
   }
 
   function runTest() {
     simpleRegister()
-      .then(function(v) {
-        info("simpleRegister() promise resolved");
-      })
       .then(testControlled)
-      .then(function(v) {
-        info("testControlled() promise resolved");
-      }, function(e) {
-        info("testControlled() promise rejected " + e);
-      })
       .then(unregister)
-      .then(function(v) {
-        info("unregister() promise resolved");
-      })
       .then(testUncontrolled)
-      .then(function(v) {
-        info("testUncontrolled() promise resolved");
-      }, function(e) {
-        info("testUncontrolled() promise rejected " + e);
-      })
       .then(function() {
         SimpleTest.finish();
       }).catch(function(e) {
         ok(false, "Some test failed with error " + e);
         SimpleTest.finish();
       });
   }
 
--- a/gfx/layers/client/TextureClient.h
+++ b/gfx/layers/client/TextureClient.h
@@ -454,17 +454,17 @@ public:
   /**
    * Set AsyncTransactionTracker of RemoveTextureFromCompositableAsync() transaction.
    */
   virtual void SetRemoveFromCompositableTracker(AsyncTransactionTracker* aTracker) {}
 
   /**
    * This function waits until the buffer is no longer being used.
    */
-  virtual void WaitForBufferOwnership() {}
+  virtual void WaitForBufferOwnership(bool aWaitReleaseFence = true) {}
 
   /**
    * Track how much of this texture is wasted.
    * For example we might allocate a 256x256 tile but only use 10x10.
    */
    void SetWaste(int aWasteArea) {
      mWasteTracker.Update(aWasteArea, BytesPerPixel(GetFormat()));
    }
--- a/gfx/layers/opengl/GrallocTextureClient.cpp
+++ b/gfx/layers/opengl/GrallocTextureClient.cpp
@@ -83,23 +83,27 @@ GrallocTextureClientOGL::ToSurfaceDescri
 
 void
 GrallocTextureClientOGL::SetRemoveFromCompositableTracker(AsyncTransactionTracker* aTracker)
 {
   mRemoveFromCompositableTracker = aTracker;
 }
 
 void
-GrallocTextureClientOGL::WaitForBufferOwnership()
+GrallocTextureClientOGL::WaitForBufferOwnership(bool aWaitReleaseFence)
 {
   if (mRemoveFromCompositableTracker) {
     mRemoveFromCompositableTracker->WaitComplete();
     mRemoveFromCompositableTracker = nullptr;
   }
 
+  if (!aWaitReleaseFence) {
+    return;
+  }
+
 #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
    if (mReleaseFenceHandle.IsValid()) {
      android::sp<Fence> fence = mReleaseFenceHandle.mFence;
 #if ANDROID_VERSION == 17
      fence->waitForever(1000, "GrallocTextureClientOGL::Lock");
      // 1000 is what Android uses. It is a warning timeout in ms.
      // This timeout was removed in ANDROID_VERSION 18.
 #else
@@ -117,26 +121,42 @@ GrallocTextureClientOGL::Lock(OpenMode a
   if (!IsValid() || !IsAllocated()) {
     return false;
   }
 
   if (mMappedBuffer) {
     return true;
   }
 
+#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 21
+  WaitForBufferOwnership(false /* aWaitReleaseFence */);
+#else
   WaitForBufferOwnership();
+#endif
 
   uint32_t usage = 0;
   if (aMode & OpenMode::OPEN_READ) {
     usage |= GRALLOC_USAGE_SW_READ_OFTEN;
   }
   if (aMode & OpenMode::OPEN_WRITE) {
     usage |= GRALLOC_USAGE_SW_WRITE_OFTEN;
   }
-  int32_t rv = mGraphicBuffer->lock(usage, reinterpret_cast<void**>(&mMappedBuffer));
+#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 21
+  android::sp<Fence> fence = android::Fence::NO_FENCE;
+  if (mReleaseFenceHandle.IsValid()) {
+    fence = mReleaseFenceHandle.mFence;
+  }
+  mReleaseFenceHandle = FenceHandle();
+  int32_t rv = mGraphicBuffer->lockAsync(usage,
+                                         reinterpret_cast<void**>(&mMappedBuffer),
+                                         fence->dup());
+#else
+  int32_t rv = mGraphicBuffer->lock(usage,
+                                    reinterpret_cast<void**>(&mMappedBuffer));
+#endif
   if (rv) {
     mMappedBuffer = nullptr;
     NS_WARNING("Couldn't lock graphic buffer");
     return false;
   }
   return BufferTextureClient::Lock(aMode);
 }
 
--- a/gfx/layers/opengl/GrallocTextureClient.h
+++ b/gfx/layers/opengl/GrallocTextureClient.h
@@ -53,17 +53,17 @@ public:
   virtual bool HasInternalBuffer() const MOZ_OVERRIDE { return false; }
 
   virtual bool IsAllocated() const MOZ_OVERRIDE;
 
   virtual bool ToSurfaceDescriptor(SurfaceDescriptor& aOutDescriptor) MOZ_OVERRIDE;
 
   virtual void SetRemoveFromCompositableTracker(AsyncTransactionTracker* aTracker) MOZ_OVERRIDE;
 
-  virtual void WaitForBufferOwnership() MOZ_OVERRIDE;
+  virtual void WaitForBufferOwnership(bool aWaitReleaseFence = true) MOZ_OVERRIDE;
 
   void InitWith(MaybeMagicGrallocBufferHandle aDesc, gfx::IntSize aSize);
 
   void SetTextureFlags(TextureFlags aFlags) { AddFlags(aFlags); }
 
   gfx::IntSize GetSize() const MOZ_OVERRIDE { return mSize; }
 
   android::sp<android::GraphicBuffer> GetGraphicBuffer()
--- a/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp
+++ b/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp
@@ -22,17 +22,29 @@ MacIOSurfaceTextureHostOGL::MacIOSurface
 bool
 MacIOSurfaceTextureHostOGL::Lock()
 {
   if (!mCompositor || !mSurface) {
     return false;
   }
 
   if (!mTextureSource) {
-    mTextureSource = new MacIOSurfaceTextureSourceOGL(mCompositor, mSurface);
+    GLuint textureHandle;
+    gl::GLContext* gl = mCompositor->gl();
+    gl->fGenTextures(1, &textureHandle);
+    gl->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, textureHandle);
+    gl->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
+    gl->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
+    mSurface->CGLTexImageIOSurface2D(gl::GLContextCGL::Cast(gl)->GetCGLContext());
+
+    mTextureSource = new GLTextureSource(mCompositor, textureHandle, LOCAL_GL_TEXTURE_RECTANGLE_ARB,
+                                         gfx::IntSize(mSurface->GetDevicePixelWidth(),
+                                                      mSurface->GetDevicePixelHeight()),
+                                         mSurface->HasAlpha() ? gfx::SurfaceFormat::R8G8B8A8:
+                                                                gfx::SurfaceFormat::R8G8B8X8);
   }
   return true;
 }
 
 void
 MacIOSurfaceTextureHostOGL::SetCompositor(Compositor* aCompositor)
 {
   CompositorOGL* glCompositor = static_cast<CompositorOGL*>(aCompositor);
--- a/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.h
+++ b/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.h
@@ -89,16 +89,16 @@ public:
   virtual gfx::IntSize GetSize() const MOZ_OVERRIDE;
 
 #ifdef MOZ_LAYERS_HAVE_LOG
   virtual const char* Name() MOZ_OVERRIDE { return "MacIOSurfaceTextureHostOGL"; }
 #endif
 
 protected:
   RefPtr<CompositorOGL> mCompositor;
-  RefPtr<MacIOSurfaceTextureSourceOGL> mTextureSource;
+  RefPtr<GLTextureSource> mTextureSource;
   RefPtr<MacIOSurface> mSurface;
 };
 
 }
 }
 
 #endif // MOZILLA_GFX_MACIOSURFACETEXTUREHOSTOGL_H
--- a/gfx/thebes/gfxPangoFonts.cpp
+++ b/gfx/thebes/gfxPangoFonts.cpp
@@ -200,18 +200,18 @@ public:
     {
         cairo_font_face_set_user_data(mFontFace,
                                       &sFontEntryKey,
                                       nullptr,
                                       nullptr);
         cairo_font_face_destroy(mFontFace);
     }
 
-    virtual void ForgetHBFace();
-    virtual void ReleaseGrFace(gr_face* aFace);
+    virtual void ForgetHBFace() MOZ_OVERRIDE;
+    virtual void ReleaseGrFace(gr_face* aFace) MOZ_OVERRIDE;
 
 protected:
     virtual nsresult
     CopyFontTable(uint32_t aTableTag, FallibleTArray<uint8_t>& aBuffer) MOZ_OVERRIDE;
 
     void MaybeReleaseFTFace();
 
 private:
@@ -666,22 +666,22 @@ public:
 
 #ifdef USE_SKIA
     virtual mozilla::TemporaryRef<mozilla::gfx::GlyphRenderingOptions>
         GetGlyphRenderingOptions(const TextRunDrawParams* aRunParams = nullptr) MOZ_OVERRIDE;
 #endif
 
     // return a cloned font resized and offset to simulate sub/superscript glyphs
     virtual already_AddRefed<gfxFont>
-    GetSubSuperscriptFont(int32_t aAppUnitsPerDevPixel);
+    GetSubSuperscriptFont(int32_t aAppUnitsPerDevPixel) MOZ_OVERRIDE;
 
 protected:
     virtual already_AddRefed<gfxFont> MakeScaledFont(gfxFontStyle *aFontStyle,
                                                      gfxFloat aFontScale);
-    virtual already_AddRefed<gfxFont> GetSmallCapsFont();
+    virtual already_AddRefed<gfxFont> GetSmallCapsFont() MOZ_OVERRIDE;
 
 private:
     gfxFcFont(cairo_scaled_font_t *aCairoFont, gfxFcFontEntry *aFontEntry,
               const gfxFontStyle *aFontStyle);
 
     // key for locating a gfxFcFont corresponding to a cairo_scaled_font
     static cairo_user_data_key_t sGfxFontKey;
 };
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -164,17 +164,17 @@ public:
 /// this gets called to be large - it is meant for critical errors only.
 
 class CrashStatsLogForwarder: public mozilla::gfx::LogForwarder
 {
 public:
   explicit CrashStatsLogForwarder(const char* aKey);
   virtual void Log(const std::string& aString) MOZ_OVERRIDE;
 
-  virtual std::vector<std::pair<int32_t,std::string> > StringsVectorCopy();
+  virtual std::vector<std::pair<int32_t,std::string> > StringsVectorCopy() MOZ_OVERRIDE;
 
   void SetCircularBufferSize(uint32_t aCapacity);
 
 private:
   // Helpers for the Log()
   bool UpdateStringsVector(const std::string& aString);
   void UpdateCrashReport();
 
--- a/js/public/Conversions.h
+++ b/js/public/Conversions.h
@@ -10,20 +10,238 @@
 #define js_Conversions_h
 
 #include "mozilla/Casting.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/TypeTraits.h"
 
 #include <math.h>
 
+#include "jspubtd.h"
+
+#include "js/RootingAPI.h"
+#include "js/Value.h"
+
+struct JSContext;
+
+namespace js {
+
+/* DO NOT CALL THIS. Use JS::ToBoolean. */
+extern JS_PUBLIC_API(bool)
+ToBooleanSlow(JS::HandleValue v);
+
+/* DO NOT CALL THIS.  Use JS::ToNumber. */
+extern JS_PUBLIC_API(bool)
+ToNumberSlow(JSContext *cx, JS::Value v, double *dp);
+
+/* DO NOT CALL THIS. Use JS::ToInt32. */
+extern JS_PUBLIC_API(bool)
+ToInt32Slow(JSContext *cx, JS::HandleValue v, int32_t *out);
+
+/* DO NOT CALL THIS. Use JS::ToUint32. */
+extern JS_PUBLIC_API(bool)
+ToUint32Slow(JSContext *cx, JS::HandleValue v, uint32_t *out);
+
+/* DO NOT CALL THIS. Use JS::ToUint16. */
+extern JS_PUBLIC_API(bool)
+ToUint16Slow(JSContext *cx, JS::HandleValue v, uint16_t *out);
+
+/* DO NOT CALL THIS. Use JS::ToInt64. */
+extern JS_PUBLIC_API(bool)
+ToInt64Slow(JSContext *cx, JS::HandleValue v, int64_t *out);
+
+/* DO NOT CALL THIS. Use JS::ToUint64. */
+extern JS_PUBLIC_API(bool)
+ToUint64Slow(JSContext *cx, JS::HandleValue v, uint64_t *out);
+
+/* DO NOT CALL THIS. Use JS::ToString. */
+extern JS_PUBLIC_API(JSString*)
+ToStringSlow(JSContext *cx, JS::HandleValue v);
+
+/* DO NOT CALL THIS. Use JS::ToObject. */
+extern JS_PUBLIC_API(JSObject*)
+ToObjectSlow(JSContext *cx, JS::HandleValue v, bool reportScanStack);
+
+} // namespace js
+
 namespace JS {
 
 namespace detail {
 
+#ifdef JS_DEBUG
+/*
+ * Assert that we're not doing GC on cx, that we're in a request as
+ * needed, and that the compartments for cx and v are correct.
+ * Also check that GC would be safe at this point.
+ */
+extern JS_PUBLIC_API(void)
+AssertArgumentsAreSane(JSContext *cx, HandleValue v);
+#else
+inline void AssertArgumentsAreSane(JSContext *cx, HandleValue v)
+{}
+#endif /* JS_DEBUG */
+
+} // namespace detail
+
+/*
+ * ES6 draft 20141224, 7.1.1, second algorithm.
+ *
+ * Most users shouldn't call this -- use JS::ToBoolean, ToNumber, or ToString
+ * instead.  This will typically only be called from custom convert hooks that
+ * wish to fall back to the ES6 default conversion behavior shared by most
+ * objects in JS, codified as OrdinaryToPrimitive.
+ */
+extern JS_PUBLIC_API(bool)
+OrdinaryToPrimitive(JSContext *cx, HandleObject obj, JSType type, MutableHandleValue vp);
+
+/* ES6 draft 20141224, 7.1.2. */
+MOZ_ALWAYS_INLINE bool
+ToBoolean(HandleValue v)
+{
+    if (v.isBoolean())
+        return v.toBoolean();
+    if (v.isInt32())
+        return v.toInt32() != 0;
+    if (v.isNullOrUndefined())
+        return false;
+    if (v.isDouble()) {
+        double d = v.toDouble();
+        return !mozilla::IsNaN(d) && d != 0;
+    }
+    if (v.isSymbol())
+        return true;
+
+    /* The slow path handles strings and objects. */
+    return js::ToBooleanSlow(v);
+}
+
+/* ES6 draft 20141224, 7.1.3. */
+MOZ_ALWAYS_INLINE bool
+ToNumber(JSContext *cx, HandleValue v, double *out)
+{
+    detail::AssertArgumentsAreSane(cx, v);
+
+    if (v.isNumber()) {
+        *out = v.toNumber();
+        return true;
+    }
+    return js::ToNumberSlow(cx, v, out);
+}
+
+/* ES6 draft 20141224, ToInteger (specialized for doubles). */
+inline double
+ToInteger(double d)
+{
+    if (d == 0)
+        return d;
+
+    if (!mozilla::IsFinite(d)) {
+        if (mozilla::IsNaN(d))
+            return 0;
+        return d;
+    }
+
+    return d < 0 ? ceil(d) : floor(d);
+}
+
+/* ES6 draft 20141224, 7.1.5. */
+MOZ_ALWAYS_INLINE bool
+ToInt32(JSContext *cx, JS::HandleValue v, int32_t *out)
+{
+    detail::AssertArgumentsAreSane(cx, v);
+
+    if (v.isInt32()) {
+        *out = v.toInt32();
+        return true;
+    }
+    return js::ToInt32Slow(cx, v, out);
+}
+
+/* ES6 draft 20141224, 7.1.6. */
+MOZ_ALWAYS_INLINE bool
+ToUint32(JSContext *cx, HandleValue v, uint32_t *out)
+{
+    detail::AssertArgumentsAreSane(cx, v);
+
+    if (v.isInt32()) {
+        *out = uint32_t(v.toInt32());
+        return true;
+    }
+    return js::ToUint32Slow(cx, v, out);
+}
+
+/* ES6 draft 20141224, 7.1.8. */
+MOZ_ALWAYS_INLINE bool
+ToUint16(JSContext *cx, HandleValue v, uint16_t *out)
+{
+    detail::AssertArgumentsAreSane(cx, v);
+
+    if (v.isInt32()) {
+        *out = uint16_t(v.toInt32());
+        return true;
+    }
+    return js::ToUint16Slow(cx, v, out);
+}
+
+/*
+ * Non-standard, with behavior similar to that of ToInt32, except in its
+ * producing an int64_t.
+ */
+MOZ_ALWAYS_INLINE bool
+ToInt64(JSContext *cx, HandleValue v, int64_t *out)
+{
+    detail::AssertArgumentsAreSane(cx, v);
+
+    if (v.isInt32()) {
+        *out = int64_t(v.toInt32());
+        return true;
+    }
+    return js::ToInt64Slow(cx, v, out);
+}
+
+/*
+ * Non-standard, with behavior similar to that of ToUint32, except in its
+ * producing a uint64_t.
+ */
+MOZ_ALWAYS_INLINE bool
+ToUint64(JSContext *cx, HandleValue v, uint64_t *out)
+{
+    detail::AssertArgumentsAreSane(cx, v);
+
+    if (v.isInt32()) {
+        *out = uint64_t(v.toInt32());
+        return true;
+    }
+    return js::ToUint64Slow(cx, v, out);
+}
+
+/* ES6 draft 20141224, 7.1.12. */
+MOZ_ALWAYS_INLINE JSString*
+ToString(JSContext *cx, HandleValue v)
+{
+    detail::AssertArgumentsAreSane(cx, v);
+
+    if (v.isString())
+        return v.toString();
+    return js::ToStringSlow(cx, v);
+}
+
+/* ES6 draft 20141224, 7.1.13. */
+inline JSObject *
+ToObject(JSContext *cx, HandleValue v)
+{
+    detail::AssertArgumentsAreSane(cx, v);
+
+    if (v.isObject())
+        return &v.toObject();
+    return js::ToObjectSlow(cx, v, false);
+}
+
+namespace detail {
+
 /*
  * Convert a double value to ResultType (an unsigned integral type) using
  * ECMAScript-style semantics (that is, in like manner to how ECMAScript's
  * ToInt32 converts to int32_t).
  *
  *   If d is infinite or NaN, return 0.
  *   Otherwise compute d2 = sign(d) * floor(abs(d)), and return the ResultType
  *   value congruent to d2 mod 2**(bit width of ResultType).
@@ -273,27 +491,11 @@ ToInt64(double d)
 
 /* WEBIDL 4.2.11 */
 inline uint64_t
 ToUint64(double d)
 {
     return detail::ToUintWidth<uint64_t>(d);
 }
 
-/* ES5 9.4 ToInteger (specialized for doubles). */
-inline double
-ToInteger(double d)
-{
-    if (d == 0)
-        return d;
-
-    if (!mozilla::IsFinite(d)) {
-        if (mozilla::IsNaN(d))
-            return 0;
-        return d;
-    }
-
-    return d < 0 ? ceil(d) : floor(d);
-}
-
 } // namespace JS
 
 #endif /* js_Conversions_h */
--- a/js/src/builtin/Intl.cpp
+++ b/js/src/builtin/Intl.cpp
@@ -664,19 +664,25 @@ js::intl_Collator(JSContext *cx, unsigne
     // intl_Collator is an intrinsic for self-hosted JavaScript, so it cannot
     // be used with "new", but it still has to be treated as a constructor.
     return Collator(cx, args, true);
 }
 
 static void
 collator_finalize(FreeOp *fop, JSObject *obj)
 {
-    UCollator *coll = static_cast<UCollator*>(obj->as<NativeObject>().getReservedSlot(UCOLLATOR_SLOT).toPrivate());
-    if (coll)
-        ucol_close(coll);
+    // This is-undefined check shouldn't be necessary, but for internal
+    // brokenness in object allocation code.  For the moment, hack around it by
+    // explicitly guarding against the possibility of the reserved slot not
+    // containing a private.  See bug 949220.
+    const Value &slot = obj->as<NativeObject>().getReservedSlot(UCOLLATOR_SLOT);
+    if (!slot.isUndefined()) {
+        if (UCollator *coll = static_cast<UCollator*>(slot.toPrivate()))
+            ucol_close(coll);
+    }
 }
 
 static JSObject *
 InitCollatorClass(JSContext *cx, HandleObject Intl, Handle<GlobalObject*> global)
 {
     RootedFunction ctor(cx, global->createConstructor(cx, &Collator, cx->names().Collator, 0));
     if (!ctor)
         return nullptr;
@@ -1151,20 +1157,25 @@ js::intl_NumberFormat(JSContext *cx, uns
     // cannot be used with "new", but it still has to be treated as a
     // constructor.
     return NumberFormat(cx, args, true);
 }
 
 static void
 numberFormat_finalize(FreeOp *fop, JSObject *obj)
 {
-    UNumberFormat *nf =
-        static_cast<UNumberFormat*>(obj->as<NativeObject>().getReservedSlot(UNUMBER_FORMAT_SLOT).toPrivate());
-    if (nf)
-        unum_close(nf);
+    // This is-undefined check shouldn't be necessary, but for internal
+    // brokenness in object allocation code.  For the moment, hack around it by
+    // explicitly guarding against the possibility of the reserved slot not
+    // containing a private.  See bug 949220.
+    const Value &slot = obj->as<NativeObject>().getReservedSlot(UNUMBER_FORMAT_SLOT);
+    if (!slot.isUndefined()) {
+        if (UNumberFormat *nf = static_cast<UNumberFormat*>(slot.toPrivate()))
+            unum_close(nf);
+    }
 }
 
 static JSObject *
 InitNumberFormatClass(JSContext *cx, HandleObject Intl, Handle<GlobalObject*> global)
 {
     RootedFunction ctor(cx, global->createConstructor(cx, &NumberFormat, cx->names().NumberFormat, 0));
     if (!ctor)
         return nullptr;
@@ -1605,19 +1616,25 @@ js::intl_DateTimeFormat(JSContext *cx, u
     // cannot be used with "new", but it still has to be treated as a
     // constructor.
     return DateTimeFormat(cx, args, true);
 }
 
 static void
 dateTimeFormat_finalize(FreeOp *fop, JSObject *obj)
 {
-    UDateFormat *df = static_cast<UDateFormat*>(obj->as<NativeObject>().getReservedSlot(UDATE_FORMAT_SLOT).toPrivate());
-    if (df)
-        udat_close(df);
+    // This is-undefined check shouldn't be necessary, but for internal
+    // brokenness in object allocation code.  For the moment, hack around it by
+    // explicitly guarding against the possibility of the reserved slot not
+    // containing a private.  See bug 949220.
+    const Value &slot = obj->as<NativeObject>().getReservedSlot(UDATE_FORMAT_SLOT);
+    if (!slot.isUndefined()) {
+        if (UDateFormat *df = static_cast<UDateFormat*>(slot.toPrivate()))
+            udat_close(df);
+    }
 }
 
 static JSObject *
 InitDateTimeFormatClass(JSContext *cx, HandleObject Intl, Handle<GlobalObject*> global)
 {
     RootedFunction ctor(cx, global->createConstructor(cx, &DateTimeFormat, cx->names().DateTimeFormat, 0));
     if (!ctor)
         return nullptr;
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -2,16 +2,17 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * 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 frontend_FullParseHandler_h
 #define frontend_FullParseHandler_h
 
+#include "mozilla/Attributes.h"
 #include "mozilla/PodOperations.h"
 
 #include "frontend/ParseNode.h"
 #include "frontend/SharedContext.h"
 
 namespace js {
 namespace frontend {
 
@@ -537,18 +538,40 @@ class FullParseHandler
 
     ParseNode *newLexicalScope(ObjectBox *blockBox) {
         return new_<LexicalScopeNode>(blockBox, pos());
     }
     void setLexicalScopeBody(ParseNode *block, ParseNode *body) {
         block->pn_expr = body;
     }
 
-    bool isOperationWithoutParens(ParseNode *pn, ParseNodeKind kind) {
-        return pn->isKind(kind) && !pn->isInParens();
+    ParseNode *newAssignment(ParseNodeKind kind, ParseNode *lhs, ParseNode *rhs,
+                             ParseContext<FullParseHandler> *pc, JSOp op)
+    {
+        return newBinaryOrAppend(kind, lhs, rhs, pc, op);
+    }
+
+    bool isUnparenthesizedYieldExpression(ParseNode *node) {
+        return node->isKind(PNK_YIELD) && !node->isInParens();
+    }
+
+    bool isUnparenthesizedCommaExpression(ParseNode *node) {
+        return node->isKind(PNK_COMMA) && !node->isInParens();
+    }
+
+    bool isUnparenthesizedAssignment(Node node) {
+        if (node->isKind(PNK_ASSIGN) && !node->isInParens()) {
+            // PNK_ASSIGN is also (mis)used for things like |var name = expr;|.
+            // But this method is only called on actual expressions, so we can
+            // just assert the node's op is the one used for plain assignment.
+            MOZ_ASSERT(node->isOp(JSOP_NOP));
+            return true;
+        }
+
+        return false;
     }
 
     inline bool finishInitializerAssignment(ParseNode *pn, ParseNode *init, JSOp op);
 
     void setBeginPosition(ParseNode *pn, ParseNode *oth) {
         setBeginPosition(pn, oth->pn_pos.begin);
     }
     void setBeginPosition(ParseNode *pn, uint32_t begin) {
@@ -575,43 +598,44 @@ class FullParseHandler
         return new_<ListNode>(kind, op, pos());
     }
 
     /* New list with one initial child node. kid must be non-null. */
     ParseNode *newList(ParseNodeKind kind, ParseNode *kid, JSOp op = JSOP_NOP) {
         return new_<ListNode>(kind, op, kid);
     }
 
-    void addList(ParseNode *pn, ParseNode *kid) {
-        pn->append(kid);
+
+    ParseNode *newCommaExpressionList(ParseNode *kid) {
+        return newList(PNK_COMMA, kid, JSOP_NOP);
     }
 
-    bool isUnparenthesizedYield(ParseNode *pn) {
-        return pn->isKind(PNK_YIELD) && !pn->isInParens();
+    void addList(ParseNode *list, ParseNode *kid) {
+        list->append(kid);
     }
 
     void setOp(ParseNode *pn, JSOp op) {
         pn->setOp(op);
     }
     void setBlockId(ParseNode *pn, unsigned blockid) {
         pn->pn_blockid = blockid;
     }
     void setFlag(ParseNode *pn, unsigned flag) {
         pn->pn_dflags |= flag;
     }
     void setListFlag(ParseNode *pn, unsigned flag) {
         MOZ_ASSERT(pn->isArity(PN_LIST));
         pn->pn_xflags |= flag;
     }
-    ParseNode *setInParens(ParseNode *pn) {
+    MOZ_WARN_UNUSED_RESULT ParseNode *parenthesize(ParseNode *pn) {
         pn->setInParens(true);
         return pn;
     }
-    ParseNode *setLikelyIIFE(ParseNode *pn) {
-        return setInParens(pn);
+    MOZ_WARN_UNUSED_RESULT ParseNode *setLikelyIIFE(ParseNode *pn) {
+        return parenthesize(pn);
     }
     void setPrologue(ParseNode *pn) {
         pn->pn_prologue = true;
     }
 
     bool isConstant(ParseNode *pn) {
         return pn->isConstant();
     }
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -2879,20 +2879,19 @@ Parser<ParseHandler>::condition()
 {
     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
     Node pn = exprInParens();
     if (!pn)
         return null();
     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
 
     /* Check for (a = b) and warn about possible (a == b) mistype. */
-    if (handler.isOperationWithoutParens(pn, PNK_ASSIGN) &&
-        !report(ParseExtraWarning, false, null(), JSMSG_EQUAL_AS_ASSIGN))
-    {
-        return null();
+    if (handler.isUnparenthesizedAssignment(pn)) {
+        if (!report(ParseExtraWarning, false, null(), JSMSG_EQUAL_AS_ASSIGN))
+            return null();
     }
     return pn;
 }
 
 template <typename ParseHandler>
 bool
 Parser<ParseHandler>::matchLabel(MutableHandle<PropertyName*> label)
 {
@@ -5912,21 +5911,21 @@ Parser<ParseHandler>::expr(InvokedPredic
     Node pn = assignExpr(invoked);
     if (!pn)
         return null();
 
     bool matched;
     if (!tokenStream.matchToken(&matched, TOK_COMMA))
         return null();
     if (matched) {
-        Node seq = handler.newList(PNK_COMMA, pn);
+        Node seq = handler.newCommaExpressionList(pn);
         if (!seq)
             return null();
         while (true) {
-            if (handler.isUnparenthesizedYield(pn)) {
+            if (handler.isUnparenthesizedYieldExpression(pn)) {
                 report(ParseError, false, pn, JSMSG_BAD_GENERATOR_SYNTAX, js_yield_str);
                 return null();
             }
 
             pn = assignExpr();
             if (!pn)
                 return null();
             handler.addList(seq, pn);
@@ -6291,17 +6290,17 @@ Parser<ParseHandler>::assignExpr(Invoked
     AssignmentFlavor flavor = kind == PNK_ASSIGN ? PlainAssignment : CompoundAssignment;
     if (!checkAndMarkAsAssignmentLhs(lhs, flavor))
         return null();
 
     Node rhs = assignExpr();
     if (!rhs)
         return null();
 
-    return handler.newBinaryOrAppend(kind, lhs, rhs, pc, op);
+    return handler.newAssignment(kind, lhs, rhs, pc, op);
 }
 
 static const char incop_name_str[][10] = {"increment", "decrement"};
 
 template <>
 bool
 Parser<FullParseHandler>::checkAndMarkAsIncOperand(ParseNode *kid, TokenKind tt, bool preorder)
 {
@@ -7275,20 +7274,19 @@ Parser<ParseHandler>::comprehensionIf(Ge
 
     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
     Node cond = assignExpr();
     if (!cond)
         return null();
     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
 
     /* Check for (a = b) and warn about possible (a == b) mistype. */
-    if (handler.isOperationWithoutParens(cond, PNK_ASSIGN) &&
-        !report(ParseExtraWarning, false, null(), JSMSG_EQUAL_AS_ASSIGN))
-    {
-        return null();
+    if (handler.isUnparenthesizedAssignment(cond)) {
+        if (!report(ParseExtraWarning, false, null(), JSMSG_EQUAL_AS_ASSIGN))
+            return null();
     }
 
     Node then = comprehensionTail(comprehensionKind);
     if (!then)
         return null();
 
     return handler.newIfStatement(begin, cond, then, null());
 }
@@ -7318,17 +7316,17 @@ Parser<ParseHandler>::comprehensionTail(
 
     if (comprehensionKind == NotGenerator)
         return handler.newUnary(PNK_ARRAYPUSH, JSOP_ARRAYPUSH, begin, bodyExpr);
 
     MOZ_ASSERT(comprehensionKind == StarGenerator);
     Node yieldExpr = newYieldExpression(begin, bodyExpr);
     if (!yieldExpr)
         return null();
-    handler.setInParens(yieldExpr);
+    yieldExpr = handler.parenthesize(yieldExpr);
 
     return handler.newExprStatement(yieldExpr, pos().end);
 }
 
 // Parse an ES6 generator or array comprehension, starting at the first 'for'.
 // The caller is responsible for matching the ending TOK_RP or TOK_RB.
 template <typename ParseHandler>
 typename ParseHandler::Node
@@ -7441,17 +7439,17 @@ Parser<ParseHandler>::argumentList(Node 
         if (!argNode)
             return false;
         if (spread) {
             argNode = handler.newUnary(PNK_SPREAD, JSOP_NOP, begin, argNode);
             if (!argNode)
                 return false;
         }
 
-        if (handler.isOperationWithoutParens(argNode, PNK_YIELD)) {
+        if (handler.isUnparenthesizedYieldExpression(argNode)) {
             TokenKind tt;
             if (!tokenStream.peekToken(&tt))
                 return false;
             if (tt == TOK_COMMA) {
                 report(ParseError, false, argNode, JSMSG_BAD_GENERATOR_SYNTAX, js_yield_str);
                 return false;
             }
         }
@@ -8274,17 +8272,17 @@ Parser<ParseHandler>::parenExprOrGenerat
     if (!tokenStream.matchToken(&matched, TOK_FOR))
         return null();
     if (matched) {
         if (pc->lastYieldOffset != startYieldOffset) {
             reportWithOffset(ParseError, false, pc->lastYieldOffset,
                              JSMSG_BAD_GENEXP_BODY, js_yield_str);
             return null();
         }
-        if (handler.isOperationWithoutParens(pn, PNK_COMMA)) {
+        if (handler.isUnparenthesizedCommaExpression(pn)) {
             report(ParseError, false, null(),
                    JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str);
             return null();
         }
         pn = legacyGeneratorExpr(pn);
         if (!pn)
             return null();
         handler.setBeginPosition(pn, begin);
@@ -8292,22 +8290,21 @@ Parser<ParseHandler>::parenExprOrGenerat
         if (!tokenStream.getToken(&tt))
             return null();
         if (tt != TOK_RP) {
             report(ParseError, false, null(),
                    JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str);
             return null();
         }
         handler.setEndPosition(pn, pos().end);
-        handler.setInParens(pn);
-        return pn;
+        return handler.parenthesize(pn);
     }
 #endif /* JS_HAS_GENERATOR_EXPRS */
 
-    pn = handler.setInParens(pn);
+    pn = handler.parenthesize(pn);
 
     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
 
     return pn;
 }
 
 // Legacy generator comprehensions can sometimes appear without parentheses.
 // For example:
@@ -8353,17 +8350,17 @@ Parser<ParseHandler>::exprInParens()
     if (!tokenStream.matchToken(&matched, TOK_FOR))
         return null();
     if (matched) {
         if (pc->lastYieldOffset != startYieldOffset) {
             reportWithOffset(ParseError, false, pc->lastYieldOffset,
                              JSMSG_BAD_GENEXP_BODY, js_yield_str);
             return null();
         }
-        if (handler.isOperationWithoutParens(pn, PNK_COMMA)) {
+        if (handler.isUnparenthesizedCommaExpression(pn)) {
             report(ParseError, false, null(),
                    JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str);
             return null();
         }
         pn = legacyGeneratorExpr(pn);
         if (!pn)
             return null();
         handler.setBeginPosition(pn, begin);
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -2,16 +2,18 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * 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 frontend_SyntaxParseHandler_h
 #define frontend_SyntaxParseHandler_h
 
+#include "mozilla/Attributes.h"
+
 #include "frontend/ParseNode.h"
 #include "frontend/TokenStream.h"
 
 namespace js {
 namespace frontend {
 
 template <typename ParseHandler>
 class Parser;
@@ -34,22 +36,71 @@ class SyntaxParseHandler
     TokenStream &tokenStream;
 
   public:
     enum Node {
         NodeFailure = 0,
         NodeGeneric,
         NodeName,
         NodeGetProp,
-        NodeString,
         NodeStringExprStatement,
-        NodeLValue
+        NodeLValue,
+
+        // In rare cases a parenthesized |node| doesn't have the same semantics
+        // as |node|.  Each such node has a special Node value, and we use a
+        // different Node value to represent the parenthesized form.  See also
+        // isUnparenthesized*(Node), newExprStatement(Node, uint32_t),
+        // parenthesize(Node), and meaningMightChangeIfParenthesized(Node).
+
+        // The directive prologue at the start of a FunctionBody or ScriptBody
+        // is the longest sequence (possibly empty) of string literal
+        // expression statements at the start of a function.  Thus we need this
+        // to treat |"use strict";| as a possible Use Strict Directive and
+        // |("use strict");| as a useless statement.
+        NodeUnparenthesizedString,
+
+        // Legacy generator expressions of the form |(expr for (...))| and
+        // array comprehensions of the form |[expr for (...)]|) don't permit
+        // |expr| to be a comma expression.  Thus we need this to treat
+        // |(a(), b for (x in []))| as a syntax error and
+        // |((a(), b) for (x in []))| as a generator that calls |a| and then
+        // yields |b| each time it's resumed.
+        NodeUnparenthesizedCommaExpr,
+
+        // Yield expressions currently (but not in ES6 -- a SpiderMonkey bug to
+        // fix) must generally be parenthesized.  (See the uses of
+        // isUnparenthesizedYieldExpression in Parser.cpp for the rare
+        // exceptions.)  Thus we need this to treat |yield 1, 2;| as a syntax
+        // error and |(yield 1), 2;| as a comma expression that will yield 1,
+        // then evaluate to 2.
+        NodeUnparenthesizedYieldExpr,
+
+        // Assignment expressions in condition contexts could be typos for
+        // equality checks.  (Think |if (x = y)| versus |if (x == y)|.)  Thus
+        // we need this to treat |if (x = y)| as a possible typo and
+        // |if ((x = y))| as a deliberate assignment within a condition.
+        //
+        // (Technically this isn't needed, as these are *only* extraWarnings
+        // warnings, and parsing with that option disables syntax parsing.  But
+        // it seems best to be consistent, and perhaps the syntax parser will
+        // eventually enforce extraWarnings and will require this then.)
+        NodeUnparenthesizedAssignment
     };
     typedef Definition::Kind DefinitionNode;
 
+  private:
+    static bool meaningMightChangeIfParenthesized(Node node) {
+        return node == NodeUnparenthesizedString ||
+               node == NodeUnparenthesizedCommaExpr ||
+               node == NodeUnparenthesizedYieldExpr ||
+               node == NodeUnparenthesizedAssignment;
+    }
+
+
+  public:
     SyntaxParseHandler(ExclusiveContext *cx, LifoAlloc &alloc,
                        TokenStream &tokenStream, bool foldConstants,
                        Parser<SyntaxParseHandler> *syntaxParser, LazyScript *lazyOuterFunction)
       : lastAtom(nullptr),
         tokenStream(tokenStream)
     {}
 
     static Node null() { return NodeFailure; }
@@ -64,24 +115,24 @@ class SyntaxParseHandler
     Node newComputedName(Node expr, uint32_t start, uint32_t end) {
         return NodeName;
     }
 
     DefinitionNode newPlaceholder(JSAtom *atom, uint32_t blockid, const TokenPos &pos) {
         return Definition::PLACEHOLDER;
     }
 
-    Node newIdentifier(JSAtom *atom, const TokenPos &pos) { return NodeString; }
+    Node newIdentifier(JSAtom *atom, const TokenPos &pos) { return NodeName; }
     Node newNumber(double value, DecimalPoint decimalPoint, const TokenPos &pos) { return NodeGeneric; }
     Node newBooleanLiteral(bool cond, const TokenPos &pos) { return NodeGeneric; }
 
     Node newStringLiteral(JSAtom *atom, const TokenPos &pos) {
         lastAtom = atom;
         lastStringPos = pos;
-        return NodeString;
+        return NodeUnparenthesizedString;
     }
 
     Node newTemplateStringLiteral(JSAtom *atom, const TokenPos &pos) {
         return NodeGeneric;
     }
 
     Node newCallSiteObject(uint32_t begin, unsigned blockidGen) {
         return NodeGeneric;
@@ -130,28 +181,28 @@ class SyntaxParseHandler
     bool addElision(Node literal, const TokenPos &pos) { return true; }
     bool addSpreadElement(Node literal, uint32_t begin, Node inner) { return true; }
     bool addArrayElement(Node literal, Node element) { return true; }
 
     Node newObjectLiteral(uint32_t begin) { return NodeGeneric; }
     bool addPrototypeMutation(Node literal, uint32_t begin, Node expr) { return true; }
     bool addPropertyDefinition(Node literal, Node name, Node expr, bool isShorthand = false) { return true; }
     bool addMethodDefinition(Node literal, Node name, Node fn, JSOp op) { return true; }
-    Node newYieldExpression(uint32_t begin, Node value, Node gen) { return NodeGeneric; }
+    Node newYieldExpression(uint32_t begin, Node value, Node gen) { return NodeUnparenthesizedYieldExpr; }
     Node newYieldStarExpression(uint32_t begin, Node value, Node gen) { return NodeGeneric; }
 
     // Statements
 
     Node newStatementList(unsigned blockid, const TokenPos &pos) { return NodeGeneric; }
     void addStatementToList(Node list, Node stmt, ParseContext<SyntaxParseHandler> *pc) {}
     bool prependInitialYield(Node stmtList, Node gen) { return true; }
     Node newEmptyStatement(const TokenPos &pos) { return NodeGeneric; }
 
     Node newExprStatement(Node expr, uint32_t end) {
-        return expr == NodeString ? NodeStringExprStatement : NodeGeneric;
+        return expr == NodeUnparenthesizedString ? NodeStringExprStatement : NodeGeneric;
     }
 
     Node newIfStatement(uint32_t begin, Node cond, Node then, Node else_) { return NodeGeneric; }
     Node newDoWhileStatement(Node body, Node cond, const TokenPos &pos) { return NodeGeneric; }
     Node newWhileStatement(uint32_t begin, Node cond, Node body) { return NodeGeneric; }
     Node newSwitchStatement(uint32_t begin, Node discriminant, Node caseList) { return NodeGeneric; }
     Node newCaseOrDefault(uint32_t begin, Node expr, Node body) { return NodeGeneric; }
     Node newContinueStatement(PropertyName *label, const TokenPos &pos) { return NodeGeneric; }
@@ -190,23 +241,16 @@ class SyntaxParseHandler
 
     Node newForHead(ParseNodeKind kind, Node decls, Node lhs, Node rhs, const TokenPos &pos) {
         return NodeGeneric;
     }
 
     Node newLexicalScope(ObjectBox *blockbox) { return NodeGeneric; }
     void setLexicalScopeBody(Node block, Node body) {}
 
-    bool isOperationWithoutParens(Node pn, ParseNodeKind kind) {
-        // It is OK to return false here, callers should only use this method
-        // for reporting strict option warnings and parsing code which the
-        // syntax parser does not handle.
-        return false;
-    }
-
     bool finishInitializerAssignment(Node pn, Node init, JSOp op) { return true; }
 
     void setBeginPosition(Node pn, Node oth) {}
     void setBeginPosition(Node pn, uint32_t begin) {}
 
     void setEndPosition(Node pn, Node oth) {}
     void setEndPosition(Node pn, uint32_t end) {}
 
@@ -217,29 +261,58 @@ class SyntaxParseHandler
     }
 
     Node newList(ParseNodeKind kind, JSOp op = JSOP_NOP) {
         return NodeGeneric;
     }
     Node newList(ParseNodeKind kind, Node kid, JSOp op = JSOP_NOP) {
         return NodeGeneric;
     }
-    void addList(Node pn, Node kid) {}
-    bool isUnparenthesizedYield(Node pn) { return false; }
+
+    Node newCommaExpressionList(Node kid) {
+        return NodeUnparenthesizedCommaExpr;
+    }
+
+    void addList(Node list, Node kid) {
+        MOZ_ASSERT(list == NodeGeneric || list == NodeUnparenthesizedCommaExpr);
+    }
+
+    Node newAssignment(ParseNodeKind kind, Node lhs, Node rhs,
+                       ParseContext<SyntaxParseHandler> *pc, JSOp op)
+    {
+        if (kind == PNK_ASSIGN)
+            return NodeUnparenthesizedAssignment;
+        return newBinaryOrAppend(kind, lhs, rhs, pc, op);
+    }
+
+    bool isUnparenthesizedYieldExpression(Node node) {
+        return node == NodeUnparenthesizedYieldExpr;
+    }
+
+    bool isUnparenthesizedCommaExpression(Node node) {
+        return node == NodeUnparenthesizedCommaExpr;
+    }
+
+    bool isUnparenthesizedAssignment(Node node) {
+        return node == NodeUnparenthesizedAssignment;
+    }
 
     void setOp(Node pn, JSOp op) {}
     void setBlockId(Node pn, unsigned blockid) {}
     void setFlag(Node pn, unsigned flag) {}
     void setListFlag(Node pn, unsigned flag) {}
-    Node setInParens(Node pn) {
-        // String literals enclosed by parentheses are ignored during
-        // strict mode parsing.
-        return (pn == NodeString) ? NodeGeneric : pn;
+    MOZ_WARN_UNUSED_RESULT Node parenthesize(Node node) {
+        if (meaningMightChangeIfParenthesized(node))
+            return NodeGeneric;
+
+        // In all other cases, the parenthesized form of |node| is equivalent
+        // to the unparenthesized form: return |node| unchanged.
+        return node;
     }
-    Node setLikelyIIFE(Node pn) {
+    MOZ_WARN_UNUSED_RESULT Node setLikelyIIFE(Node pn) {
         return pn; // Remain in syntax-parse mode.
     }
     void setPrologue(Node pn) {}
 
     bool isConstant(Node pn) { return false; }
     PropertyName *isName(Node pn) {
         return (pn == NodeName) ? lastAtom->asPropertyName() : nullptr;
     }
--- a/js/src/gc/Verifier.cpp
+++ b/js/src/gc/Verifier.cpp
@@ -214,17 +214,16 @@ gc::GCRuntime::startVerifyPreBarriers()
     trc->term = trc->edgeptr + size;
 
     if (!trc->nodemap.init())
         goto oom;
 
     /* Create the root node. */
     trc->curnode = MakeNode(trc, nullptr, JSGCTraceKind(0));
 
-    /* We want MarkRuntime to save the roots to gcSavedRoots. */
     incrementalState = MARK_ROOTS;
 
     /* Make all the roots be edges emanating from the root node. */
     markRuntime(trc);
 
     VerifyNode *node;
     node = trc->curnode;
     if (trc->edgeptr == trc->term)
--- a/js/src/jit-test/tests/basic/hypot-approx.js
+++ b/js/src/jit-test/tests/basic/hypot-approx.js
@@ -16,10 +16,19 @@ assertNear(Math.hypot(1e-3, 1e-3, 1e-3),
 
 assertNear(Math.hypot(1e300, 1e300), 1.4142135623730952e+300);
 assertNear(Math.hypot(1e100, 1e200, 1e300), 1e300);
 
 assertNear(Math.hypot(1e3, 1e-3), 1000.0000000005);
 assertNear(Math.hypot(1e-300, 1e300), 1e300);
 assertNear(Math.hypot(1e3, 1e-3, 1e3, 1e-3), 1414.2135623738021555);
 
-for (var i = 1, j = 1; i < 2; i += 0.05, j += 0.05)
+assertNear(Math.hypot(1e1, 1e2, 1e3), Math.sqrt(1e2 + 1e4 + 1e6));
+assertNear(Math.hypot(1e1, 1e2, 1e3, 1e4), Math.sqrt(1e2 + 1e4 + 1e6 + 1e8));
+
+for (var i = 1, j = 2; i < 2; i += 0.05, j += 0.05)
     assertNear(Math.hypot(i, j), Math.sqrt(i * i + j * j));
+
+for (var i = 1, j = 2, k = 3; i < 2; i += 0.05, j += 0.05, k += 0.05)
+    assertNear(Math.hypot(i, j, k), Math.sqrt(i * i + j * j + k * k));
+
+for (var i = 1, j = 2, k = 3, l = 4; i < 2; i += 0.05, j += 0.05, k += 0.05, l += 0.5)
+    assertNear(Math.hypot(i, j, k, l), Math.sqrt(i * i + j * j + k * k + l * l));
--- a/js/src/jit-test/tests/basic/hypot-exact.js
+++ b/js/src/jit-test/tests/basic/hypot-exact.js
@@ -23,35 +23,52 @@ for (var inf of [Infinity, -Infinity]) {
     assertEq(Math.hypot(0, 0, inf), Infinity);
 
     assertEq(Math.hypot(inf, NaN), Infinity);
     assertEq(Math.hypot(NaN, inf), Infinity);
 
     assertEq(Math.hypot(inf, NaN, NaN), Infinity);
     assertEq(Math.hypot(NaN, inf, NaN), Infinity);
     assertEq(Math.hypot(NaN, NaN, inf), Infinity);
+
+    assertEq(Math.hypot(inf, NaN, NaN, NaN), Infinity);
+    assertEq(Math.hypot(NaN, inf, NaN, NaN), Infinity);
+    assertEq(Math.hypot(NaN, NaN, inf, NaN), Infinity);
+    assertEq(Math.hypot(NaN, NaN, NaN, inf), Infinity);
 }
 
 // If no argument is +∞ or −∞, and any argument is NaN, the result is NaN.
 assertEq(Math.hypot(NaN), NaN);
 
 assertEq(Math.hypot(NaN, 0), NaN);
 assertEq(Math.hypot(0, NaN), NaN);
 
 assertEq(Math.hypot(NaN, NaN), NaN);
 
 assertEq(Math.hypot(NaN, 0, 0), NaN);
 assertEq(Math.hypot(0, NaN, 0), NaN);
 assertEq(Math.hypot(0, 0, NaN), NaN);
 
+assertEq(Math.hypot(NaN, 0, 0, 0), NaN);
+assertEq(Math.hypot(0, NaN, 0, 0), NaN);
+assertEq(Math.hypot(0, 0, NaN, 0), NaN);
+assertEq(Math.hypot(0, 0, 0, NaN), NaN);
+
 assertEq(Math.hypot(Number.MAX_VALUE, Number.MIN_VALUE, NaN), NaN);
+assertEq(Math.hypot(Number.MAX_VALUE, Number.MIN_VALUE, Number.MIN_VALUE, NaN), NaN);
 
 // If all arguments are either +0 or -0, the result is +0.
 assertEq(Math.hypot(-0, -0), +0);
 assertEq(Math.hypot(+0, -0), +0);
 
 assertEq(Math.hypot(-0, -0, -0), +0);
 assertEq(Math.hypot(+0, -0, -0), +0);
 assertEq(Math.hypot(-0, +0, -0), +0);
 assertEq(Math.hypot(+0, +0, -0), +0);
 
+assertEq(Math.hypot(-0, -0, -0, -0), +0);
+assertEq(Math.hypot(+0, -0, -0, -0), +0);
+assertEq(Math.hypot(-0, -0, +0, -0), +0);
+assertEq(Math.hypot(+0, +0, +0, -0), +0);
+assertEq(Math.hypot(-0, -0, -0, +0), +0);
+
 // The length property of the hypot function is 2.
 assertEq(Math.hypot.length, 2);
--- a/js/src/jit-test/tests/ion/dce-with-rinstructions.js
+++ b/js/src/jit-test/tests/ion/dce-with-rinstructions.js
@@ -1057,38 +1057,91 @@ function rtrunc_to_int32_object(i) {
 var uceFault_trunc_to_int32_string = eval(uneval(uceFault).replace('uceFault', 'uceFault_trunc_to_int32_string'));
 function rtrunc_to_int32_string(i) {
     var x = (i + "0") | 0;
     if (uceFault_trunc_to_int32_string(i) || uceFault_trunc_to_int32_string(i))
         assertEq(x, (i + "0") | 0);
     return i;
 }
 
-var uceFault_hypot_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_hypot_number'));
-function rhypot_number(i) {
+var uceFault_hypot_number_2args = eval(uneval(uceFault).replace('uceFault', 'uceFault_hypot_number_2args'));
+function rhypot_number_2args(i) {
     var x = Math.hypot(i, i + 1);
-    if (uceFault_hypot_number(i) || uceFault_hypot_number(i))
+    if (uceFault_hypot_number_2args(i) || uceFault_hypot_number_2args(i))
         assertEq(x, Math.sqrt(i * i + (i + 1) * (i + 1)));
     return i;
 }
 
-var uceFault_hypot_object = eval(uneval(uceFault).replace('uceFault', 'uceFault_hypot_object'));
-function rhypot_object(i) {
+var uceFault_hypot_number_3args = eval(uneval(uceFault).replace('uceFault', 'uceFault_hypot_number_3args'));
+function rhypot_number_3args(i) {
+    var x = Math.hypot(i, i + 1, i + 2);
+    if (uceFault_hypot_number_3args(i) || uceFault_hypot_number_3args(i))
+        assertEq(x, Math.sqrt(i * i + (i + 1) * (i + 1) + (i + 2) * (i + 2)));
+    return i;
+}
+
+var uceFault_hypot_number_4args = eval(uneval(uceFault).replace('uceFault', 'uceFault_hypot_number_4args'));
+function rhypot_number_4args(i) {
+    var x = Math.hypot(i, i + 1, i + 2, i + 3);
+    if (uceFault_hypot_number_4args(i) || uceFault_hypot_number_4args(i))
+        assertEq(x, Math.sqrt(i * i + (i + 1) * (i + 1) + (i + 2) * (i + 2) + (i + 3) * (i + 3)));
+    return i;
+}
+
+var uceFault_hypot_object_2args = eval(uneval(uceFault).replace('uceFault', 'uceFault_hypot_object_2args'));
+function rhypot_object_2args(i) {
     var t0 = i;
     var t1 = i + 1;
     var o0 = { valueOf: function () { return t0; } };
     var o1 = { valueOf: function () { return t1; } };
     var x = Math.hypot(o0, o1);
     t0 = 1000;
     t1 = 2000;
-    if (uceFault_hypot_object(i) || uceFault_hypot_object(i) )
+    if (uceFault_hypot_object_2args(i) || uceFault_hypot_object_2args(i) )
         assertEq(x, Math.sqrt(i * i + (i + 1) * (i + 1)));
     return i;
 }
 
+var uceFault_hypot_object_3args = eval(uneval(uceFault).replace('uceFault', 'uceFault_hypot_object_3args'));
+function rhypot_object_3args(i) {
+    var t0 = i;
+    var t1 = i + 1;
+    var t2 = i + 2;
+    var o0 = { valueOf: function () { return t0; } };
+    var o1 = { valueOf: function () { return t1; } };
+    var o2 = { valueOf: function () { return t2; } };
+    var x = Math.hypot(o0, o1, o2);
+    t0 = 1000;
+    t1 = 2000;
+    t2 = 3000;
+    if (uceFault_hypot_object_3args(i) || uceFault_hypot_object_3args(i) )
+        assertEq(x, Math.sqrt(i * i + (i + 1) * (i + 1) + (i + 2) * (i + 2)));
+    return i;
+}
+
+var uceFault_hypot_object_4args = eval(uneval(uceFault).replace('uceFault', 'uceFault_hypot_object_4args'));
+function rhypot_object_4args(i) {
+    var t0 = i;
+    var t1 = i + 1;
+    var t2 = i + 2;
+    var t3 = i + 3;
+    var o0 = { valueOf: function () { return t0; } };
+    var o1 = { valueOf: function () { return t1; } };
+    var o2 = { valueOf: function () { return t2; } };
+    var o3 = { valueOf: function () { return t3; } };
+    var x = Math.hypot(o0, o1, o2, o3);
+    t0 = 1000;
+    t1 = 2000;
+    t2 = 3000;
+    t3 = 4000;
+    if (uceFault_hypot_object_4args(i) || uceFault_hypot_object_4args(i) )
+        assertEq(x, Math.sqrt(i * i + (i + 1) * (i + 1) + (i + 2) * (i + 2) + (i + 3) * (i + 3)));
+    return i;
+}
+
 var uceFault_sin_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_sin_number'));
 function rsin_number(i) {
     var x = Math.sin(i);
     if (uceFault_sin_number(i) || uceFault_sin_number(i))
         assertEq(x, Math.sin(i));
     return i;
 }
 
@@ -1219,18 +1272,22 @@ for (i = 0; i < 100; i++) {
     rtypeof(i);
     rtodouble_value(i);
     rtodouble_number(i);
     rtofloat32_number(i);
     rtofloat32_object(i);
     rtrunc_to_int32_number(i);
     rtrunc_to_int32_object(i);
     rtrunc_to_int32_string(i);
-    rhypot_number(i);
-    rhypot_object(i);
+    rhypot_number_2args(i);
+    rhypot_number_3args(i);
+    rhypot_number_4args(i);
+    rhypot_object_2args(i);
+    rhypot_object_3args(i);
+    rhypot_object_4args(i);
     rsin_number(i);
     rsin_object(i);
     rlog_number(i);
     rlog_object(i);
 }
 
 // Test that we can refer multiple time to the same recover instruction, as well
 // as chaining recover instructions.
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -3903,24 +3903,35 @@ CodeGenerator::visitAtan2D(LAtan2D *lir)
 
     MOZ_ASSERT(ToFloatRegister(lir->output()) == ReturnDoubleReg);
 }
 
 void
 CodeGenerator::visitHypot(LHypot *lir)
 {
     Register temp = ToRegister(lir->temp());
-    FloatRegister x = ToFloatRegister(lir->x());
-    FloatRegister y = ToFloatRegister(lir->y());
-
-    masm.setupUnalignedABICall(2, temp);
-    masm.passABIArg(x, MoveOp::DOUBLE);
-    masm.passABIArg(y, MoveOp::DOUBLE);
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ecmaHypot), MoveOp::DOUBLE);
-
+    uint32_t numArgs = lir->numArgs();
+    masm.setupUnalignedABICall(numArgs, temp);
+
+    for (uint32_t i = 0 ; i < numArgs; ++i)
+        masm.passABIArg(ToFloatRegister(lir->getOperand(i)), MoveOp::DOUBLE);
+
+    switch(numArgs) {
+      case 2:
+        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ecmaHypot), MoveOp::DOUBLE);
+        break;
+      case 3:
+        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, hypot3), MoveOp::DOUBLE);
+        break;
+      case 4:
+        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, hypot4), MoveOp::DOUBLE);
+        break;
+      default:
+        MOZ_CRASH("Unexpected number of arguments to hypot function.");
+    }
     MOZ_ASSERT(ToFloatRegister(lir->output()) == ReturnDoubleReg);
 }
 
 void
 CodeGenerator::visitNewArray(LNewArray *lir)
 {
     Register objReg = ToRegister(lir->output());
     Register tempReg = ToRegister(lir->temp());
--- a/js/src/jit/IonTypes.h
+++ b/js/src/jit/IonTypes.h
@@ -631,17 +631,24 @@ enum ABIFunctionType
     // double f(int, double)
     Args_Double_IntDouble = Args_Double_None |
         (ArgType_Double << (ArgType_Shift * 1)) |
         (ArgType_General << (ArgType_Shift * 2)),
 
     // int f(int, double)
     Args_Int_IntDouble = Args_General0 |
         (ArgType_Double << (ArgType_Shift * 1)) |
-        (ArgType_General << (ArgType_Shift * 2))
+        (ArgType_General << (ArgType_Shift * 2)),
+
+    // double f(double, double, double)
+    Args_Double_DoubleDoubleDouble = Args_Double_DoubleDouble | (ArgType_Double << (ArgType_Shift * 3)),
+
+    // double f(double, double, double, double)
+    Args_Double_DoubleDoubleDoubleDouble = Args_Double_DoubleDoubleDouble | (ArgType_Double << (ArgType_Shift * 4))
+
 };
 
 enum class BarrierKind : uint32_t {
     // No barrier is needed.
     NoBarrier,
 
     // The barrier only has to check the value's type tag is in the TypeSet.
     // Specific object types don't have to be checked.
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -2960,26 +2960,50 @@ class LAtan2D : public LCallInstructionH
         return getTemp(0);
     }
 
     const LDefinition *output() {
         return getDef(0);
     }
 };
 
-class LHypot : public LCallInstructionHelper<1, 2, 1>
-{
+class LHypot : public LCallInstructionHelper<1, 4, 1>
+{
+    uint32_t numOperands_;
   public:
     LIR_HEADER(Hypot)
-    LHypot(const LAllocation &x, const LAllocation &y, const LDefinition &temp) {
+    LHypot(const LAllocation &x, const LAllocation &y, const LDefinition &temp)
+      : numOperands_(2)
+    {
         setOperand(0, x);
         setOperand(1, y);
         setTemp(0, temp);
     }
 
+    LHypot(const LAllocation &x, const LAllocation &y, const LAllocation &z, const LDefinition &temp)
+      : numOperands_(3)
+    {
+        setOperand(0, x);
+        setOperand(1, y);
+        setOperand(2, z);
+        setTemp(0, temp);
+    }
+
+    LHypot(const LAllocation &x, const LAllocation &y, const LAllocation &z, const LAllocation &w, const LDefinition &temp)
+      : numOperands_(4)
+    {
+        setOperand(0, x);
+        setOperand(1, y);
+        setOperand(2, z);
+        setOperand(3, w);
+        setTemp(0, temp);
+    }
+
+    uint32_t numArgs() const { return numOperands_; }
+
     const LAllocation *x() {
         return getOperand(0);
     }
 
     const LAllocation *y() {
         return getOperand(1);
     }
 
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -1301,23 +1301,44 @@ LIRGenerator::visitAtan2(MAtan2 *ins)
     LAtan2D *lir = new(alloc()) LAtan2D(useRegisterAtStart(y), useRegisterAtStart(x),
                                         tempFixed(CallTempReg0));
     defineReturn(lir, ins);
 }
 
 void
 LIRGenerator::visitHypot(MHypot *ins)
 {
-    MDefinition *x = ins->x();
-    MOZ_ASSERT(x->type() == MIRType_Double);
-
-    MDefinition *y = ins->y();
-    MOZ_ASSERT(y->type() == MIRType_Double);
-
-    LHypot *lir = new(alloc()) LHypot(useRegisterAtStart(x), useRegisterAtStart(y), tempFixed(CallTempReg0));
+    LHypot *lir = nullptr;
+    uint32_t length = ins->numOperands();
+    for (uint32_t i = 0; i < length; ++i)
+        MOZ_ASSERT(ins->getOperand(i)->type() == MIRType_Double);
+
+    switch(length) {
+      case 2:
+        lir = new(alloc()) LHypot(useRegisterAtStart(ins->getOperand(0)),
+                                  useRegisterAtStart(ins->getOperand(1)),
+                                  tempFixed(CallTempReg0));
+        break;
+      case 3:
+        lir = new(alloc()) LHypot(useRegisterAtStart(ins->getOperand(0)),
+                                  useRegisterAtStart(ins->getOperand(1)),
+                                  useRegisterAtStart(ins->getOperand(2)),
+                                  tempFixed(CallTempReg0));
+        break;
+      case 4:
+        lir = new(alloc()) LHypot(useRegisterAtStart(ins->getOperand(0)),
+                                  useRegisterAtStart(ins->getOperand(1)),
+                                  useRegisterAtStart(ins->getOperand(2)),
+                                  useRegisterAtStart(ins->getOperand(3)),
+                                  tempFixed(CallTempReg0));
+        break;
+      default:
+        MOZ_CRASH("Unexpected number of arguments to LHypot.");
+    }
+
     defineReturn(lir, ins);
 }
 
 void
 LIRGenerator::visitPow(MPow *ins)
 {
     MDefinition *input = ins->input();
     MOZ_ASSERT(input->type() == MIRType_Double);
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -997,31 +997,40 @@ IonBuilder::inlineMathAtan2(CallInfo &ca
 }
 
 IonBuilder::InliningStatus
 IonBuilder::inlineMathHypot(CallInfo &callInfo)
 {
     if (callInfo.constructing())
         return InliningStatus_NotInlined;
 
-    if (callInfo.argc() != 2)
+    uint32_t argc = callInfo.argc();
+    if (argc < 2 || argc > 4)
         return InliningStatus_NotInlined;
 
     if (getInlineReturnType() != MIRType_Double)
         return InliningStatus_NotInlined;
 
-    MIRType argType0 = callInfo.getArg(0)->type();
-    MIRType argType1 = callInfo.getArg(1)->type();
-
-    if (!IsNumberType(argType0) || !IsNumberType(argType1))
+    MDefinitionVector vector(alloc());
+    if (!vector.reserve(argc))
         return InliningStatus_NotInlined;
 
+    for (uint32_t i = 0; i < argc; ++i) {
+        MDefinition * arg = callInfo.getArg(i);
+        if (!IsNumberType(arg->type()))
+            return InliningStatus_NotInlined;
+        vector.infallibleAppend(arg);
+    }
+
     callInfo.setImplicitlyUsedUnchecked();
-
-    MHypot *hypot = MHypot::New(alloc(), callInfo.getArg(0), callInfo.getArg(1));
+    MHypot *hypot = MHypot::New(alloc(), vector);
+
+    if (!hypot)
+        return InliningStatus_NotInlined;
+
     current->add(hypot);
     current->push(hypot);
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
 IonBuilder::inlineMathPow(CallInfo &callInfo)
 {
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -2226,16 +2226,28 @@ MMathFunction::trySpecializeFloat32(Temp
             ConvertDefinitionToDouble<0>(alloc, input(), this);
         return;
     }
 
     setResultType(MIRType_Float32);
     setPolicyType(MIRType_Float32);
 }
 
+MHypot *MHypot::New(TempAllocator &alloc, const MDefinitionVector & vector)
+{
+    uint32_t length = vector.length();
+    MHypot * hypot = new(alloc) MHypot;
+    if (!hypot->init(alloc, length))
+        return nullptr;
+
+    for (uint32_t i = 0; i < length; ++i)
+        hypot->initOperand(i, vector[i]);
+    return hypot;
+}
+
 bool
 MAdd::fallible() const
 {
     // the add is fallible if range analysis does not say that it is finite, AND
     // either the truncation analysis shows that there are non-truncated uses.
     if (truncateKind() >= IndirectTruncate)
         return false;
     if (range() && range()->hasInt32Bounds())
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -4789,18 +4789,18 @@ class MTruncateToInt32
     void computeRange(TempAllocator &alloc) MOZ_OVERRIDE;
     TruncateKind operandTruncateKind(size_t index) const MOZ_OVERRIDE;
 # ifdef DEBUG
     bool isConsistentFloat32Use(MUse *use) const MOZ_OVERRIDE {
         return true;
     }
 #endif
 
-    bool writeRecoverData(CompactBufferWriter &writer) const;
-    bool canRecoverOnBailout() const {
+    bool writeRecoverData(CompactBufferWriter &writer) const MOZ_OVERRIDE;
+    bool canRecoverOnBailout() const MOZ_OVERRIDE {
         return input()->type() < MIRType_Symbol;
     }
 
     ALLOW_CLONE(MTruncateToInt32)
 };
 
 // Converts any type to a string
 class MToString :
@@ -5502,39 +5502,28 @@ class MAtan2
         return true;
     }
 
     ALLOW_CLONE(MAtan2)
 };
 
 // Inline implementation of Math.hypot().
 class MHypot
-  : public MBinaryInstruction,
-    public MixPolicy<DoublePolicy<0>, DoublePolicy<1> >::Data
-{
-    MHypot(MDefinition *y, MDefinition *x)
-      : MBinaryInstruction(x, y)
+  : public MVariadicInstruction,
+    public AllDoublePolicy::Data
+{
+    MHypot()
     {
         setResultType(MIRType_Double);
         setMovable();
     }
 
   public:
     INSTRUCTION_HEADER(Hypot)
-    static MHypot *New(TempAllocator &alloc, MDefinition *x, MDefinition *y) {
-        return new(alloc) MHypot(y, x);
-    }
-
-    MDefinition *x() const {
-        return getOperand(0);
-    }
-
-    MDefinition *y() const {
-        return getOperand(1);
-    }
+    static MHypot *New(TempAllocator &alloc, const MDefinitionVector &vector);
 
     bool congruentTo(const MDefinition *ins) const MOZ_OVERRIDE {
         return congruentIfOperandsEqual(ins);
     }
 
     AliasSet getAliasSet() const MOZ_OVERRIDE {
         return AliasSet::None();
     }
@@ -5543,17 +5532,23 @@ class MHypot
         return true;
     }
 
     bool writeRecoverData(CompactBufferWriter &writer) const MOZ_OVERRIDE;
     bool canRecoverOnBailout() const MOZ_OVERRIDE {
         return true;
     }
 
-    ALLOW_CLONE(MHypot)
+    bool canClone() const {
+        return true;
+    }
+
+    MInstruction *clone(TempAllocator &alloc, const MDefinitionVector &inputs) const {
+       return MHypot::New(alloc, inputs);
+    }
 };
 
 // Inline implementation of Math.pow().
 class MPow
   : public MBinaryInstruction,
     public PowPolicy::Data
 {
     MPow(MDefinition *input, MDefinition *power, MIRType powerType)
--- a/js/src/jit/Recover.cpp
+++ b/js/src/jit/Recover.cpp
@@ -869,33 +869,34 @@ RAtan2::recover(JSContext *cx, SnapshotI
     return true;
 }
 
 bool
 MHypot::writeRecoverData(CompactBufferWriter &writer) const
 {
     MOZ_ASSERT(canRecoverOnBailout());
     writer.writeUnsigned(uint32_t(RInstruction::Recover_Hypot));
+    writer.writeUnsigned(uint32_t(numOperands()));
     return true;
 }
 
 RHypot::RHypot(CompactBufferReader &reader)
+    : numOperands_(reader.readUnsigned())
 { }
 
 bool
 RHypot::recover(JSContext *cx, SnapshotIterator &iter) const
 {
     JS::AutoValueVector vec(cx);
 
-    // currently, only 2 args can be saved in MIR
-    if (!vec.reserve(2))
+    if (!vec.reserve(numOperands_))
         return false;
 
-    vec.infallibleAppend(iter.read());
-    vec.infallibleAppend(iter.read());
+    for (uint32_t i = 0 ; i < numOperands_ ; ++i)
+       vec.infallibleAppend(iter.read());
 
     RootedValue result(cx);
 
     if(!js::math_hypot_handle(cx, vec, &result))
         return false;
 
     iter.storeInstructionResult(result);
     return true;
--- a/js/src/jit/Recover.h
+++ b/js/src/jit/Recover.h
@@ -472,21 +472,24 @@ class RAtan2 MOZ_FINAL : public RInstruc
         return 2;
     }
 
     bool recover(JSContext *cx, SnapshotIterator &iter) const;
 };
 
 class RHypot MOZ_FINAL : public RInstruction
 {
+   private:
+     uint32_t numOperands_;
+
    public:
      RINSTRUCTION_HEADER_(Hypot)
 
      virtual uint32_t numOperands() const {
-         return 2;
+         return numOperands_;
      }
 
      bool recover(JSContext *cx, SnapshotIterator &iter) const;
 };
 
 class RMathFunction MOZ_FINAL : public RInstruction
 {
   private:
--- a/js/src/jit/TypePolicy.cpp
+++ b/js/src/jit/TypePolicy.cpp
@@ -90,16 +90,36 @@ ArithPolicy::adjustInputs(TempAllocator 
         if (!replace->typePolicy()->adjustInputs(alloc, replace))
             return false;
     }
 
     return true;
 }
 
 bool
+AllDoublePolicy::adjustInputs(TempAllocator &alloc, MInstruction *ins)
+{
+    for (size_t i = 0, e = ins->numOperands(); i < e; i++) {
+        MDefinition *in = ins->getOperand(i);
+        if (in->type() == MIRType_Double)
+            continue;
+
+        MInstruction *replace = MToDouble::New(alloc, in);
+
+        ins->block()->insertBefore(ins, replace);
+        ins->replaceOperand(i, replace);
+
+        if (!replace->typePolicy()->adjustInputs(alloc, replace))
+            return false;
+    }
+
+    return true;
+}
+
+bool
 ComparePolicy::adjustInputs(TempAllocator &alloc, MInstruction *def)
 {
     MOZ_ASSERT(def->isCompare());
     MCompare *compare = def->toCompare();
 
     // Convert Float32 operands to doubles
     for (size_t i = 0; i < 2; i++) {
         MDefinition *in = def->getOperand(i);
@@ -1020,16 +1040,17 @@ FilterTypeSetPolicy::adjustInputs(TempAl
     _(FilterTypeSetPolicy)                      \
     _(InstanceOfPolicy)                         \
     _(PowPolicy)                                \
     _(StoreTypedArrayElementStaticPolicy)       \
     _(StoreTypedArrayHolePolicy)                \
     _(StoreTypedArrayPolicy)                    \
     _(StoreUnboxedObjectOrNullPolicy)           \
     _(TestPolicy)                               \
+    _(AllDoublePolicy)                          \
     _(ToDoublePolicy)                           \
     _(ToInt32Policy)                            \
     _(ToStringPolicy)                           \
     _(TypeBarrierPolicy)
 
 #define TEMPLATE_TYPE_POLICY_LIST(_)                                    \
     _(BoxExceptPolicy<0, MIRType_String>)                               \
     _(BoxPolicy<0>)                                                     \
--- a/js/src/jit/TypePolicy.h
+++ b/js/src/jit/TypePolicy.h
@@ -88,16 +88,23 @@ class BoxInputsPolicy MOZ_FINAL : public
 
 class ArithPolicy MOZ_FINAL : public TypePolicy
 {
   public:
     SPECIALIZATION_DATA_;
     virtual bool adjustInputs(TempAllocator &alloc, MInstruction *def) MOZ_OVERRIDE;
 };
 
+class AllDoublePolicy MOZ_FINAL : public TypePolicy
+{
+  public:
+    EMPTY_DATA_;
+    bool adjustInputs(TempAllocator &alloc, MInstruction *def);
+};
+
 class BitwisePolicy MOZ_FINAL : public TypePolicy
 {
   public:
     SPECIALIZATION_DATA_;
     virtual bool adjustInputs(TempAllocator &alloc, MInstruction *def) MOZ_OVERRIDE;
 };
 
 class ComparePolicy MOZ_FINAL : public TypePolicy
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -4155,16 +4155,18 @@ AssertValidABIFunctionType(uint32_t pass
       case Args_Int_Double:
       case Args_Float32_Float32:
       case Args_Double_Double:
       case Args_Double_Int:
       case Args_Double_DoubleInt:
       case Args_Double_DoubleDouble:
       case Args_Double_IntDouble:
       case Args_Int_IntDouble:
+      case Args_Double_DoubleDoubleDouble:
+      case Args_Double_DoubleDoubleDoubleDouble:
         break;
       default:
         MOZ_CRASH("Unexpected type");
     }
 }
 #endif
 
 void
--- a/js/src/jit/arm/Simulator-arm.cpp
+++ b/js/src/jit/arm/Simulator-arm.cpp
@@ -1445,16 +1445,25 @@ Simulator::getFpArgs(double *x, double *
     } else {
         *x = get_double_from_register_pair(0);
         *y = get_double_from_register_pair(2);
         *z = get_register(2);
     }
 }
 
 void
+Simulator::getFpFromStack(int32_t *stack, double *x)
+{
+    MOZ_ASSERT(stack && x);
+    char buffer[2 * sizeof(stack[0])];
+    memcpy(buffer, stack, 2 * sizeof(stack[0]));
+    memcpy(x, buffer, 2 * sizeof(stack[0]));
+}
+
+void
 Simulator::setCallResultDouble(double result)
 {
     // The return value is either in r0/r1 or d0.
     if (UseHardFpABI()) {
         char buffer[2 * sizeof(vfp_registers_[0])];
         memcpy(buffer, &result, sizeof(buffer));
         // Copy result to d0.
         memcpy(vfp_registers_, buffer, sizeof(buffer));
@@ -2073,16 +2082,19 @@ typedef double (*Prototype_Double_Int)(i
 typedef int32_t (*Prototype_Int_Double)(double arg0);
 typedef float (*Prototype_Float32_Float32)(float arg0);
 
 typedef double (*Prototype_DoubleInt)(double arg0, int32_t arg1);
 typedef double (*Prototype_Double_IntDouble)(int32_t arg0, double arg1);
 typedef double (*Prototype_Double_DoubleDouble)(double arg0, double arg1);
 typedef int32_t (*Prototype_Int_IntDouble)(int32_t arg0, double arg1);
 
+typedef double (*Prototype_Double_DoubleDoubleDouble)(double arg0, double arg1, double arg2);
+typedef double (*Prototype_Double_DoubleDoubleDoubleDouble)(double arg0, double arg1,
+                                                            double arg2, double arg3);
 
 // Fill the volatile registers with scratch values.
 //
 // Some of the ABI calls assume that the float registers are not scratched, even
 // though the ABI defines them as volatile - a performance optimization. These
 // are all calls passing operands in integer registers, so for now the simulator
 // does not scratch any float registers for these calls. Should try to narrow it
 // further in future.
@@ -2294,16 +2306,41 @@ Simulator::softwareInterrupt(SimInstruct
             else
                 dval0 = get_double_from_register_pair(2);
             Prototype_Int_IntDouble target = reinterpret_cast<Prototype_Int_IntDouble>(external);
             int32_t result = target(ival, dval0);
             scratchVolatileRegisters(/* scratchFloat = true */);
             set_register(r0, result);
             break;
           }
+          case Args_Double_DoubleDoubleDouble: {
+            double dval0, dval1, dval2;
+            int32_t ival;
+            getFpArgs(&dval0, &dval1, &ival);
+            // the last argument is on stack
+            getFpFromStack(stack_pointer, &dval2);
+            Prototype_Double_DoubleDoubleDouble target = reinterpret_cast<Prototype_Double_DoubleDoubleDouble>(external);
+            double dresult = target(dval0, dval1, dval2);
+            scratchVolatileRegisters(/* scratchFloat = true */);
+            setCallResultDouble(dresult);
+            break;
+         }
+         case Args_Double_DoubleDoubleDoubleDouble: {
+            double dval0, dval1, dval2, dval3;
+            int32_t ival;
+            getFpArgs(&dval0, &dval1, &ival);
+            // the two last arguments are on stack
+            getFpFromStack(stack_pointer, &dval2);
+            getFpFromStack(stack_pointer + 2, &dval3);
+            Prototype_Double_DoubleDoubleDoubleDouble target = reinterpret_cast<Prototype_Double_DoubleDoubleDoubleDouble>(external);
+            double dresult = target(dval0, dval1, dval2, dval3);
+            scratchVolatileRegisters(/* scratchFloat = true */);
+            setCallResultDouble(dresult);
+            break;
+          }
           default:
             MOZ_CRASH("call");
         }
 
         if (single_stepping_)
             single_step_callback_(single_step_callback_arg_, this, nullptr);
 
         set_register(lr, saved_lr);
--- a/js/src/jit/arm/Simulator-arm.h
+++ b/js/src/jit/arm/Simulator-arm.h
@@ -284,16 +284,17 @@ class Simulator
     bool skipCalleeSavedRegsCheck;
 
     // Runtime call support.
     static void *RedirectNativeFunction(void *nativeFunction, ABIFunctionType type);
 
   private:
     // Handle arguments and return value for runtime FP functions.
     void getFpArgs(double *x, double *y, int32_t *z);
+    void getFpFromStack(int32_t *stack, double *x1);
     void setCallResultDouble(double result);
     void setCallResultFloat(float result);
     void setCallResult(int64_t res);
     void scratchVolatileRegisters(bool scratchFloat = true);
 
     template<class ReturnType, int register_size>
     ReturnType getFromVFPRegister(int reg_index);
 
--- a/js/src/jit/mips/Assembler-mips.cpp
+++ b/js/src/jit/mips/Assembler-mips.cpp
@@ -964,26 +964,26 @@ BufferOffset
 Assembler::as_clz(Register rd, Register rs)
 {
     return writeInst(InstReg(op_special2, rs, rd, rd, ff_clz).encode());
 }
 
 BufferOffset
 Assembler::as_ins(Register rt, Register rs, uint16_t pos, uint16_t size)
 {
-    MOZ_ASSERT(pos < 32 && size != 0 && size <= 32 && pos + size != 0 && pos + size >= 32);
+    MOZ_ASSERT(pos < 32 && size != 0 && size <= 32 && pos + size != 0 && pos + size <= 32);
     Register rd;
     rd = Register::FromCode(pos + size - 1);
     return writeInst(InstReg(op_special3, rs, rt, rd, pos, ff_ins).encode());
 }
 
 BufferOffset
 Assembler::as_ext(Register rt, Register rs, uint16_t pos, uint16_t size)
 {
-    MOZ_ASSERT(pos < 32 && size != 0 && size <= 32 && pos + size != 0 && pos + size >= 32);
+    MOZ_ASSERT(pos < 32 && size != 0 && size <= 32 && pos + size != 0 && pos + size <= 32);
     Register rd;
     rd = Register::FromCode(size - 1);
     return writeInst(InstReg(op_special3, rs, rt, rd, pos, ff_ext).encode());
 }
 
 // FP instructions
 BufferOffset
 Assembler::as_ld(FloatRegister fd, Register base, int32_t off)
--- a/js/src/jit/mips/MacroAssembler-mips.cpp
+++ b/js/src/jit/mips/MacroAssembler-mips.cpp
@@ -3467,16 +3467,18 @@ AssertValidABIFunctionType(uint32_t pass
       case Args_Int_Double:
       case Args_Float32_Float32:
       case Args_Double_Double:
       case Args_Double_Int:
       case Args_Double_DoubleInt:
       case Args_Double_DoubleDouble:
       case Args_Double_IntDouble:
       case Args_Int_IntDouble:
+      case Args_Double_DoubleDoubleDouble:
+      case Args_Double_DoubleDoubleDoubleDouble:
         break;
       default:
         MOZ_CRASH("Unexpected type");
     }
 }
 #endif
 
 void
--- a/js/src/jit/mips/MacroAssembler-mips.h
+++ b/js/src/jit/mips/MacroAssembler-mips.h
@@ -227,17 +227,17 @@ class MacroAssemblerMIPS : public Assemb
     void ma_b(Register lhs, Register rhs, Label *l, Condition c, JumpKind jumpKind = LongJump);
     void ma_b(Register lhs, Imm32 imm, Label *l, Condition c, JumpKind jumpKind = LongJump);
     void ma_b(Register lhs, ImmPtr imm, Label *l, Condition c, JumpKind jumpKind = LongJump) {
         ma_b(lhs, Imm32(uint32_t(imm.value)), l, c, jumpKind);
     }
     void ma_b(Register lhs, ImmGCPtr imm, Label *l, Condition c, JumpKind jumpKind = LongJump) {
         MOZ_ASSERT(lhs != ScratchRegister);
         ma_li(ScratchRegister, imm);
-        ma_b(lhs, Imm32(uint32_t(imm.value)), l, c, jumpKind);
+        ma_b(lhs, ScratchRegister, l, c, jumpKind);
     }
     void ma_b(Register lhs, Address addr, Label *l, Condition c, JumpKind jumpKind = LongJump);
     void ma_b(Address addr, Imm32 imm, Label *l, Condition c, JumpKind jumpKind = LongJump);
     void ma_b(Address addr, Register rhs, Label *l, Condition c, JumpKind jumpKind = LongJump) {
         MOZ_ASSERT(rhs != ScratchRegister);
         ma_lw(ScratchRegister, addr);
         ma_b(ScratchRegister, rhs, l, c, jumpKind);
     }
--- a/js/src/jit/mips/Simulator-mips.cpp
+++ b/js/src/jit/mips/Simulator-mips.cpp
@@ -1546,16 +1546,24 @@ void
 Simulator::getFpArgs(double *x, double *y, int32_t *z)
 {
     *x = getFpuRegisterDouble(12);
     *y = getFpuRegisterDouble(14);
     *z = getRegister(a2);
 }
 
 void
+Simulator::getFpFromStack(int32_t *stack, double *x)
+{
+    MOZ_ASSERT(stack);
+    MOZ_ASSERT(x);
+    memcpy(x, stack, sizeof(double));
+}
+
+void
 Simulator::setCallResultDouble(double result)
 {
     setFpuRegisterDouble(f0, result);
 }
 
 void
 Simulator::setCallResultFloat(float result)
 {
@@ -1852,16 +1860,20 @@ typedef double (*Prototype_Double_Int)(i
 typedef int32_t (*Prototype_Int_Double)(double arg0);
 typedef float (*Prototype_Float32_Float32)(float arg0);
 
 typedef double (*Prototype_DoubleInt)(double arg0, int32_t arg1);
 typedef double (*Prototype_Double_IntDouble)(int32_t arg0, double arg1);
 typedef double (*Prototype_Double_DoubleDouble)(double arg0, double arg1);
 typedef int32_t (*Prototype_Int_IntDouble)(int32_t arg0, double arg1);
 
+typedef double (*Prototype_Double_DoubleDoubleDouble)(double arg0, double arg1, double arg2);
+typedef double (*Prototype_Double_DoubleDoubleDoubleDouble)(double arg0, double arg1,
+                                                            double arg2, double arg3);
+
 // Software interrupt instructions are used by the simulator to call into C++.
 void
 Simulator::softwareInterrupt(SimInstruction *instr)
 {
     int32_t func = instr->functionFieldRaw();
     uint32_t code = (func == ff_break) ? instr->bits(25, 6) : -1;
 
     // We first check if we met a call_rt_redirected.
@@ -2017,16 +2029,39 @@ Simulator::softwareInterrupt(SimInstruct
           case Args_Int_IntDouble: {
             int32_t ival = getRegister(a0);
             double dval0 = getDoubleFromRegisterPair(a2);
             Prototype_Int_IntDouble target = reinterpret_cast<Prototype_Int_IntDouble>(external);
             int32_t result = target(ival, dval0);
             setRegister(v0, result);
             break;
           }
+          case Args_Double_DoubleDoubleDouble: {
+            double dval0, dval1, dval2;
+            int32_t ival;
+            getFpArgs(&dval0, &dval1, &ival);
+            // the last argument is on stack
+            getFpFromStack(stack_pointer + 4, &dval2);
+            Prototype_Double_DoubleDoubleDouble target = reinterpret_cast<Prototype_Double_DoubleDoubleDouble>(external);
+            double dresult = target(dval0, dval1, dval2);
+            setCallResultDouble(dresult);
+            break;
+         }
+         case Args_Double_DoubleDoubleDoubleDouble: {
+            double dval0, dval1, dval2, dval3;
+            int32_t ival;
+            getFpArgs(&dval0, &dval1, &ival);
+            // the two last arguments are on stack
+            getFpFromStack(stack_pointer + 4, &dval2);
+            getFpFromStack(stack_pointer + 6, &dval3);
+            Prototype_Double_DoubleDoubleDoubleDouble target = reinterpret_cast<Prototype_Double_DoubleDoubleDoubleDouble>(external);
+            double dresult = target(dval0, dval1, dval2, dval3);
+            setCallResultDouble(dresult);
+            break;
+          }
           default:
             MOZ_CRASH("call");
         }
 
         setRegister(ra, saved_ra);
         set_pc(getRegister(ra));
 #endif
     } else if (func == ff_break && code <= kMaxStopCode) {
--- a/js/src/jit/mips/Simulator-mips.h
+++ b/js/src/jit/mips/Simulator-mips.h
@@ -292,16 +292,17 @@ class Simulator {
     };
     int16_t exceptions[kNumExceptions];
 
     // Exceptions.
     void signalExceptions();
 
     // Handle arguments and return value for runtime FP functions.
     void getFpArgs(double *x, double *y, int32_t *z);
+    void getFpFromStack(int32_t *stack, double *x);
 
     void setCallResultDouble(double result);
     void setCallResultFloat(float result);
     void setCallResult(int64_t res);
 
     void callInternal(uint8_t *entry);
 
     // Architecture state.
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -447,28 +447,16 @@ JS_ValueToSource(JSContext *cx, HandleVa
 }
 
 JS_PUBLIC_API(bool)
 JS_DoubleIsInt32(double d, int32_t *ip)
 {
     return mozilla::NumberIsInt32(d, ip);
 }
 
-JS_PUBLIC_API(int32_t)
-JS_DoubleToInt32(double d)
-{
-    return ToInt32(d);
-}
-
-JS_PUBLIC_API(uint32_t)
-JS_DoubleToUint32(double d)
-{
-    return ToUint32(d);
-}
-
 JS_PUBLIC_API(JSType)
 JS_TypeOfValue(JSContext *cx, HandleValue value)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, value);
     return TypeOfValue(value);
 }
@@ -5937,25 +5925,25 @@ AutoGCRooter::AutoGCRooter(ContextFriend
   : down(cx->autoGCRooters),
     tag_(tag),
     stackTop(&cx->autoGCRooters)
 {
     MOZ_ASSERT(this != *stackTop);
     *stackTop = this;
 }
 
-#ifdef DEBUG
+#ifdef JS_DEBUG
 JS_PUBLIC_API(void)
-JS::AssertArgumentsAreSane(JSContext *cx, HandleValue value)
+JS::detail::AssertArgumentsAreSane(JSContext *cx, HandleValue value)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, value);
 }
-#endif /* DEBUG */
+#endif /* JS_DEBUG */
 
 JS_PUBLIC_API(void *)
 JS_EncodeScript(JSContext *cx, HandleScript scriptArg, uint32_t *lengthp)
 {
     XDREncoder encoder(cx);
     RootedScript script(cx, scriptArg);
     if (!encoder.codeScript(&script))
         return nullptr;
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -56,30 +56,16 @@ class JS_PUBLIC_API(AutoCheckRequestDept
 
 #else
 
 # define CHECK_REQUEST(cx) \
     ((void) 0)
 
 #endif /* JS_DEBUG */
 
-#ifdef JS_DEBUG
-/*
- * Assert that we're not doing GC on cx, that we're in a request as
- * needed, and that the compartments for cx and v are correct.
- * Also check that GC would be safe at this point.
- */
-JS_PUBLIC_API(void)
-AssertArgumentsAreSane(JSContext *cx, JS::HandleValue v);
-#else
-inline void AssertArgumentsAreSane(JSContext *cx, JS::HandleValue v) {
-    /* Do nothing */
-}
-#endif /* JS_DEBUG */
-
 /* AutoValueArray roots an internal fixed-size array of Values. */
 template <size_t N>
 class AutoValueArray : public AutoGCRooter
 {
     const size_t length_;
     Value elements_[N];
 
   public:
@@ -1105,207 +1091,19 @@ extern JS_PUBLIC_API(JSFunction *)
 JS_ValueToFunction(JSContext *cx, JS::HandleValue v);
 
 extern JS_PUBLIC_API(JSFunction *)
 JS_ValueToConstructor(JSContext *cx, JS::HandleValue v);
 
 extern JS_PUBLIC_API(JSString *)
 JS_ValueToSource(JSContext *cx, JS::Handle<JS::Value> v);
 
-namespace js {
-/*
- * DO NOT CALL THIS.  Use JS::ToNumber
- */
-extern JS_PUBLIC_API(bool)
-ToNumberSlow(JSContext *cx, JS::Value v, double *dp);
-
-/*
- * DO NOT CALL THIS. Use JS::ToBoolean
- */
-extern JS_PUBLIC_API(bool)
-ToBooleanSlow(JS::HandleValue v);
-
-/*
- * DO NOT CALL THIS. Use JS::ToString
- */
-extern JS_PUBLIC_API(JSString*)
-ToStringSlow(JSContext *cx, JS::HandleValue v);
-
-/*
- * DO NOT CALL THIS. Use JS::ToObject.
- */
-extern JS_PUBLIC_API(JSObject*)
-ToObjectSlow(JSContext *cx, JS::HandleValue vp, bool reportScanStack);
-
-} /* namespace js */
-
-namespace JS {
-
-/* ES5 9.3 ToNumber. */
-MOZ_ALWAYS_INLINE bool
-ToNumber(JSContext *cx, HandleValue v, double *out)
-{
-    AssertArgumentsAreSane(cx, v);
-
-    if (v.isNumber()) {
-        *out = v.toNumber();
-        return true;
-    }
-    return js::ToNumberSlow(cx, v, out);
-}
-
-MOZ_ALWAYS_INLINE bool
-ToBoolean(HandleValue v)
-{
-    if (v.isBoolean())
-        return v.toBoolean();
-    if (v.isInt32())
-        return v.toInt32() != 0;
-    if (v.isNullOrUndefined())
-        return false;
-    if (v.isDouble()) {
-        double d = v.toDouble();
-        return !mozilla::IsNaN(d) && d != 0;
-    }
-    if (v.isSymbol())
-        return true;
-
-    /* The slow path handles strings and objects. */
-    return js::ToBooleanSlow(v);
-}
-
-MOZ_ALWAYS_INLINE JSString*
-ToString(JSContext *cx, HandleValue v)
-{
-    if (v.isString())
-        return v.toString();
-    return js::ToStringSlow(cx, v);
-}
-
-/* ES5 9.9 ToObject. */
-MOZ_ALWAYS_INLINE JSObject*
-ToObject(JSContext *cx, HandleValue vp)
-{
-    if (vp.isObject())
-        return &vp.toObject();
-    return js::ToObjectSlow(cx, vp, false);
-}
-
-/*
- * Implements ES6 draft rev 28 (2014 Oct 14) 7.1.1, second algorithm.
- *
- * Most users should not call this -- use JS::ToNumber, ToBoolean, or ToString
- * instead. This should only be called from custom convert hooks. It implements
- * the default conversion behavior shared by most objects in JS, so it's useful
- * as a fallback.
- */
-extern JS_PUBLIC_API(bool)
-OrdinaryToPrimitive(JSContext *cx, JS::HandleObject obj, JSType type,
-                    JS::MutableHandleValue vp);
-
-} /* namespace JS */
-
 extern JS_PUBLIC_API(bool)
 JS_DoubleIsInt32(double d, int32_t *ip);
 
-extern JS_PUBLIC_API(int32_t)
-JS_DoubleToInt32(double d);
-
-extern JS_PUBLIC_API(uint32_t)
-JS_DoubleToUint32(double d);
-
-
-namespace js {
-/* DO NOT CALL THIS. Use JS::ToUint16. */
-extern JS_PUBLIC_API(bool)
-ToUint16Slow(JSContext *cx, JS::HandleValue v, uint16_t *out);
-
-/* DO NOT CALL THIS. Use JS::ToInt32. */
-extern JS_PUBLIC_API(bool)
-ToInt32Slow(JSContext *cx, JS::HandleValue v, int32_t *out);
-
-/* DO NOT CALL THIS. Use JS::ToUint32. */
-extern JS_PUBLIC_API(bool)
-ToUint32Slow(JSContext *cx, JS::HandleValue v, uint32_t *out);
-
-/* DO NOT CALL THIS. Use JS::ToInt64. */
-extern JS_PUBLIC_API(bool)
-ToInt64Slow(JSContext *cx, JS::HandleValue v, int64_t *out);
-
-/* DO NOT CALL THIS. Use JS::ToUint64. */
-extern JS_PUBLIC_API(bool)
-ToUint64Slow(JSContext *cx, JS::HandleValue v, uint64_t *out);
-} /* namespace js */
-
-namespace JS {
-
-MOZ_ALWAYS_INLINE bool
-ToUint16(JSContext *cx, JS::HandleValue v, uint16_t *out)
-{
-    AssertArgumentsAreSane(cx, v);
-
-    if (v.isInt32()) {
-        *out = uint16_t(v.toInt32());
-        return true;
-    }
-    return js::ToUint16Slow(cx, v, out);
-}
-
-MOZ_ALWAYS_INLINE bool
-ToInt32(JSContext *cx, JS::HandleValue v, int32_t *out)
-{
-    AssertArgumentsAreSane(cx, v);
-
-    if (v.isInt32()) {
-        *out = v.toInt32();
-        return true;
-    }
-    return js::ToInt32Slow(cx, v, out);
-}
-
-MOZ_ALWAYS_INLINE bool
-ToUint32(JSContext *cx, JS::HandleValue v, uint32_t *out)
-{
-    AssertArgumentsAreSane(cx, v);
-
-    if (v.isInt32()) {
-        *out = uint32_t(v.toInt32());
-        return true;
-    }
-    return js::ToUint32Slow(cx, v, out);
-}
-
-MOZ_ALWAYS_INLINE bool
-ToInt64(JSContext *cx, JS::HandleValue v, int64_t *out)
-{
-    AssertArgumentsAreSane(cx, v);
-
-    if (v.isInt32()) {
-        *out = int64_t(v.toInt32());
-        return true;
-    }
-    return js::ToInt64Slow(cx, v, out);
-}
-
-MOZ_ALWAYS_INLINE bool
-ToUint64(JSContext *cx, JS::HandleValue v, uint64_t *out)
-{
-    AssertArgumentsAreSane(cx, v);
-
-    if (v.isInt32()) {
-        /* Account for sign extension of negatives into the longer 64bit space. */
-        *out = uint64_t(int64_t(v.toInt32()));
-        return true;
-    }
-    return js::ToUint64Slow(cx, v, out);
-}
-
-
-} /* namespace JS */
-
 extern JS_PUBLIC_API(JSType)
 JS_TypeOfValue(JSContext *cx, JS::Handle<JS::Value> v);
 
 extern JS_PUBLIC_API(bool)
 JS_StrictlyEqual(JSContext *cx, jsval v1, jsval v2, bool *equal);
 
 extern JS_PUBLIC_API(bool)
 JS_LooselyEqual(JSContext *cx, JS::Handle<JS::Value> v1, JS::Handle<JS::Value> v2, bool *equal);
--- a/js/src/jsmath.cpp
+++ b/js/src/jsmath.cpp
@@ -1373,16 +1373,60 @@ js::ecmaHypot(double x, double y)
      */
     if (mozilla::IsInfinite(x) || mozilla::IsInfinite(y)) {
         return mozilla::PositiveInfinity<double>();
     }
 #endif
     return hypot(x, y);
 }
 
+static inline
+void
+hypot_step(double &scale, double &sumsq, double x)
+{
+    double xabs = mozilla::Abs(x);
+    if (scale < xabs) {
+        sumsq = 1 + sumsq * (scale / xabs) * (scale / xabs);
+        scale = xabs;
+    } else if (scale != 0) {
+        sumsq += (xabs / scale) * (xabs / scale);
+    }
+}
+
+double
+js::hypot4(double x, double y, double z, double w)
+{
+    /* Check for infinity or NaNs so that we can return immediatelly.
+     * Does not need to be WIN_XP specific as ecmaHypot
+     */
+    if (mozilla::IsInfinite(x) || mozilla::IsInfinite(y) ||
+            mozilla::IsInfinite(z) || mozilla::IsInfinite(w))
+        return mozilla::PositiveInfinity<double>();
+
+    if (mozilla::IsNaN(x) || mozilla::IsNaN(y) || mozilla::IsNaN(z) ||
+            mozilla::IsNaN(w))
+        return GenericNaN();
+
+    double scale = 0;
+    double sumsq = 1;
+
+    hypot_step(scale, sumsq, x);
+    hypot_step(scale, sumsq, y);
+    hypot_step(scale, sumsq, z);
+    hypot_step(scale, sumsq, w);
+
+    return scale * sqrt(sumsq);
+}
+
+double
+js::hypot3(double x, double y, double z)
+{
+    return hypot4(x, y, z, 0.0);
+}
+
 bool
 js::math_hypot(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return math_hypot_handle(cx, args, args.rval());
 }
 
 bool
@@ -1413,24 +1457,17 @@ js::math_hypot_handle(JSContext *cx, Han
         if (!ToNumber(cx, args[i], &x))
             return false;
 
         isInfinite |= mozilla::IsInfinite(x);
         isNaN |= mozilla::IsNaN(x);
         if (isInfinite || isNaN)
             continue;
 
-        double xabs = mozilla::Abs(x);
-
-        if (scale < xabs) {
-            sumsq = 1 + sumsq * (scale / xabs) * (scale / xabs);
-            scale = xabs;
-        } else if (scale != 0) {
-            sumsq += (xabs / scale) * (xabs / scale);
-        }
+        hypot_step(scale, sumsq, x);
     }
 
     double result = isInfinite ? PositiveInfinity<double>() :
                     isNaN ? GenericNaN() :
                     scale * sqrt(sumsq);
     res.setNumber(result);
     return true;
 }
--- a/js/src/jsmath.h
+++ b/js/src/jsmath.h
@@ -240,16 +240,22 @@ extern bool
 math_asinh(JSContext *cx, unsigned argc, js::Value *vp);
 
 extern bool
 math_atanh(JSContext *cx, unsigned argc, js::Value *vp);
 
 extern double
 ecmaHypot(double x, double y);
 
+extern double
+hypot3(double x, double y, double z);
+
+extern double
+hypot4(double x, double y, double z, double w);
+
 extern bool
 math_hypot(JSContext *cx, unsigned argc, Value *vp);
 
 extern bool
 math_hypot_handle(JSContext *cx, HandleValueArray args, MutableHandleValue res);
 
 extern bool
 math_trunc(JSContext *cx, unsigned argc, Value *vp);
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -3555,17 +3555,17 @@ js::PrimitiveToObject(JSContext *cx, con
 /*
  * Invokes the ES5 ToObject algorithm on vp, returning the result. If vp might
  * already be an object, use ToObject. reportCantConvert controls how null and
  * undefined errors are reported.
  *
  * Callers must handle the already-object case.
  */
 JSObject *
-js::ToObjectSlow(JSContext *cx, HandleValue val, bool reportScanStack)
+js::ToObjectSlow(JSContext *cx, JS::HandleValue val, bool reportScanStack)
 {
     MOZ_ASSERT(!val.isMagic());
     MOZ_ASSERT(!val.isObject());
 
     if (val.isNullOrUndefined()) {
         if (reportScanStack) {
             js_ReportIsNullOrUndefined(cx, JSDVG_SEARCH_STACK, val, NullPtr());
         } else {
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -15,16 +15,17 @@
  * values, called slots.  The map/slot pointer pair is GC'ed, while the map
  * is reference counted and the slot vector is malloc'ed.
  */
 
 #include "mozilla/MemoryReporting.h"
 
 #include "gc/Barrier.h"
 #include "gc/Marking.h"
+#include "js/Conversions.h"
 #include "js/GCAPI.h"
 #include "js/HeapAPI.h"
 #include "vm/Shape.h"
 #include "vm/String.h"
 #include "vm/Xdr.h"
 
 namespace JS {
 struct ClassInfo;
--- a/js/src/shell/OSObject.cpp
+++ b/js/src/shell/OSObject.cpp
@@ -16,16 +16,18 @@
 #else
 #include <sys/wait.h>
 #include <unistd.h>
 #endif
 
 // For JSFunctionSpecWithHelp
 #include "jsfriendapi.h"
 
+#include "js/Conversions.h"
+
 using namespace JS;
 
 static bool
 os_getenv(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() < 1) {
         JS_ReportError(cx, "os.getenv requires 1 argument");
--- a/js/src/tests/ecma_6/Comprehensions/error-messages.js
+++ b/js/src/tests/ecma_6/Comprehensions/error-messages.js
@@ -26,72 +26,74 @@ const GENERIC              = error("(for
 const BAD_GENERATOR_SYNTAX = error("(for (x of []) x, 1)").message;
 const MISSING_SEMI         = error("yield 1").message;
 const MISSING_PAREN        = error("(yield 1)").message;
 const PAREN_PAREN          = error("(foo").message;
 const FOR_OF_PAREN         = error("(for (x of y, z) w)").message;
 
 const cases = [
 // Expressions involving yield without a value, not currently implemented.  Many
-// of these errors would need to be updated.
-//{ expr: "yield",        top: TOP_YIELD, fun: null,              gen: GENEXP_YIELD, desc: "simple yield" },
-//{ expr: "1, yield",     top: TOP_YIELD, fun: null,              gen: GENEXP_YIELD, desc: "simple yield at end of list" },
-//{ expr: "yield, 1",     top: TOP_YIELD, fun: YIELD_PAREN, gen: YIELD_PAREN,  desc: "simple yield in list" },
-//{ expr: "(yield)",      top: TOP_YIELD, fun: null,              gen: GENEXP_YIELD, desc: "simple yield, parenthesized" },
-//{ expr: "(1, yield)",   top: TOP_YIELD, fun: null,              gen: GENEXP_YIELD, desc: "simple yield at end of list, parenthesized" },
-//{ expr: "(yield, 1)",   top: TOP_YIELD, fun: YIELD_PAREN, gen: YIELD_PAREN,  desc: "simple yield in list, parenthesized" },
-//{ expr: "((((yield))))",   top: TOP_YIELD, fun: null, gen: GENEXP_YIELD, desc: "deeply nested yield" },
-//{ expr: "(for (x of []) yield)",           top: TOP_YIELD, fun: GENERIC,      gen: GENERIC,      desc: "simple yield in genexp" },
-//{ expr: "(for (x of []) yield, 1)",        top: TOP_YIELD, fun: YIELD_PAREN,  gen: YIELD_PAREN,  desc: "simple yield in list in genexp" },
-//{ expr: "(for (x of []) 1, yield)",        top: TOP_YIELD, fun: GENERIC,      gen: GENERIC,      desc: "simple yield at end of list in genexp" },
-//{ expr: "(for (x of []) (yield))",         top: TOP_YIELD, fun: GENEXP_YIELD, gen: GENEXP_YIELD, desc: "simple yield, parenthesized in genexp" },
-//{ expr: "(for (x of []) 1, (yield))",      top: TOP_YIELD, fun: GENEXP_YIELD, gen: GENEXP_YIELD, desc: "simple yield, parenthesized in list in genexp" },
-//{ expr: "(for (x of []) (1, yield))",      top: TOP_YIELD, fun: GENEXP_YIELD, gen: GENEXP_YIELD, desc: "simple yield at end of list, parenthesized in genexp" },
-//{ expr: "(for (x of []) 1, (2, yield))",   top: TOP_YIELD, fun: GENEXP_YIELD, gen: GENEXP_YIELD, desc: "simple yield at end of list, parenthesized in list in genexp" },
-//{ expr: "(for (x of []) (yield, 1))",      top: TOP_YIELD, fun: YIELD_PAREN,  gen: YIELD_PAREN,  desc: "simple yield in list, parenthesized in genexp" },
-//{ expr: "(for (x of []) 1, (yield, 2))",   top: TOP_YIELD, fun: YIELD_PAREN,  gen: YIELD_PAREN,  desc: "simple yield in list, parenthesized in list in genexp" },
+// of these errors would need to be updated.  (Note: line numbers below might be
+// mere placeholders, not actually the expected correct behavior -- check before
+// blindly uncommenting.)
+//{ expr: "yield",        top: [TOP_YIELD, 777], fun: null,              gen: [GENEXP_YIELD, 2], desc: "simple yield" },
+//{ expr: "1, yield",     top: [TOP_YIELD, 777], fun: null,              gen: [GENEXP_YIELD, 2], desc: "simple yield at end of list" },
+//{ expr: "yield, 1",     top: [TOP_YIELD, 777], fun: [YIELD_PAREN, 2], gen: [YIELD_PAREN, 2],  desc: "simple yield in list" },
+//{ expr: "(yield)",      top: [TOP_YIELD, 777], fun: null,              gen: [GENEXP_YIELD, 2], desc: "simple yield, parenthesized" },
+//{ expr: "(1, yield)",   top: [TOP_YIELD, 777], fun: null,              gen: [GENEXP_YIELD, 2], desc: "simple yield at end of list, parenthesized" },
+//{ expr: "(yield, 1)",   top: [TOP_YIELD, 777], fun: [YIELD_PAREN, 2], gen: [YIELD_PAREN, 2],  desc: "simple yield in list, parenthesized" },
+//{ expr: "((((yield))))",   top: [TOP_YIELD, 777], fun: null, gen: [GENEXP_YIELD, 2], desc: "deeply nested yield" },
+//{ expr: "(for (x of []) yield)",           top: [TOP_YIELD, 777], fun: [GENERIC, 777],      gen: [GENERIC, 777],      desc: "simple yield in genexp" },
+//{ expr: "(for (x of []) yield, 1)",        top: [TOP_YIELD, 777], fun: [YIELD_PAREN, 2],  gen: [YIELD_PAREN, 2],  desc: "simple yield in list in genexp" },
+//{ expr: "(for (x of []) 1, yield)",        top: [TOP_YIELD, 777], fun: [GENERIC, 777],      gen: [GENERIC, 777],      desc: "simple yield at end of list in genexp" },
+//{ expr: "(for (x of []) (yield))",         top: [TOP_YIELD, 777], fun: [GENEXP_YIELD, 2], gen: [GENEXP_YIELD, 2], desc: "simple yield, parenthesized in genexp" },
+//{ expr: "(for (x of []) 1, (yield))",      top: [TOP_YIELD, 777], fun: [GENEXP_YIELD, 2], gen: [GENEXP_YIELD, 2], desc: "simple yield, parenthesized in list in genexp" },
+//{ expr: "(for (x of []) (1, yield))",      top: [TOP_YIELD, 777], fun: [GENEXP_YIELD, 2], gen: [GENEXP_YIELD, 2], desc: "simple yield at end of list, parenthesized in genexp" },
+//{ expr: "(for (x of []) 1, (2, yield))",   top: [TOP_YIELD, 777], fun: [GENEXP_YIELD, 2], gen: [GENEXP_YIELD, 2], desc: "simple yield at end of list, parenthesized in list in genexp" },
+//{ expr: "(for (x of []) (yield, 1))",      top: [TOP_YIELD, 777], fun: [YIELD_PAREN, 2],  gen: [YIELD_PAREN, 2],  desc: "simple yield in list, parenthesized in genexp" },
+//{ expr: "(for (x of []) 1, (yield, 2))",   top: [TOP_YIELD, 777], fun: [YIELD_PAREN, 2],  gen: [YIELD_PAREN, 2],  desc: "simple yield in list, parenthesized in list in genexp" },
 //{ expr: "(for (x of []) (function*() { yield }))",           top: null, fun: null, gen: null, desc: "legal yield in nested function" },
 
   // yield expressions
-  { expr: "yield 1",      top: MISSING_SEMI, fun: MISSING_SEMI, gen: null, genexp: GENEXP_YIELD, desc: "yield w/ arg" },
-  { expr: "1, yield 2",   top: MISSING_SEMI, fun: MISSING_SEMI, gen: null, genexp: FOR_OF_PAREN, desc: "yield w/ arg at end of list" },
-  { expr: "yield 1, 2",   top: MISSING_SEMI, fun: MISSING_SEMI, gen: null, genexp: FOR_OF_PAREN, desc: "yield w/ arg in list" },
-  { expr: "(yield 1)",    top: MISSING_PAREN, fun: MISSING_PAREN, gen: null, genexp: GENEXP_YIELD, desc: "yield w/ arg, parenthesized" },
-  { expr: "(1, yield 2)", top: MISSING_PAREN, fun: MISSING_PAREN, gen: null, genexp: GENEXP_YIELD, desc: "yield w/ arg at end of list, parenthesized" },
-  { expr: "(yield 1, 2)", top: MISSING_PAREN, fun: MISSING_PAREN, gen: null, genexp: YIELD_PAREN, desc: "yield w/ arg in list, parenthesized" },
+  { expr: "yield 1",      top: [MISSING_SEMI, 2], fun: [MISSING_SEMI, 2], gen: null, genexp: [GENEXP_YIELD, 2], desc: "yield w/ arg" },
+  { expr: "1, yield 2",   top: [MISSING_SEMI, 2], fun: [MISSING_SEMI, 2], gen: null, genexp: [FOR_OF_PAREN, 1], desc: "yield w/ arg at end of list" },
+  { expr: "yield 1, 2",   top: [MISSING_SEMI, 2], fun: [MISSING_SEMI, 2], gen: [YIELD_PAREN, 3], genexp: [FOR_OF_PAREN, 3], desc: "yield w/ arg in list" },
+  { expr: "(yield 1)",    top: [MISSING_PAREN, 2], fun: [MISSING_PAREN, 2], gen: null, genexp: [GENEXP_YIELD, 2], desc: "yield w/ arg, parenthesized" },
+  { expr: "(1, yield 2)", top: [MISSING_PAREN, 2], fun: [MISSING_PAREN, 2], gen: null, genexp: [GENEXP_YIELD, 2], desc: "yield w/ arg at end of list, parenthesized" },
+  { expr: "(yield 1, 2)", top: [MISSING_PAREN, 2], fun: [MISSING_PAREN, 2], gen: [YIELD_PAREN, 3], genexp: [YIELD_PAREN, 2], desc: "yield w/ arg in list, parenthesized" },
 
   // deeply nested yield expressions
-  { expr: "((((yield 1))))", top: MISSING_PAREN, fun: MISSING_PAREN, gen: null, genexp: GENEXP_YIELD, desc: "deeply nested yield w/ arg" },
+  { expr: "((((yield 1))))", top: [MISSING_PAREN, 2], fun: [MISSING_PAREN, 2], gen: null, genexp: [GENEXP_YIELD, 2], desc: "deeply nested yield w/ arg" },
 
   // arguments
   { expr: "arguments",    top: null, fun: null, gen: null, genexp: null, desc: "arguments in list" },
-  { expr: "1, arguments", top: null, fun: null, gen: null, genexp: FOR_OF_PAREN, desc: "arguments in list" },
+  { expr: "1, arguments", top: null, fun: null, gen: null, genexp: [FOR_OF_PAREN, 1], desc: "arguments in list" },
 
   // yield in generator expressions
-  { expr: "(for (x of []) yield 1)",         top: GENEXP_YIELD, fun: GENEXP_YIELD, gen: GENEXP_YIELD, genexp: GENEXP_YIELD, desc: "yield w/ arg in genexp" },
-  { expr: "(for (x of []) yield 1, 2)",      top: GENEXP_YIELD, fun: GENEXP_YIELD,  gen: GENEXP_YIELD, genexp: GENEXP_YIELD, desc: "yield w/ arg in list in genexp" },
-  { expr: "(for (x of []) 1, yield 2)",      top: PAREN_PAREN, fun: PAREN_PAREN,  gen: PAREN_PAREN, genexp: PAREN_PAREN, desc: "yield w/ arg at end of list in genexp" },
-  { expr: "(for (x of []) (yield 1))",       top: GENEXP_YIELD, fun: GENEXP_YIELD, gen: GENEXP_YIELD, genexp: GENEXP_YIELD, desc: "yield w/ arg, parenthesized in genexp" },
-  { expr: "(for (x of []) 1, (yield 2))",    top: PAREN_PAREN, fun: PAREN_PAREN, gen: PAREN_PAREN, genexp: PAREN_PAREN, desc: "yield w/ arg, parenthesized in list in genexp" },
-  { expr: "(for (x of []) (1, yield 2))",    top: GENEXP_YIELD, fun: GENEXP_YIELD, gen: GENEXP_YIELD, genexp: GENEXP_YIELD, desc: "yield w/ arg at end of list, parenthesized in genexp" },
-  { expr: "(for (x of []) 1, (2, yield 3))", top: PAREN_PAREN, fun: PAREN_PAREN, gen: PAREN_PAREN, genexp: PAREN_PAREN, desc: "yield w/ arg at end of list, parenthesized in list in genexp" },
-  { expr: "(for (x of []) (yield 1, 2))",    top: YIELD_PAREN, fun: YIELD_PAREN, gen: YIELD_PAREN, genexp: YIELD_PAREN, desc: "yield w/ arg in list, parenthesized in genexp" },
-  { expr: "(for (x of []) 1, (yield 2, 3))", top: PAREN_PAREN, fun: PAREN_PAREN, gen: PAREN_PAREN, genexp: PAREN_PAREN, desc: "yield w/ arg in list, parenthesized in list in genexp" },
+  { expr: "(for (x of []) yield 1)",         top: [GENEXP_YIELD, 2], fun: [GENEXP_YIELD, 2], gen: [GENEXP_YIELD, 2], genexp: [GENEXP_YIELD, 2], desc: "yield w/ arg in genexp" },
+  { expr: "(for (x of []) yield 1, 2)",      top: [GENEXP_YIELD, 2], fun: [GENEXP_YIELD, 2],  gen: [GENEXP_YIELD, 2], genexp: [GENEXP_YIELD, 2], desc: "yield w/ arg in list in genexp" },
+  { expr: "(for (x of []) 1, yield 2)",      top: [PAREN_PAREN, 1], fun: [PAREN_PAREN, 1],  gen: [PAREN_PAREN, 1], genexp: [PAREN_PAREN, 1], desc: "yield w/ arg at end of list in genexp" },
+  { expr: "(for (x of []) (yield 1))",       top: [GENEXP_YIELD, 2], fun: [GENEXP_YIELD, 2], gen: [GENEXP_YIELD, 2], genexp: [GENEXP_YIELD, 2], desc: "yield w/ arg, parenthesized in genexp" },
+  { expr: "(for (x of []) 1, (yield 2))",    top: [PAREN_PAREN, 1], fun: [PAREN_PAREN, 1], gen: [PAREN_PAREN, 1], genexp: [PAREN_PAREN, 1], desc: "yield w/ arg, parenthesized in list in genexp" },
+  { expr: "(for (x of []) (1, yield 2))",    top: [GENEXP_YIELD, 2], fun: [GENEXP_YIELD, 2], gen: [GENEXP_YIELD, 2], genexp: [GENEXP_YIELD, 2], desc: "yield w/ arg at end of list, parenthesized in genexp" },
+  { expr: "(for (x of []) 1, (2, yield 3))", top: [PAREN_PAREN, 1], fun: [PAREN_PAREN, 1], gen: [PAREN_PAREN, 1], genexp: [PAREN_PAREN, 1], desc: "yield w/ arg at end of list, parenthesized in list in genexp" },
+  { expr: "(for (x of []) (yield 1, 2))",    top: [YIELD_PAREN, 2], fun: [YIELD_PAREN, 2], gen: [YIELD_PAREN, 2], genexp: [YIELD_PAREN, 2], desc: "yield w/ arg in list, parenthesized in genexp" },
+  { expr: "(for (x of []) 1, (yield 2, 3))", top: [PAREN_PAREN, 1], fun: [PAREN_PAREN, 1], gen: [PAREN_PAREN, 1], genexp: [PAREN_PAREN, 1], desc: "yield w/ arg in list, parenthesized in list in genexp" },
 
   // deeply nested yield in generator expressions
-  { expr: "(for (x of []) (((1, yield 2))))",               top: GENEXP_YIELD, fun: GENEXP_YIELD, gen: GENEXP_YIELD, genexp: GENEXP_YIELD, desc: "deeply nested yield in genexp" },
-  { expr: "(for (y of []) (for (x of []) ((1, yield 2))))", top: GENEXP_YIELD, fun: GENEXP_YIELD, gen: GENEXP_YIELD, genexp: GENEXP_YIELD, desc: "deeply nested yield in multiple genexps" },
+  { expr: "(for (x of []) (((1, yield 2))))",               top: [GENEXP_YIELD, 2], fun: [GENEXP_YIELD, 2], gen: [GENEXP_YIELD, 2], genexp: [GENEXP_YIELD, 2], desc: "deeply nested yield in genexp" },
+  { expr: "(for (y of []) (for (x of []) ((1, yield 2))))", top: [GENEXP_YIELD, 2], fun: [GENEXP_YIELD, 2], gen: [GENEXP_YIELD, 2], genexp: [GENEXP_YIELD, 2], desc: "deeply nested yield in multiple genexps" },
 
   // arguments in generator expressions
   { expr: "(for (x of []) arguments)",         top: null, fun: null, gen: null, genexp: null, desc: "simple arguments in genexp" },
-  { expr: "(for (x of []) 1, arguments)",      top: BAD_GENERATOR_SYNTAX, fun: BAD_GENERATOR_SYNTAX, gen: BAD_GENERATOR_SYNTAX, genexp: BAD_GENERATOR_SYNTAX, desc: "arguments in list in genexp" },
+  { expr: "(for (x of []) 1, arguments)",      top: [BAD_GENERATOR_SYNTAX, 1], fun: [BAD_GENERATOR_SYNTAX, 1], gen: [BAD_GENERATOR_SYNTAX, 1], genexp: [BAD_GENERATOR_SYNTAX, 1], desc: "arguments in list in genexp" },
   { expr: "(for (x of []) (arguments))",       top: null, fun: null, gen: null, genexp: null, desc: "arguments, parenthesized in genexp" },
-  { expr: "(for (x of []) 1, (arguments))",    top: BAD_GENERATOR_SYNTAX, fun: BAD_GENERATOR_SYNTAX, gen: BAD_GENERATOR_SYNTAX, genexp: BAD_GENERATOR_SYNTAX, desc: "arguments, parenthesized in list in genexp" },
+  { expr: "(for (x of []) 1, (arguments))",    top: [BAD_GENERATOR_SYNTAX, 1], fun: [BAD_GENERATOR_SYNTAX, 1], gen: [BAD_GENERATOR_SYNTAX, 1], genexp: [BAD_GENERATOR_SYNTAX, 1], desc: "arguments, parenthesized in list in genexp" },
   { expr: "(for (x of []) (1, arguments))",    top: null, fun: null, gen: null, genexp: null, desc: "arguments in list, parenthesized in genexp" },
-  { expr: "(for (x of []) 1, (2, arguments))", top: BAD_GENERATOR_SYNTAX, fun: BAD_GENERATOR_SYNTAX, gen: BAD_GENERATOR_SYNTAX, genexp: BAD_GENERATOR_SYNTAX, desc: "arguments in list, parenthesized in list in genexp" },
+  { expr: "(for (x of []) 1, (2, arguments))", top: [BAD_GENERATOR_SYNTAX, 1], fun: [BAD_GENERATOR_SYNTAX, 1], gen: [BAD_GENERATOR_SYNTAX, 1], genexp: [BAD_GENERATOR_SYNTAX, 1], desc: "arguments in list, parenthesized in list in genexp" },
 
   // deeply nested arguments in generator expressions
   { expr: "(for (x of []) (((1, arguments))))",               top: null, fun: null, gen: null, genexp: null, desc: "deeply nested arguments in genexp" },
   { expr: "(for (y of []) (for (x of []) ((1, arguments))))", top: null, fun: null, gen: null, genexp: null, desc: "deeply nested arguments in multiple genexps" },
 
   // legal yield/arguments in nested function
   { expr: "(for (x of []) (function*() { yield 1 }))",         top: null, fun: null, gen: null, genexp: null, desc: "legal yield in nested function" },
   { expr: "(for (x of []) (function() { arguments }))",       top: null, fun: null, gen: null, genexp: null, desc: "legal arguments in nested function" },
@@ -105,25 +107,24 @@ test();
 function splitKeyword(str) {
   return str.
 //         replace(/[)] yield/, ')\nyield\n').
          replace(/yield ([0-9])/, '\nyield $1\n').
          replace(/yield([^ ]|$)/, '\nyield\n$1').
          replace(/arguments/, '\narguments\n');
 }
 
-function expectError1(err, ctx, msg) {
+function expectError1(err, ctx, msg, lineNumber) {
   reportCompare('object', typeof err,     'exn for: ' + msg);
   reportCompare(ctx,      err.message,    'exn message for: ' + msg);
-  if (ctx !== BAD_GENERATOR_SYNTAX && ctx != PAREN_PAREN && ctx != FOR_OF_PAREN)
-      reportCompare(2,    err.lineNumber, 'exn token for: ' + msg);
+  reportCompare(lineNumber,    err.lineNumber, 'exn token for: ' + msg);
 }
 
-function expectError(expr, wrapCtx, expect, msg) {
-  expectError1(error(wrapCtx(expr)), expect, msg);
+function expectError(expr, wrapCtx, [expect, lineNumber], msg) {
+  expectError1(error(wrapCtx(expr)), expect, msg, lineNumber);
 }
 
 function expectSuccess(err, msg) {
   reportCompare(null, err, 'parse: ' + msg);
 }
 
 function atTop(str) { return str }
 function inFun(str) { return '(function(){' + str + '})' }
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Expressions/delete-name-parenthesized-early-error-strict-mode.js
@@ -0,0 +1,76 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/licenses/publicdomain/
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 1111101;
+var summary =
+  "delete (foo), delete ((foo)), and so on are strict mode early errors";
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+function checkSyntaxError(code)
+{
+  function helper(maker)
+  {
+    var earlyError = false;
+    try
+    {
+      var f = maker(code);
+
+      var error = "no early error, created a function with code <" + code + ">";
+      try
+      {
+        f();
+        error += ", and the function can be called without error";
+      }
+      catch (e)
+      {
+        error +=", and calling the function throws " + e;
+      }
+
+      throw new Error(error);
+    }
+    catch (e)
+    {
+      assertEq(e instanceof SyntaxError, true,
+               "expected syntax error, got " + e);
+    }
+  }
+
+  helper(Function);
+  helper(eval);
+}
+
+checkSyntaxError("function f() { 'use strict'; delete escape; } f();");
+checkSyntaxError("function f() { 'use strict'; delete escape; }");
+checkSyntaxError("function f() { 'use strict'; delete (escape); } f();");
+checkSyntaxError("function f() { 'use strict'; delete (escape); }");
+checkSyntaxError("function f() { 'use strict'; delete ((escape)); } f();");
+checkSyntaxError("function f() { 'use strict'; delete ((escape)); }");
+
+// Meanwhile, non-strict all of these should work
+
+function checkFine(code)
+{
+  Function(code);
+  (1, eval)(code); // indirect, to be consistent w/above
+}
+
+checkFine("function f() { delete escape; } f();");
+checkFine("function f() { delete escape; }");
+checkFine("function f() { delete (escape); } f();");
+checkFine("function f() { delete (escape); }");
+checkFine("function f() { delete ((escape)); } f();");
+checkFine("function f() { delete ((escape)); }");
+
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
+
+print("Tests complete");
--- a/js/src/tests/ecma_6/Generators/syntax.js
+++ b/js/src/tests/ecma_6/Generators/syntax.js
@@ -29,17 +29,17 @@ function* g() { (yield 3) + (yield 4); }
 function* g() { yield; }
 function* g() { yield }
 function* g() {
     yield
 }
 function* g() { (yield) }
 function* g() { [yield] }
 function* g() { {yield} }
-function* g() { yield, yield }
+function* g() { (yield), (yield) }
 function* g() { yield; yield }
 function* g() { (yield) ? yield : yield }
 function* g() {
     (yield)
     ? yield
     : yield
 }
 
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -491,32 +491,16 @@ class PerThreadData : public PerThreadDa
      * thread is associated.  This is private because accessing the
      * fields of this runtime can provoke race conditions, so the
      * intention is that access will be mediated through safe
      * functions like |runtimeFromMainThread| and |associatedWith()| below.
      */
     JSRuntime *runtime_;
 
   public:
-    /*
-     * We save all conservative scanned roots in this vector so that
-     * conservative scanning can be "replayed" deterministically. In DEBUG mode,
-     * this allows us to run a non-incremental GC after every incremental GC to
-     * ensure that no objects were missed.
-     */
-#ifdef DEBUG
-    struct SavedGCRoot {
-        void *thing;
-        JSGCTraceKind kind;
-
-        SavedGCRoot(void *thing, JSGCTraceKind kind) : thing(thing), kind(kind) {}
-    };
-    js::Vector<SavedGCRoot, 0, js::SystemAllocPolicy> gcSavedRoots;
-#endif
-
 #ifdef JS_TRACE_LOGGING
     TraceLoggerThread   *traceLogger;
 #endif
 
     /* Pointer to the current AutoFlushICache. */
     js::jit::AutoFlushICache *autoFlushICache_;
 
   public:
--- a/media/gmp-clearkey/0.1/clearkey.info
+++ b/media/gmp-clearkey/0.1/clearkey.info
@@ -1,4 +1,4 @@
 Name: clearkey
 Description: ClearKey decrypt-only GMP plugin
 Version: 0.1
-APIs: eme-decrypt-v3[org.w3.clearkey]
+APIs: eme-decrypt-v4[org.w3.clearkey]
--- a/media/mtransport/nr_socket_prsock.cpp
+++ b/media/mtransport/nr_socket_prsock.cpp
@@ -688,19 +688,67 @@ int NrSocket::read(void* buf, size_t max
     ABORT(R_EOD);
 
   *len = (size_t)status;  // Guaranteed to be > 0
   _status = 0;
 abort:
   return(_status);
 }
 
+NS_IMPL_ISUPPORTS(NrSocketIpcProxy, nsIUDPSocketInternal)
+
+nsresult
+NrSocketIpcProxy::Init(const nsRefPtr<NrSocketIpc>& socket)
+{
+  nsresult rv;
+  sts_thread_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+  if (NS_FAILED(rv)) {
+    MOZ_ASSERT(false, "Failed to get STS thread");
+    return rv;
+  }
+
+  socket_ = socket;
+  return NS_OK;
+}
+
+NrSocketIpcProxy::~NrSocketIpcProxy()
+{
+  // Send our ref to STS to be released
+  RUN_ON_THREAD(sts_thread_,
+                mozilla::WrapRelease(socket_.forget()),
+                NS_DISPATCH_NORMAL);
+}
+
+// IUDPSocketInternal interfaces
+// callback while error happened in UDP socket operation
+NS_IMETHODIMP NrSocketIpcProxy::CallListenerError(const nsACString &message,
+                                                  const nsACString &filename,
+                                                  uint32_t line_number) {
+  return socket_->CallListenerError(message, filename, line_number);
+}
+
+// callback while receiving UDP packet
+NS_IMETHODIMP NrSocketIpcProxy::CallListenerReceivedData(const nsACString &host,
+                                                         uint16_t port,
+                                                         const uint8_t *data,
+                                                         uint32_t data_length) {
+  return socket_->CallListenerReceivedData(host, port, data, data_length);
+}
+
+// callback while UDP socket is opened
+NS_IMETHODIMP NrSocketIpcProxy::CallListenerOpened() {
+  return socket_->CallListenerOpened();
+}
+
+// callback while UDP socket is closed
+NS_IMETHODIMP NrSocketIpcProxy::CallListenerClosed() {
+  return socket_->CallListenerClosed();
+}
+
 // NrSocketIpc Implementation
-NS_IMPL_ISUPPORTS(NrSocketIpc, nsIUDPSocketInternal)
-
 NrSocketIpc::NrSocketIpc(const nsCOMPtr<nsIEventTarget> &main_thread)
     : err_(false),
       state_(NR_INIT),
       main_thread_(main_thread),
       monitor_("NrSocketIpc") {
 }
 
 // IUDPSocketInternal interfaces
@@ -1017,17 +1065,25 @@ void NrSocketIpc::create_m(const nsACStr
     MOZ_ASSERT(false, "Failed to create UDPSocketChild");
     mon.NotifyAll();
     return;
   }
 
   socket_child_ = new nsMainThreadPtrHolder<nsIUDPSocketChild>(socketChild);
   socket_child_->SetFilterName(nsCString("stun"));
 
-  if (NS_FAILED(socket_child_->Bind(this, host, port,
+  nsRefPtr<NrSocketIpcProxy> proxy(new NrSocketIpcProxy);
+  rv = proxy->Init(this);
+  if (NS_FAILED(rv)) {
+    err_ = true;
+    mon.NotifyAll();
+    return;
+  }
+
+  if (NS_FAILED(socket_child_->Bind(proxy, host, port,
                                     /* reuse = */ false,
                                     /* loopback = */ false))) {
     err_ = true;
     MOZ_ASSERT(false, "Failed to create UDP socket");
     mon.NotifyAll();
     return;
   }
 }
@@ -1102,17 +1158,17 @@ static nr_socket_vtbl nr_socket_local_vt
   nr_socket_local_getaddr,
   nr_socket_local_connect,
   nr_socket_local_write,
   nr_socket_local_read,
   nr_socket_local_close
 };
 
 int nr_socket_local_create(nr_transport_addr *addr, nr_socket **sockp) {
-  NrSocketBase *sock = nullptr;
+  RefPtr<NrSocketBase> sock;
 
   // create IPC bridge for content process
   if (XRE_GetProcessType() == GeckoProcessType_Default) {
     sock = new NrSocket();
   } else {
     nsCOMPtr<nsIThread> main_thread;
     NS_GetMainThread(getter_AddRefs(main_thread));
     sock = new NrSocketIpc(main_thread.get());
@@ -1124,25 +1180,26 @@ int nr_socket_local_create(nr_transport_
   if (r)
     ABORT(r);
 
   r = nr_socket_create_int(static_cast<void *>(sock),
                            sock->vtbl(), sockp);
   if (r)
     ABORT(r);
 
-  // Add a reference so that we can delete it in destroy()
-  sock->AddRef();
-
   _status = 0;
 
+  {
+    // We will release this reference in destroy(), not exactly the normal
+    // ownership model, but it is what it is.
+    NrSocketBase* dummy = sock.forget().take();
+    (void)dummy;
+  }
+
 abort:
-  if (_status) {
-    delete sock;
-  }
   return _status;
 }
 
 
 static int nr_socket_local_destroy(void **objp) {
   if(!objp || !*objp)
     return 0;
 
--- a/media/mtransport/nr_socket_prsock.h
+++ b/media/mtransport/nr_socket_prsock.h
@@ -183,30 +183,38 @@ struct nr_udp_message {
   PRNetAddr from;
   nsAutoPtr<DataBuffer> data;
 
 private:
   ~nr_udp_message() {}
   DISALLOW_COPY_ASSIGN(nr_udp_message);
 };
 
-class NrSocketIpc : public NrSocketBase,
-                    public nsIUDPSocketInternal {
+class NrSocketIpc : public NrSocketBase {
 public:
 
   enum NrSocketIpcState {
     NR_INIT,
     NR_CONNECTING,
     NR_CONNECTED,
     NR_CLOSING,
     NR_CLOSED,
   };
 
-  NS_DECL_THREADSAFE_ISUPPORTS
-  NS_DECL_NSIUDPSOCKETINTERNAL
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(NrSocketIpc)
+
+  NS_IMETHODIMP CallListenerError(const nsACString &message,
+                                  const nsACString &filename,
+                                  uint32_t line_number);
+  NS_IMETHODIMP CallListenerReceivedData(const nsACString &host,
+                                         uint16_t port,
+                                         const uint8_t *data,
+                                         uint32_t data_length);
+  NS_IMETHODIMP CallListenerOpened();
+  NS_IMETHODIMP CallListenerClosed();
 
   explicit NrSocketIpc(const nsCOMPtr<nsIEventTarget> &main_thread);
 
   // Implementations of the NrSocketBase APIs
   virtual int create(nr_transport_addr *addr) MOZ_OVERRIDE;
   virtual int sendto(const void *msg, size_t len,
                      int flags, nr_transport_addr *to) MOZ_OVERRIDE;
   virtual int recvfrom(void * buf, size_t maxlen,
@@ -235,16 +243,32 @@ private:
   std::queue<RefPtr<nr_udp_message> > received_msgs_;
 
   nsMainThreadPtrHandle<nsIUDPSocketChild> socket_child_;
   nsCOMPtr<nsIEventTarget> sts_thread_;
   const nsCOMPtr<nsIEventTarget> main_thread_;
   ReentrantMonitor monitor_;
 };
 
+// The socket child holds onto one of these, which just passes callbacks
+// through and makes sure the ref to the NrSocketIpc is released on STS.
+class NrSocketIpcProxy : public nsIUDPSocketInternal {
+public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSIUDPSOCKETINTERNAL
+
+  nsresult Init(const nsRefPtr<NrSocketIpc>& socket);
+
+private:
+  virtual ~NrSocketIpcProxy();
+
+  nsRefPtr<NrSocketIpc> socket_;
+  nsCOMPtr<nsIEventTarget> sts_thread_;
+};
+
 int nr_netaddr_to_transport_addr(const net::NetAddr *netaddr,
                                  nr_transport_addr *addr,
                                  int protocol);
 int nr_praddr_to_transport_addr(const PRNetAddr *praddr,
                                 nr_transport_addr *addr,
                                 int protocol, int keep);
 int nr_transport_addr_get_addrstring_and_port(nr_transport_addr *addr,
                                               nsACString *host, int32_t *port);
--- a/media/mtransport/runnable_utils.h
+++ b/media/mtransport/runnable_utils.h
@@ -91,11 +91,30 @@ RUN_ON_THREAD(nsIEventTarget *thread, de
       MOZ_ASSERT(NS_SUCCEEDED(rv));               \
       MOZ_ASSERT(on);                             \
     }                                           \
   } while(0)
 #else
 #define ASSERT_ON_THREAD(t)
 #endif
 
+template <class T>
+class DispatchedRelease : public detail::runnable_args_base<detail::NoResult> {
+public:
+  explicit DispatchedRelease(already_AddRefed<T>& ref) : ref_(ref) {}
+
+  NS_IMETHOD Run() {
+    ref_ = nullptr;
+    return NS_OK;
+  }
+private:
+  nsRefPtr<T> ref_;
+};
+
+template <typename T>
+DispatchedRelease<T>* WrapRelease(already_AddRefed<T>&& ref)
+{
+  return new DispatchedRelease<T>(ref);
+}
+
 } /* namespace mozilla */
 
 #endif
--- a/media/mtransport/third_party/nICEr/src/stun/stun_server_ctx.c
+++ b/media/mtransport/third_party/nICEr/src/stun/stun_server_ctx.c
@@ -104,16 +104,17 @@ static int nr_stun_server_client_create(
       ABORT(R_NO_MEMORY);
 
     if(!(clnt->username=r_strdup(user)))
       ABORT(R_NO_MEMORY);
 
     if(r=r_data_copy(&clnt->password,pass))
       ABORT(r);
 
+    r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-SERVER(%s)/CLIENT(%s): Adding client for %s",ctx->label, client_label, user);
     clnt->stun_server_cb=cb;
     clnt->cb_arg=cb_arg;
 
     *clntp = clnt;
     _status=0;
  abort:
     if(_status){
       nr_stun_server_destroy_client(clnt);
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
@@ -208,16 +208,17 @@ PeerConnectionImpl* PeerConnectionImpl::
 NS_IMETHODIMP PeerConnectionMedia::ProtocolProxyQueryHandler::
 OnProxyAvailable(nsICancelable *request,
                  nsIChannel *aChannel,
                  nsIProxyInfo *proxyinfo,
                  nsresult result) {
   CSFLogInfo(logTag, "%s: Proxy Available: %d", __FUNCTION__, (int)result);
 
   if (NS_SUCCEEDED(result) && proxyinfo) {
+    CSFLogInfo(logTag, "%s: Had proxyinfo", __FUNCTION__);
     nsresult rv;
     nsCString httpsProxyHost;
     int32_t httpsProxyPort;
 
     rv = proxyinfo->GetHost(httpsProxyHost);
     if (NS_FAILED(rv)) {
       CSFLogError(logTag, "%s: Failed to get proxy server host", __FUNCTION__);
       return rv;
@@ -238,17 +239,17 @@ OnProxyAvailable(nsICancelable *request,
       CSFLogError(logTag, "%s: Failed to set proxy server (ICE ctx unavailable)",
           __FUNCTION__);
     }
   }
 
   if (result != NS_ERROR_ABORT) {
     // NS_ERROR_ABORT means that the PeerConnectionMedia is no longer waiting
     pcm_->mProxyResolveCompleted = true;
-    pcm_->GatherIfReady();
+    pcm_->FlushIceCtxOperationQueueIfReady();
   }
 
   return NS_OK;
 }
 
 NS_IMPL_ISUPPORTS(PeerConnectionMedia::ProtocolProxyQueryHandler, nsIProtocolProxyCallback)
 
 PeerConnectionMedia::PeerConnectionMedia(PeerConnectionImpl *parent)
@@ -256,17 +257,16 @@ PeerConnectionMedia::PeerConnectionMedia
       mParentHandle(parent->GetHandle()),
       mParentName(parent->GetName()),
       mAllowIceLoopback(false),
       mIceCtx(nullptr),
       mDNSResolver(new mozilla::NrIceResolver()),
       mUuidGen(MakeUnique<PCUuidGenerator>()),
       mMainThread(mParent->GetMainThread()),
       mSTSThread(mParent->GetSTSThread()),
-      mTransportsUpdated(false),
       mProxyResolveCompleted(false) {
   nsresult rv;
 
   nsCOMPtr<nsIProtocolProxyService> pps =
     do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &rv);
   if (NS_FAILED(rv)) {
     CSFLogError(logTag, "%s: Failed to get proxy service: %d", __FUNCTION__, (int)rv);
     return;
@@ -395,17 +395,16 @@ PeerConnectionMedia::UpdateTransports(co
                                pwd,
                                candidates),
                   NS_DISPATCH_NORMAL);
   }
 
 
   // TODO(bug 1017888): Need to deal properly with renegotatiation.
   // For now just start gathering.
-  mTransportsUpdated = true;
   GatherIfReady();
 }
 
 nsresult PeerConnectionMedia::UpdateMediaPipelines(
     const mozilla::JsepSession& session) {
   size_t numPairs = session.GetNegotiatedTrackPairCount();
   mozilla::MediaPipelineFactory factory(this);
   const mozilla::JsepTrackPair* pair;
@@ -462,26 +461,27 @@ PeerConnectionMedia::StartIceChecks(cons
     } else {
       CSFLogDebug(logTag, "Transport %u has %u components",
                           static_cast<unsigned>(i),
                           static_cast<unsigned>(transport->mComponents));
       numComponentsByLevel.push_back(transport->mComponents);
     }
   }
 
-  RUN_ON_THREAD(GetSTSThread(),
-                WrapRunnable(
-                  RefPtr<PeerConnectionMedia>(this),
-                  &PeerConnectionMedia::StartIceChecks_s,
-                  session.IsIceControlling(),
-                  session.RemoteIsIceLite(),
-                  // Copy, just in case API changes to return a ref
-                  std::vector<std::string>(session.GetIceOptions()),
-                  numComponentsByLevel),
-                NS_DISPATCH_NORMAL);
+  nsRefPtr<nsIRunnable> runnable(
+      WrapRunnable(
+        RefPtr<PeerConnectionMedia>(this),
+        &PeerConnectionMedia::StartIceChecks_s,
+        session.IsIceControlling(),
+        session.RemoteIsIceLite(),
+        // Copy, just in case API changes to return a ref
+        std::vector<std::string>(session.GetIceOptions()),
+        numComponentsByLevel));
+
+  PerformOrEnqueueIceCtxOperation(runnable);
 }
 
 void
 PeerConnectionMedia::StartIceChecks_s(
     bool aIsControlling,
     bool aIsIceLite,
     const std::vector<std::string>& aIceOptionsList,
     const std::vector<size_t>& aComponentCountByLevel) {
@@ -552,26 +552,52 @@ PeerConnectionMedia::AddIceCandidate_s(c
   if (NS_FAILED(rv)) {
     CSFLogError(logTag, "Couldn't process ICE candidate at level %u",
                 aMLine);
     return;
   }
 }
 
 void
+PeerConnectionMedia::FlushIceCtxOperationQueueIfReady()
+{
+  ASSERT_ON_THREAD(mMainThread);
+
+  if (IsIceCtxReady()) {
+    for (auto i = mQueuedIceCtxOperations.begin();
+         i != mQueuedIceCtxOperations.end();
+         ++i) {
+      GetSTSThread()->Dispatch(*i, NS_DISPATCH_NORMAL);
+    }
+    mQueuedIceCtxOperations.clear();
+  }
+}
+
+void
+PeerConnectionMedia::PerformOrEnqueueIceCtxOperation(
+    const nsRefPtr<nsIRunnable>& runnable)
+{
+  ASSERT_ON_THREAD(mMainThread);
+
+  if (IsIceCtxReady()) {
+    GetSTSThread()->Dispatch(runnable, NS_DISPATCH_NORMAL);
+  } else {
+    mQueuedIceCtxOperations.push_back(runnable);
+  }
+}
+
+void
 PeerConnectionMedia::GatherIfReady() {
   ASSERT_ON_THREAD(mMainThread);
 
-  if (mTransportsUpdated && mProxyResolveCompleted) {
-    RUN_ON_THREAD(GetSTSThread(),
-        WrapRunnable(
-          RefPtr<PeerConnectionMedia>(this),
-          &PeerConnectionMedia::EnsureIceGathering_s),
-        NS_DISPATCH_NORMAL);
-  }
+  nsRefPtr<nsIRunnable> runnable(WrapRunnable(
+        RefPtr<PeerConnectionMedia>(this),
+        &PeerConnectionMedia::EnsureIceGathering_s));
+
+  PerformOrEnqueueIceCtxOperation(runnable);
 }
 
 void
 PeerConnectionMedia::EnsureIceGathering_s() {
   if (mIceCtx->gathering_state() == NrIceCtx::ICE_CTX_GATHER_INIT) {
     if (mProxyServer) {
       mIceCtx->SetProxyServer(*mProxyServer);
     }
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h
@@ -461,16 +461,18 @@ class PeerConnectionMedia : public sigsl
 
   // Manage ICE transports.
   void UpdateIceMediaStream_s(size_t aMLine, size_t aComponentCount,
                               bool aHasAttrs,
                               const std::string& aUfrag,
                               const std::string& aPassword,
                               const std::vector<std::string>& aCandidateList);
   void GatherIfReady();
+  void FlushIceCtxOperationQueueIfReady();
+  void PerformOrEnqueueIceCtxOperation(const nsRefPtr<nsIRunnable>& runnable);
   void EnsureIceGathering_s();
   void StartIceChecks_s(bool aIsControlling,
                         bool aIsIceLite,
                         const std::vector<std::string>& aIceOptionsList,
                         const std::vector<size_t>& aComponentCountByLevel);
 
   // Process a trickle ICE candidate.
   void AddIceCandidate_s(const std::string& aCandidate, const std::string& aMid,
@@ -492,16 +494,19 @@ class PeerConnectionMedia : public sigsl
   void IceGatheringStateChange_m(mozilla::NrIceCtx* ctx,
                                  mozilla::NrIceCtx::GatheringState state);
   void IceConnectionStateChange_m(mozilla::NrIceCtx* ctx,
                                   mozilla::NrIceCtx::ConnectionState state);
   void OnCandidateFound_m(const std::string &candidate, uint16_t aMLine);
   void EndOfLocalCandidates_m(const std::string& aDefaultAddr,
                               uint16_t aDefaultPort,
                               uint16_t aMLine);
+  bool IsIceCtxReady() const {
+    return mProxyResolveCompleted;
+  }
 
 
   // The parent PC
   PeerConnectionImpl *mParent;
   // and a loose handle on it for event driven stuff
   std::string mParentHandle;
   std::string mParentName;
 
@@ -534,18 +539,21 @@ class PeerConnectionMedia : public sigsl
   mozilla::UniquePtr<PCUuidGenerator> mUuidGen;
 
   // The main thread.
   nsCOMPtr<nsIThread> mMainThread;
 
   // The STS thread.
   nsCOMPtr<nsIEventTarget> mSTSThread;
 
-  // Used to track when transports are updated and are ready to start gathering
-  bool mTransportsUpdated;
+  // Used whenever we need to dispatch a runnable to STS to tweak something
+  // on our ICE ctx, but are not ready to do so at the moment (eg; we are
+  // waiting to get a callback with our http proxy config before we start
+  // gathering or start checking)
+  std::vector<nsRefPtr<nsIRunnable>> mQueuedIceCtxOperations;
 
   // Used to cancel any ongoing proxy request.
   nsCOMPtr<nsICancelable> mProxyRequest;
 
   // Used to track the state of the request.
   bool mProxyResolveCompleted;
 
   // Used to store the result of the request.
--- a/mozglue/build/WindowsDllBlocklist.cpp
+++ b/mozglue/build/WindowsDllBlocklist.cpp
@@ -154,16 +154,22 @@ static DllBlockInfo sWindowsDllBlocklist
   { "libredir2.dll", 0x5385B7ED, DllBlockInfo::USE_TIMESTAMP },
 
   // Crashes with RoboForm2Go written against old SDK, bug 988311
   { "rf-firefox-22.dll", ALL_VERSIONS },
 
   // Crashes with DesktopTemperature, bug 1046382
   { "dtwxsvc.dll", 0x53153234, DllBlockInfo::USE_TIMESTAMP },
 
+  // Startup crashes with Lenovo Onekey Theater, bug 1123778
+  { "activedetect32.dll", UNVERSIONED },
+  { "activedetect64.dll", UNVERSIONED },
+  { "windowsapihookdll32.dll", UNVERSIONED },
+  { "windowsapihookdll64.dll", UNVERSIONED },
+
   { nullptr, 0 }
 };
 
 #ifndef STATUS_DLL_NOT_FOUND
 #define STATUS_DLL_NOT_FOUND ((DWORD)0xC0000135L)
 #endif
 
 // define this for very verbose dll load debug spew
--- a/netwerk/cache2/CacheFile.cpp
+++ b/netwerk/cache2/CacheFile.cpp
@@ -607,18 +607,19 @@ CacheFile::OnMetadataWritten(nsresult aR
   LOG(("CacheFile::OnMetadataWritten() [this=%p, rv=0x%08x]", this, aResult));
 
   MOZ_ASSERT(mWritingMetadata);
   mWritingMetadata = false;
 
   MOZ_ASSERT(!mMemoryOnly);
   MOZ_ASSERT(!mOpeningFile);
 
-  if (NS_FAILED(aResult)) {
+  if (NS_WARN_IF(NS_FAILED(aResult))) {
     // TODO close streams with an error ???
+    SetError(aResult);
   }
 
   if (mOutput || mInputs.Length() || mChunks.Count())
     return NS_OK;
 
   if (IsDirty())
     WriteMetadataIfNeededLocked();
 
--- a/netwerk/cache2/CacheFileIOManager.cpp
+++ b/netwerk/cache2/CacheFileIOManager.cpp
@@ -701,16 +701,20 @@ public:
     nsresult rv;
 
     MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable*>(this), "net::cache::write-background");
     if (mHandle->IsClosed()) {
       rv = NS_ERROR_NOT_INITIALIZED;
     } else {
       rv = CacheFileIOManager::gInstance->WriteInternal(
           mHandle, mOffset, mBuf, mCount, mValidate);
+      if (NS_FAILED(rv) && !mCallback) {
+        // No listener is going to handle the error, doom the file
+        CacheFileIOManager::gInstance->DoomFileInternal(mHandle);
+      }
     }
     MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable*>(this), "net::cache::write-background");
 
     MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable*>(this), "net::cache::write-result");
     if (mCallback) {
       mCallback->OnDataWritten(mHandle, mBuf, rv);
     } else {
       free(const_cast<char *>(mBuf));
--- a/security/pkix/test/gtest/pkixbuild_tests.cpp
+++ b/security/pkix/test/gtest/pkixbuild_tests.cpp
@@ -137,17 +137,17 @@ private:
   }
 
   Result IsChainValid(const DERArray&, Time) override
   {
     return Success;
   }
 
   Result VerifySignedData(const SignedDataWithSignature& signedData,
-                          Input subjectPublicKeyInfo)
+                          Input subjectPublicKeyInfo) override
   {
     return TestVerifySignedData(signedData, subjectPublicKeyInfo);
   }
 
   Result DigestBuf(Input item, /*out*/ uint8_t *digestBuf, size_t digestBufLen)
                    override
   {
     ADD_FAILURE();
--- a/toolkit/components/aboutmemory/tests/test_aboutmemory.xul
+++ b/toolkit/components/aboutmemory/tests/test_aboutmemory.xul
@@ -364,17 +364,17 @@ 99.95 MB (100.0%) -- explicit\n\
        └──-0.04 MB (-0.04%) -- d [?!]\n\
           ├───0.02 MB (00.02%) ── e\n\
           └──-0.06 MB (-0.06%) ── f [?!]\n\
 \n\
 Other Measurements\n\
 \n\
 100.00 MB ── heap-allocated\n\
 \n\
-End of 5th\n\
+End of 5th \
 ";
 
   let amvExpectedText =
 "\
 Main Process\n\
 Explicit Allocations\n\
 \n\
 653,876,224 B (100.0%) -- explicit\n\
@@ -533,17 +533,17 @@ 104,801,280 B (100.0%) -- explicit\n\
        └──-40,960 B (-0.04%) -- d [?!]\n\
           ├───20,480 B (00.02%) ── e\n\
           └──-61,440 B (-0.06%) ── f [?!]\n\
 \n\
 Other Measurements\n\
 \n\
 104,857,600 B ── heap-allocated\n\
 \n\
-End of 5th\n\
+End of 5th \
 ";
 
   function finish()
   {
     mgr.unblockRegistrationAndRestoreOriginalReporters();
     SimpleTest.finish();
   }
 
--- a/toolkit/system/dbus/nsNetworkManagerListener.h
+++ b/toolkit/system/dbus/nsNetworkManagerListener.h
@@ -15,19 +15,19 @@ class nsNetworkManagerListener : public 
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSINETWORKLINKSERVICE
 
   nsNetworkManagerListener();
 
   nsresult Init();
 
-  virtual void RegisterWithConnection(DBusConnection* connection);
-  virtual void UnregisterWithConnection(DBusConnection* connection);
-  virtual bool HandleMessage(DBusMessage* msg);
+  virtual void RegisterWithConnection(DBusConnection* connection) MOZ_OVERRIDE;
+  virtual void UnregisterWithConnection(DBusConnection* connection) MOZ_OVERRIDE;
+  virtual bool HandleMessage(DBusMessage* msg) MOZ_OVERRIDE;
 
   /**
    * This gets called when NetworkManager sends us a StateChange signal,
    * or when we receive a reply to our inquiry.
    * The message contains the current NMState.
    */
   void UpdateNetworkStatus(DBusMessage* message);
 
--- a/xpcom/base/SystemMemoryReporter.cpp
+++ b/xpcom/base/SystemMemoryReporter.cpp
@@ -154,17 +154,17 @@ public:
       }                                                                       \
     }                                                                         \
   } while (0)
 
 #define REPORT(_path, _amount, _desc) \
   REPORT_WITH_CLEANUP(_path, UNITS_BYTES, _amount, _desc, (void)0)
 
   NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
-                            nsISupports* aData, bool aAnonymize)
+                            nsISupports* aData, bool aAnonymize) MOZ_OVERRIDE
   {
     // There is lots of privacy-sensitive data in /proc. Just skip this
     // reporter entirely when anonymization is required.
     if (aAnonymize) {
       return NS_OK;
     }
 
     if (!Preferences::GetBool("memory.system_memory_reporter")) {
--- a/xpcom/base/nsMemoryReporterManager.cpp
+++ b/xpcom/base/nsMemoryReporterManager.cpp
@@ -550,17 +550,17 @@ PrivateDistinguishedAmount(int64_t* aN)
 class VsizeMaxContiguousReporter MOZ_FINAL : public nsIMemoryReporter
 {
   ~VsizeMaxContiguousReporter() {}
 
 public:
   NS_DECL_ISUPPORTS
 
   NS_METHOD CollectReports(nsIHandleReportCallback* aHandleReport,
-                           nsISupports* aData, bool aAnonymize)
+                           nsISupports* aData, bool aAnonymize) MOZ_OVERRIDE
   {
     int64_t amount;
     nsresult rv = VsizeMaxContiguousDistinguishedAmount(&amount);
     NS_ENSURE_SUCCESS(rv, rv);
     return MOZ_COLLECT_REPORT(
       "vsize-max-contiguous", KIND_OTHER, UNITS_BYTES, amount,
       "Size of the maximum contiguous block of available virtual "
       "memory.");
@@ -573,17 +573,17 @@ NS_IMPL_ISUPPORTS(VsizeMaxContiguousRepo
 class PrivateReporter MOZ_FINAL : public nsIMemoryReporter
 {
   ~PrivateReporter() {}
 
 public:
   NS_DECL_ISUPPORTS
 
   NS_METHOD CollectReports(nsIHandleReportCallback* aHandleReport,
-                           nsISupports* aData, bool aAnonymize)
+                           nsISupports* aData, bool aAnonymize) MOZ_OVERRIDE
   {
     int64_t amount;
     nsresult rv = PrivateDistinguishedAmount(&amount);
     NS_ENSURE_SUCCESS(rv, rv);
     return MOZ_COLLECT_REPORT(
       "private", KIND_OTHER, UNITS_BYTES, amount,
 "Memory that cannot be shared with other processes, including memory that is "
 "committed and marked MEM_PRIVATE, data that is not mapped, and executable "
@@ -917,17 +917,17 @@ namespace mozilla {
 namespace dmd {
 
 class DMDReporter MOZ_FINAL : public nsIMemoryReporter
 {
 public:
   NS_DECL_ISUPPORTS
 
   NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
-                            nsISupports* aData, bool aAnonymize)
+                            nsISupports* aData, bool aAnonymize) MOZ_OVERRIDE
   {
     dmd::Sizes sizes;
     dmd::SizeOf(&sizes);
 
 #define REPORT(_path, _amount, _desc)                                         \
   do {                                                                        \
     nsresult rv;                                                              \
     rv = aHandleReport->Callback(EmptyCString(), NS_LITERAL_CSTRING(_path),   \