Merge inbound to m-c a=merge
authorWes Kocher <wkocher@mozilla.com>
Wed, 25 Jun 2014 18:18:43 -0700
changeset 190849 464bca437658a87a7cb9846ba916f0e8d715dbc5
parent 190848 6401390a005df7ae8b6b44f179cb75cc7de79ba9 (current diff)
parent 190764 e97f7d9d54d9461bea0cacd451f28831fc0e781b (diff)
child 190850 c99c96d225597df2ddcac4c1b5e092237481f4ff
child 190935 19c155434605bccaab48d250ea48e04357bb37ca
child 190957 15e8c8100ed72fcfd6b8af2fe9d45adcd1537e5a
push id45406
push userkwierso@gmail.com
push dateThu, 26 Jun 2014 01:39:41 +0000
treeherdermozilla-inbound@c99c96d22559 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone33.0a1
first release with
nightly linux32
464bca437658 / 33.0a1 / 20140626030201 / files
nightly linux64
464bca437658 / 33.0a1 / 20140626030201 / files
nightly mac
464bca437658 / 33.0a1 / 20140626030201 / files
nightly win32
464bca437658 / 33.0a1 / 20140626030201 / files
nightly win64
464bca437658 / 33.0a1 / 20140626030201 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to m-c a=merge
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -552,17 +552,18 @@ window[chromehidden~="toolbar"] toolbar:
 #identity-popup-content-box.chromeUI > .identity-popup-label:not(#identity-popup-brandName):not(#identity-popup-chromeLabel),
 #identity-popup-content-box.chromeUI > .identity-popup-description,
 #identity-popup.chromeUI > #identity-popup-button-container,
 #identity-popup-content-box.unknownIdentity > #identity-popup-connectedToLabel ,
 #identity-popup-content-box.unknownIdentity > #identity-popup-runByLabel ,
 #identity-popup-content-box.unknownIdentity > #identity-popup-content-host ,
 #identity-popup-content-box.unknownIdentity > #identity-popup-content-owner ,
 #identity-popup-content-box.verifiedIdentity > #identity-popup-connectedToLabel2 ,
-#identity-popup-content-box.verifiedDomain > #identity-popup-connectedToLabel2 {
+#identity-popup-content-box.verifiedDomain > #identity-popup-connectedToLabel2 ,
+#identity-popup-content-box.verifiedDomain > #identity-popup-runByLabel {
   display: none;
 }
 
 /*  Full Screen UI */
 
 #fullscr-toggler {
   height: 1px;
   background: black;
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -6623,17 +6623,16 @@ var gIdentityHandler = {
     let supplemental = "";
     let verifier = "";
     let host = "";
     let owner = "";
 
     switch (newMode) {
     case this.IDENTITY_MODE_DOMAIN_VERIFIED:
       host = this.getEffectiveHost();
-      owner = gNavigatorBundle.getString("identity.ownerUnknown2");
       verifier = this._identityBox.tooltipText;
       break;
     case this.IDENTITY_MODE_IDENTIFIED: {
       // If it's identified, then we can populate the dialog with credentials
       let iData = this.getIdentityData();
       host = this.getEffectiveHost();
       owner = iData.subjectOrg;
       verifier = this._identityBox.tooltipText;
--- a/browser/base/content/pageinfo/pageInfo.js
+++ b/browser/base/content/pageinfo/pageInfo.js
@@ -437,24 +437,16 @@ function loadTab(args)
   var initialTab = (args && args.initialTab) || "generalTab";
   var radioGroup = document.getElementById("viewGroup");
   initialTab = document.getElementById(initialTab) || document.getElementById("generalTab");
   radioGroup.selectedItem = initialTab;
   radioGroup.selectedItem.doCommand();
   radioGroup.focus();
 }
 
-function onClickMore()
-{
-  var radioGrp = document.getElementById("viewGroup");
-  var radioElt = document.getElementById("securityTab");
-  radioGrp.selectedItem = radioElt;
-  showTab('security');
-}
-
 function toggleGroupbox(id)
 {
   var elt = document.getElementById(id);
   if (elt.hasAttribute("closed")) {
     elt.removeAttribute("closed");
     if (elt.flexWhenOpened)
       elt.flex = elt.flexWhenOpened;
   }
--- a/browser/base/content/pageinfo/pageInfo.xul
+++ b/browser/base/content/pageinfo/pageInfo.xul
@@ -150,26 +150,16 @@
             <splitter class="tree-splitter"/>
             <treecol id="meta-content" label="&generalMetaContent;"
                      persist="width" flex="4"
                      onclick="gMetaView.onPageMediaSort('meta-content');"/>
           </treecols>
           <treechildren id="metatreechildren" flex="1"/>
         </tree>        
       </groupbox>
-      <groupbox id="securityBox">
-        <caption id="securityBoxCaption" label="&securityHeader;"/>
-        <description id="general-security-identity" class="header"/>
-        <description id="general-security-privacy"  class="header"/>
-        <hbox id="securityDetailsButtonBox" align="right">
-          <button id="security-view-details" label="&generalSecurityDetails;"
-                  accesskey="&generalSecurityDetails.accesskey;"
-                  oncommand="onClickMore();"/>
-        </hbox>
-      </groupbox>
     </vbox>
 
     <!-- Media information -->
     <vbox id="mediaPanel">
       <tree id="imagetree" onselect="onImageSelect();" contextmenu="picontext"
             ondragstart="onBeginLinkDrag(event,'image-address','image-alt')">
         <treecols>
           <treecol sortSeparators="true" primary="true" persist="width" flex="10"
--- a/browser/base/content/pageinfo/security.js
+++ b/browser/base/content/pageinfo/security.js
@@ -149,63 +149,56 @@ var security = {
 
   _cert : null
 };
 
 function securityOnLoad() {
   var info = security._getSecurityInfo();
   if (!info) {
     document.getElementById("securityTab").hidden = true;
-    document.getElementById("securityBox").collapsed = true;
     return;
   }
   else {
     document.getElementById("securityTab").hidden = false;
-    document.getElementById("securityBox").collapsed = false;
   }
 
   const pageInfoBundle = document.getElementById("pageinfobundle");
 
   /* Set Identity section text */
   setText("security-identity-domain-value", info.hostName);
   
-  var owner, verifier, generalPageIdentityString;
+  var owner, verifier;
   if (info.cert && !info.isBroken) {
     // Try to pull out meaningful values.  Technically these fields are optional
     // so we'll employ fallbacks where appropriate.  The EV spec states that Org
     // fields must be specified for subject and issuer so that case is simpler.
     if (info.isEV) {
       owner = info.cert.organization;
       verifier = security.mapIssuerOrganization(info.cAName);
-      generalPageIdentityString = pageInfoBundle.getFormattedString("generalSiteIdentity",
-                                                                    [owner, verifier]);
     }
     else {
       // Technically, a non-EV cert might specify an owner in the O field or not,
       // depending on the CA's issuing policies.  However we don't have any programmatic
       // way to tell those apart, and no policy way to establish which organization
       // vetting standards are good enough (that's what EV is for) so we default to
       // treating these certs as domain-validated only.
       owner = pageInfoBundle.getString("securityNoOwner");
       verifier = security.mapIssuerOrganization(info.cAName ||
                                                 info.cert.issuerCommonName ||
                                                 info.cert.issuerName);
-      generalPageIdentityString = owner;
     }
   }
   else {
     // We don't have valid identity credentials.
     owner = pageInfoBundle.getString("securityNoOwner");
     verifier = pageInfoBundle.getString("notset");
-    generalPageIdentityString = owner;
   }
 
   setText("security-identity-owner-value", owner);
   setText("security-identity-verifier-value", verifier);
-  setText("general-security-identity", generalPageIdentityString);
 
   /* Manage the View Cert button*/
   var viewCert = document.getElementById("security-view-cert");
   if (info.cert) {
     security._cert = info.cert;
     viewCert.collapsed = false;
   }
   else
@@ -264,17 +257,16 @@ function securityOnLoad() {
       msg1 = pkiBundle.getFormattedString("pageInfo_Privacy_None1", [info.hostName]);
     else
       msg1 = pkiBundle.getString("pageInfo_Privacy_None3");
     msg2 = pkiBundle.getString("pageInfo_Privacy_None2");
   }
   setText("security-technical-shortform", hdr);
   setText("security-technical-longform1", msg1);
   setText("security-technical-longform2", msg2); 
-  setText("general-security-privacy", hdr);
 }
 
 function setText(id, value)
 {
   var element = document.getElementById(id);
   if (!element)
     return;
   if (element.localName == "textbox" || element.localName == "label")
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -287,18 +287,16 @@ identity.identified.state_and_country=%S
 
 identity.encrypted2=The connection to this website is secure.
 identity.mixed_display_loaded=The connection to this website is not fully secure because it contains unencrypted elements (such as images).
 identity.mixed_active_loaded2=This website contains interactive content that isn't encrypted (such as scripts). Other people can view your information or modify the website's behavior.
 identity.unencrypted=Your connection to this website is not encrypted.
 
 identity.unknown.tooltip=This website does not supply identity information.
 
-identity.ownerUnknown2=(unknown)
-
 # LOCALIZATION NOTE (identity.chrome): %S is replaced with the brandShortName.
 identity.chrome=This is a secure %S page.
 
 # Edit Bookmark UI
 editBookmarkPanel.pageBookmarkedTitle=Page Bookmarked
 editBookmarkPanel.pageBookmarkedDescription=%S will always remember this page for you.
 editBookmarkPanel.bookmarkedRemovedTitle=Bookmark Removed
 editBookmarkPanel.editBookmarkTitle=Edit This Bookmark
--- a/browser/locales/en-US/chrome/browser/pageInfo.dtd
+++ b/browser/locales/en-US/chrome/browser/pageInfo.dtd
@@ -20,18 +20,16 @@
 <!ENTITY  generalMode           "Render Mode:">
 <!ENTITY  generalSize           "Size:">
 <!ENTITY  generalReferrer       "Referring URL:">
 <!ENTITY  generalSource         "Cache Source:">
 <!ENTITY  generalModified       "Modified:">
 <!ENTITY  generalEncoding       "Encoding:">
 <!ENTITY  generalMetaName       "Name">
 <!ENTITY  generalMetaContent    "Content">
-<!ENTITY  generalSecurityDetails   "Details">
-<!ENTITY  generalSecurityDetails.accesskey "D">
 
 <!ENTITY  mediaTab              "Media">
 <!ENTITY  mediaTab.accesskey    "M">
 <!ENTITY  mediaLocation         "Location:">
 <!ENTITY  mediaText             "Associated Text:">
 <!ENTITY  mediaAltHeader        "Alternate Text">
 <!ENTITY  mediaAddress          "Address">
 <!ENTITY  mediaType             "Type">
@@ -60,17 +58,16 @@
 <!ENTITY  permissionsFor        "Permissions for:">
 <!ENTITY  permPlugins           "Activate Plugins">
 
 <!ENTITY  permClearStorage           "Clear Storage">
 <!ENTITY  permClearStorage.accesskey "C">
 
 <!ENTITY  securityTab           "Security">
 <!ENTITY  securityTab.accesskey "S">
-<!ENTITY  securityHeader        "Security information for this page">
 <!ENTITY  securityView.certView "View Certificate">
 <!ENTITY  securityView.accesskey "V">
 <!ENTITY  securityView.unknown   "Unknown">
 
 
 <!ENTITY  securityView.identity.header   "Website Identity">
 <!ENTITY  securityView.identity.owner    "Owner:">
 <!ENTITY  securityView.identity.domain   "Website:">
--- a/browser/locales/en-US/chrome/browser/pageInfo.properties
+++ b/browser/locales/en-US/chrome/browser/pageInfo.properties
@@ -33,17 +33,16 @@ mediaAnimatedImageType=%S Image (animate
 mediaDimensions=%Spx \u00D7 %Spx
 mediaDimensionsScaled=%Spx \u00D7 %Spx (scaled to %Spx \u00D7 %Spx)
 
 generalQuirksMode=Quirks mode
 generalStrictMode=Standards compliance mode
 generalSize=%S KB (%S bytes)
 generalMetaTag=Meta (1 tag)
 generalMetaTags=Meta (%S tags)
-generalSiteIdentity=This website is owned by %S\nThis has been verified by %S
 
 feedRss=RSS
 feedAtom=Atom
 feedXML=XML
 
 securityNoOwner=This website does not supply ownership information.
 securityOneVisit=Yes, once
 securityNVisits=Yes, %S times
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -362,16 +362,29 @@ public:
    */
   static bool IsHTMLBlock(nsIAtom* aLocalName);
 
   /**
    * Is the HTML local name a void element?
    */
   static bool IsHTMLVoid(nsIAtom* aLocalName);
 
+  enum ParseHTMLIntegerResultFlags {
+    eParseHTMLInteger_NoFlags               = 0,
+    eParseHTMLInteger_IsPercent             = 1 << 0,
+    eParseHTMLInteger_NonStandard           = 1 << 1,
+    eParseHTMLInteger_DidNotConsumeAllInput = 1 << 2,
+    // Set if one or more error flags were set.
+    eParseHTMLInteger_Error                 = 1 << 3,
+    eParseHTMLInteger_ErrorNoValue          = 1 << 4,
+    eParseHTMLInteger_ErrorOverflow         = 1 << 5
+  };
+  static int32_t ParseHTMLInteger(const nsAString& aValue,
+                                  ParseHTMLIntegerResultFlags *aResult);
+
   /**
    * Parse a margin string of format 'top, right, bottom, left' into
    * an nsIntMargin.
    *
    * @param aString the string to parse
    * @param aResult the resulting integer
    * @return whether the value could be parsed
    */
--- a/content/base/src/EventSource.cpp
+++ b/content/base/src/EventSource.cpp
@@ -1233,17 +1233,17 @@ EventSource::DispatchAllMessageEvents()
   mGoingToDispatchAllMessages = false;
 
   nsresult rv = CheckInnerWindowCorrectness();
   if (NS_FAILED(rv)) {
     return;
   }
 
   AutoJSAPI jsapi;
-  if (NS_WARN_IF(!jsapi.InitUsingWin(GetOwner()))) {
+  if (NS_WARN_IF(!jsapi.Init(GetOwner()))) {
     return;
   }
   JSContext* cx = jsapi.cx();
 
   while (mMessagesToDispatch.GetSize() > 0) {
     nsAutoPtr<Message>
       message(static_cast<Message*>(mMessagesToDispatch.PopFront()));
 
--- a/content/base/src/ResponsiveImageSelector.cpp
+++ b/content/base/src/ResponsiveImageSelector.cpp
@@ -5,24 +5,41 @@
 
 #include "mozilla/dom/ResponsiveImageSelector.h"
 #include "nsIURI.h"
 #include "nsIDocument.h"
 #include "nsContentUtils.h"
 #include "nsPresContext.h"
 #include "nsNetUtil.h"
 
+#include "nsCSSParser.h"
+#include "nsCSSProps.h"
+#include "nsIMediaList.h"
+#include "nsRuleNode.h"
+#include "nsRuleData.h"
+
 using namespace mozilla;
 using namespace mozilla::dom;
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_ISUPPORTS0(ResponsiveImageSelector);
 
+static bool
+ParseInteger(const nsAString& aString, int32_t& aInt)
+{
+  nsContentUtils::ParseHTMLIntegerResultFlags parseResult;
+  aInt = nsContentUtils::ParseHTMLInteger(aString, &parseResult);
+  return !(parseResult &
+           ( nsContentUtils::eParseHTMLInteger_Error |
+             nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput |
+             nsContentUtils::eParseHTMLInteger_IsPercent ));
+}
+
 ResponsiveImageSelector::ResponsiveImageSelector(nsIContent *aContent)
   : mContent(aContent),
     mBestCandidateIndex(-1)
 {
 }
 
 ResponsiveImageSelector::~ResponsiveImageSelector()
 {}
@@ -129,16 +146,31 @@ ResponsiveImageSelector::SetDefaultSourc
   rv = nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(candidateURL),
                                                  aSpec, doc, docBaseURI);
   NS_ENSURE_SUCCESS(rv, rv);
 
   SetDefaultSource(candidateURL);
   return NS_OK;
 }
 
+uint32_t
+ResponsiveImageSelector::NumCandidates(bool aIncludeDefault)
+{
+  uint32_t candidates = mCandidates.Length();
+
+  // If present, the default candidate is the last item
+  if (!aIncludeDefault && candidates &&
+      (mCandidates[candidates - 1].Type() ==
+       ResponsiveImageCandidate::eCandidateType_Default)) {
+    candidates--;
+  }
+
+  return candidates;
+}
+
 void
 ResponsiveImageSelector::SetDefaultSource(nsIURI *aURL)
 {
   // Check if the last element of our candidates is a default
   int32_t candidates = mCandidates.Length();
   if (candidates && (mCandidates[candidates - 1].Type() ==
                      ResponsiveImageCandidate::eCandidateType_Default)) {
     mCandidates.RemoveElementAt(candidates - 1);
@@ -148,21 +180,46 @@ ResponsiveImageSelector::SetDefaultSourc
   }
 
   // Add new default if set
   if (aURL) {
     AppendDefaultCandidate(aURL);
   }
 }
 
+bool
+ResponsiveImageSelector::SetSizesFromDescriptor(const nsAString & aSizes)
+{
+  mSizeQueries.Clear();
+  mSizeValues.Clear();
+  mBestCandidateIndex = -1;
+
+  nsCSSParser cssParser;
+
+  if (!cssParser.ParseSourceSizeList(aSizes, nullptr, 0,
+                                     mSizeQueries, mSizeValues, true)) {
+    return false;
+  }
+
+  return mSizeQueries.Length() > 0;
+}
+
 void
 ResponsiveImageSelector::AppendCandidateIfUnique(const ResponsiveImageCandidate & aCandidate)
 {
+  int numCandidates = mCandidates.Length();
+
+  // With the exception of Default, which should not be added until we are done
+  // building the list, the spec forbids mixing width and explicit density
+  // selectors in the same set.
+  if (numCandidates && mCandidates[0].Type() != aCandidate.Type()) {
+    return;
+  }
+
   // Discard candidates with identical parameters, they will never match
-  int numCandidates = mCandidates.Length();
   for (int i = 0; i < numCandidates; i++) {
     if (mCandidates[i].HasSameParameter(aCandidate)) {
       return;
     }
   }
 
   mBestCandidateIndex = -1;
   mCandidates.AppendElement(aCandidate);
@@ -198,17 +255,17 @@ ResponsiveImageSelector::GetSelectedImag
 double
 ResponsiveImageSelector::GetSelectedImageDensity()
 {
   int bestIndex = GetBestCandidateIndex();
   if (bestIndex < 0) {
     return 1.0;
   }
 
-  return mCandidates[bestIndex].Density();
+  return mCandidates[bestIndex].Density(this);
 }
 
 int
 ResponsiveImageSelector::GetBestCandidateIndex()
 {
   if (mBestCandidateIndex != -1) {
     return mBestCandidateIndex;
   }
@@ -228,34 +285,101 @@ ResponsiveImageSelector::GetBestCandidat
   }
 
   double displayDensity = pctx->CSSPixelsToDevPixels(1.0f);
 
   // Per spec, "In a UA-specific manner, choose one image source"
   // - For now, select the lowest density greater than displayDensity, otherwise
   //   the greatest density available
 
-  int bestIndex = 0; // First index will always be the best so far
-  double bestDensity = mCandidates[bestIndex].Density();
-  for (int i = 1; i < numCandidates; i++) {
-    double candidateDensity = mCandidates[i].Density();
+  // If the list contains computed width candidates, compute the current
+  // effective image width. Note that we currently disallow both computed and
+  // static density candidates in the same selector, so checking the first
+  // candidate is sufficient.
+  int32_t computedWidth = -1;
+  if (numCandidates && mCandidates[0].IsComputedFromWidth()) {
+    DebugOnly<bool> computeResult = \
+      ComputeFinalWidthForCurrentViewport(&computedWidth);
+    MOZ_ASSERT(computeResult,
+               "Computed candidates not allowed without sizes data");
+
+    // If we have a default candidate in the list, don't consider it when using
+    // computed widths. (It has a static 1.0 density that is inapplicable to a
+    // sized-image)
+    if (numCandidates > 1 && mCandidates[numCandidates - 1].Type() ==
+        ResponsiveImageCandidate::eCandidateType_Default) {
+      numCandidates--;
+    }
+  }
+
+  int bestIndex = -1;
+  double bestDensity = -1.0;
+  for (int i = 0; i < numCandidates; i++) {
+    double candidateDensity = \
+      (computedWidth == -1) ? mCandidates[i].Density(this)
+                            : mCandidates[i].Density(computedWidth);
     // - If bestIndex is below display density, pick anything larger.
     // - Otherwise, prefer if less dense than bestDensity but still above
     //   displayDensity.
-    if ((bestDensity < displayDensity && candidateDensity > bestDensity) ||
-        (candidateDensity > displayDensity && candidateDensity < bestDensity)) {
+    if (bestIndex == -1 ||
+        (bestDensity < displayDensity && candidateDensity > bestDensity) ||
+        (candidateDensity >= displayDensity && candidateDensity < bestDensity)) {
       bestIndex = i;
       bestDensity = candidateDensity;
     }
   }
 
+  MOZ_ASSERT(bestIndex >= 0 && bestIndex < numCandidates);
   mBestCandidateIndex = bestIndex;
   return bestIndex;
 }
 
+bool
+ResponsiveImageSelector::ComputeFinalWidthForCurrentViewport(int32_t *aWidth)
+{
+  unsigned int numSizes = mSizeQueries.Length();
+  if (!numSizes) {
+    return false;
+  }
+
+  nsIDocument* doc = mContent ? mContent->OwnerDoc() : nullptr;
+  nsIPresShell *presShell = doc ? doc->GetShell() : nullptr;
+  nsPresContext *pctx = presShell ? presShell->GetPresContext() : nullptr;
+
+  if (!pctx) {
+    MOZ_ASSERT(false, "Unable to find presContext for this content");
+    return false;
+  }
+
+  MOZ_ASSERT(numSizes == mSizeValues.Length(),
+             "mSizeValues length differs from mSizeQueries");
+
+  unsigned int i;
+  for (i = 0; i < numSizes; i++) {
+    if (mSizeQueries[i]->Matches(pctx, nullptr)) {
+      break;
+    }
+  }
+
+  nscoord effectiveWidth;
+  if (i == numSizes) {
+    // No match defaults to 100% viewport
+    nsCSSValue defaultWidth(100.0f, eCSSUnit_ViewportWidth);
+    effectiveWidth = nsRuleNode::CalcLengthWithInitialFont(pctx,
+                                                           defaultWidth);
+  } else {
+    effectiveWidth = nsRuleNode::CalcLengthWithInitialFont(pctx,
+                                                           mSizeValues[i]);
+  }
+
+  MOZ_ASSERT(effectiveWidth >= 0);
+  *aWidth = nsPresContext::AppUnitsToIntCSSPixels(std::max(effectiveWidth, 0));
+  return true;
+}
+
 ResponsiveImageCandidate::ResponsiveImageCandidate()
 {
   mType = eCandidateType_Invalid;
   mValue.mDensity = 1.0;
 }
 
 ResponsiveImageCandidate::ResponsiveImageCandidate(nsIURI *aURL,
                                                    double aDensity)
@@ -268,16 +392,23 @@ ResponsiveImageCandidate::ResponsiveImag
 
 void
 ResponsiveImageCandidate::SetURL(nsIURI *aURL)
 {
   mURL = aURL;
 }
 
 void
+ResponsiveImageCandidate::SetParameterAsComputedWidth(int32_t aWidth)
+{
+  mType = eCandidateType_ComputedFromWidth;
+  mValue.mWidth = aWidth;
+}
+
+void
 ResponsiveImageCandidate::SetParameterDefault()
 {
   MOZ_ASSERT(mType == eCandidateType_Invalid, "double setting candidate type");
 
   mType = eCandidateType_Default;
   // mValue shouldn't actually be used for this type, but set it to default
   // anyway
   mValue.mDensity = 1.0;
@@ -292,16 +423,17 @@ ResponsiveImageCandidate::SetParameterAs
   mValue.mDensity = aDensity;
 }
 
 bool
 ResponsiveImageCandidate::SetParamaterFromDescriptor(const nsAString & aDescriptor)
 {
   // Valid input values must be positive, using -1 for not-set
   double density = -1.0;
+  int32_t width = -1;
 
   nsAString::const_iterator iter, end;
   aDescriptor.BeginReading(iter);
   aDescriptor.EndReading(end);
 
   // Parse descriptor list
   // We currently only support a single density descriptor of the form:
   //   <floating-point number>x
@@ -324,33 +456,47 @@ ResponsiveImageCandidate::SetParamaterFr
 
     // Iter is at end of descriptor, type is single character previous to that.
     // Safe because we just verified that iter > start
     --iter;
     nsAString::const_iterator type(iter);
     ++iter;
 
     const nsDependentSubstring& valStr = Substring(start, type);
-    if (*type == char16_t('x')) {
-      nsresult rv;
-      double possibleDensity = PromiseFlatString(valStr).ToDouble(&rv);
-      if (density == -1.0 && NS_SUCCEEDED(rv) && possibleDensity > 0.0) {
-        density = possibleDensity;
+    if (*type == char16_t('w')) {
+      int32_t possibleWidth;
+      if (width == -1 && density == -1.0) {
+        if (ParseInteger(valStr, possibleWidth) && possibleWidth > 0) {
+          width = possibleWidth;
+        }
+      } else {
+        return false;
+      }
+    } else if (*type == char16_t('x')) {
+      if (width == -1 && density == -1.0) {
+        nsresult rv;
+        double possibleDensity = PromiseFlatString(valStr).ToDouble(&rv);
+        if (NS_SUCCEEDED(rv) && possibleDensity > 0.0) {
+          density = possibleDensity;
+        }
       } else {
         return false;
       }
     }
   }
 
-  // Not explicitly set -> 1.0
-  if (density == -1.0) {
-    density = 1.0;
+  if (width != -1) {
+    SetParameterAsComputedWidth(width);
+  } else if (density != -1.0) {
+    SetParameterAsDensity(density);
+  } else {
+    // No valid descriptors -> 1.0 density
+    SetParameterAsDensity(1.0);
   }
 
-  SetParameterAsDensity(density);
   return true;
 }
 
 bool
 ResponsiveImageCandidate::HasSameParameter(const ResponsiveImageCandidate & aOther) const
 {
   if (aOther.mType != mType) {
     return false;
@@ -362,46 +508,82 @@ ResponsiveImageCandidate::HasSameParamet
 
   if (mType == eCandidateType_Density) {
     return aOther.mValue.mDensity == mValue.mDensity;
   }
 
   if (mType == eCandidateType_Invalid) {
     MOZ_ASSERT(false, "Comparing invalid candidates?");
     return true;
+  } else if (mType == eCandidateType_ComputedFromWidth) {
+    return aOther.mValue.mWidth == mValue.mWidth;
   }
 
   MOZ_ASSERT(false, "Somebody forgot to check for all uses of this enum");
   return false;
 }
 
+already_AddRefed<nsIURI>
+ResponsiveImageCandidate::URL() const
+{
+  nsCOMPtr<nsIURI> url = mURL;
+  return url.forget();
+}
+
 double
-ResponsiveImageCandidate::Density() const
+ResponsiveImageCandidate::Density(ResponsiveImageSelector *aSelector) const
 {
-  // When we support 'sizes' this will get more interesting
+  if (mType == eCandidateType_ComputedFromWidth) {
+    int32_t width;
+    if (!aSelector->ComputeFinalWidthForCurrentViewport(&width)) {
+      return 1.0;
+    }
+    return Density(width);
+  }
+
+  // Other types don't need matching width
+  MOZ_ASSERT(mType == eCandidateType_Default || mType == eCandidateType_Density,
+             "unhandled candidate type");
+  return Density(-1);
+}
+
+double
+ResponsiveImageCandidate::Density(int32_t aMatchingWidth) const
+{
   if (mType == eCandidateType_Invalid) {
     MOZ_ASSERT(false, "Getting density for uninitialized candidate");
     return 1.0;
   }
 
   if (mType == eCandidateType_Default) {
     return 1.0;
   }
 
   if (mType == eCandidateType_Density) {
     return mValue.mDensity;
+  } else if (mType == eCandidateType_ComputedFromWidth) {
+    if (aMatchingWidth <= 0) {
+      MOZ_ASSERT(false, "0 or negative matching width is invalid per spec");
+      return 1.0;
+    }
+    double density = double(mValue.mWidth) / double(aMatchingWidth);
+    MOZ_ASSERT(density > 0.0);
+    return density;
   }
 
-  MOZ_ASSERT(false, "Bad candidate type in Density()");
+  MOZ_ASSERT(false, "Unknown candidate type");
   return 1.0;
 }
 
-already_AddRefed<nsIURI>
-ResponsiveImageCandidate::URL() const
+bool
+ResponsiveImageCandidate::IsComputedFromWidth() const
 {
-  MOZ_ASSERT(mType != eCandidateType_Invalid,
-             "Getting URL of incomplete candidate");
-  nsCOMPtr<nsIURI> url = mURL;
-  return url.forget();
+  if (mType == eCandidateType_ComputedFromWidth) {
+    return true;
+  }
+
+  MOZ_ASSERT(mType == eCandidateType_Default || mType == eCandidateType_Density,
+             "Unknown candidate type");
+  return false;
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/content/base/src/ResponsiveImageSelector.h
+++ b/content/base/src/ResponsiveImageSelector.h
@@ -5,35 +5,47 @@
 
 #ifndef mozilla_dom_responsiveimageselector_h__
 #define mozilla_dom_responsiveimageselector_h__
 
 #include "nsISupports.h"
 #include "nsIContent.h"
 #include "nsString.h"
 
+class nsMediaQuery;
+class nsCSSValue;
+
 namespace mozilla {
 namespace dom {
 
 class ResponsiveImageCandidate;
 
 class ResponsiveImageSelector : public nsISupports
 {
+  friend class ResponsiveImageCandidate;
 public:
   NS_DECL_ISUPPORTS
   ResponsiveImageSelector(nsIContent *aContent);
 
   // Given a srcset string, parse and replace current candidates (does not
   // replace default source)
   bool SetCandidatesFromSourceSet(const nsAString & aSrcSet);
 
+  // Fill the source sizes from a valid sizes descriptor. Returns false if
+  // descriptor is invalid.
+  bool SetSizesFromDescriptor(const nsAString & aSizesDescriptor);
+
   // Set the default source, treated as the least-precedence 1.0 density source.
   nsresult SetDefaultSource(const nsAString & aSpec);
   void SetDefaultSource(nsIURI *aURL);
 
+  uint32_t NumCandidates(bool aIncludeDefault = true);
+
+  nsIContent *Content() { return mContent; }
+
   // Get the URL for the selected best candidate
   already_AddRefed<nsIURI> GetSelectedImageURL();
   double GetSelectedImageDensity();
 
 protected:
   virtual ~ResponsiveImageSelector();
 
 private:
@@ -43,62 +55,83 @@ private:
 
   // Append a default candidate with this URL. Does not check if the array
   // already contains one, use SetDefaultSource instead.
   void AppendDefaultCandidate(nsIURI *aURL);
 
   // Get index of best candidate
   int GetBestCandidateIndex();
 
+  // Compute a density from a Candidate width. Returns false if sizes were not
+  // specified for this selector.
+  //
+  // aContext is the presContext to use for current viewport sizing, null will
+  // use the associated content's context.
+  bool ComputeFinalWidthForCurrentViewport(int32_t *aWidth);
+
   nsCOMPtr<nsIContent> mContent;
   // If this array contains an eCandidateType_Default, it should be the last
   // element, such that the Setters can preserve/replace it respectively.
   nsTArray<ResponsiveImageCandidate> mCandidates;
   int mBestCandidateIndex;
+
+  nsTArray< nsAutoPtr<nsMediaQuery> > mSizeQueries;
+  nsTArray<nsCSSValue> mSizeValues;
 };
 
 class ResponsiveImageCandidate {
 public:
   ResponsiveImageCandidate();
   ResponsiveImageCandidate(nsIURI *aURL, double aDensity);
 
   void SetURL(nsIURI *aURL);
   // Set this as a default-candidate. This behaves the same as density 1.0, but
   // has a differing type such that it can be replaced by subsequent
   // SetDefaultSource calls.
   void SetParameterDefault();
 
   // Set this candidate as a by-density candidate with specified density.
   void SetParameterAsDensity(double aDensity);
+  void SetParameterAsComputedWidth(int32_t aWidth);
 
   // Fill from a valid candidate descriptor. Returns false descriptor is
   // invalid.
   bool SetParamaterFromDescriptor(const nsAString & aDescriptor);
 
   // Check if our parameter (which does not include the url) is identical
   bool HasSameParameter(const ResponsiveImageCandidate & aOther) const;
 
   already_AddRefed<nsIURI> URL() const;
-  double Density() const;
+
+  // Compute and return the density relative to a selector.
+  double Density(ResponsiveImageSelector *aSelector) const;
+  // If the width is already known. Useful when iterating over candidates to
+  // avoid having each call re-compute the width.
+  double Density(int32_t aMatchingWidth) const;
+
+  // If this selector is computed from the selector's matching width.
+  bool IsComputedFromWidth() const;
 
   enum eCandidateType {
     eCandidateType_Invalid,
     eCandidateType_Density,
     // Treated as 1.0 density, but a separate type so we can update the
     // responsive candidates and default separately
     eCandidateType_Default,
+    eCandidateType_ComputedFromWidth
   };
 
   eCandidateType Type() const { return mType; }
 
 private:
 
   nsCOMPtr<nsIURI> mURL;
   eCandidateType mType;
   union {
     double mDensity;
+    int32_t mWidth;
   } mValue;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_responsiveimageselector_h__
--- a/content/base/src/WebSocket.cpp
+++ b/content/base/src/WebSocket.cpp
@@ -861,17 +861,17 @@ WebSocket::CreateAndDispatchMessageEvent
 {
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
 
   nsresult rv = CheckInnerWindowCorrectness();
   if (NS_FAILED(rv))
     return NS_OK;
 
   AutoJSAPI jsapi;
-  if (NS_WARN_IF(!jsapi.InitUsingWin(GetOwner()))) {
+  if (NS_WARN_IF(!jsapi.Init(GetOwner()))) {
     return NS_ERROR_FAILURE;
   }
   JSContext* cx = jsapi.cx();
 
   // Create appropriate JS object for message
   JS::Rooted<JS::Value> jsData(cx);
   if (isBinary) {
     if (mBinaryType == dom::BinaryType::Blob) {
--- a/content/base/src/nsAttrValue.cpp
+++ b/content/base/src/nsAttrValue.cpp
@@ -1438,94 +1438,102 @@ nsAttrValue::ParseEnumValue(const nsAStr
   return false;
 }
 
 bool
 nsAttrValue::ParseSpecialIntValue(const nsAString& aString)
 {
   ResetIfSet();
 
-  nsresult ec;
-  bool strict;
-  bool isPercent = false;
   nsAutoString tmp(aString);
-  int32_t originalVal = StringToInteger(aString, &strict, &ec, true, &isPercent);
+  nsContentUtils::ParseHTMLIntegerResultFlags result;
+  int32_t originalVal = nsContentUtils::ParseHTMLInteger(aString, &result);
 
-  if (NS_FAILED(ec)) {
+  if (result & nsContentUtils::eParseHTMLInteger_Error) {
     return false;
   }
 
+  bool isPercent = result & nsContentUtils::eParseHTMLInteger_IsPercent;
   int32_t val = std::max(originalVal, 0);
+  bool nonStrict = val != originalVal ||
+                   (result & nsContentUtils::eParseHTMLInteger_NonStandard) ||
+                   (result & nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput);
 
   // % (percent)
   if (isPercent || tmp.RFindChar('%') >= 0) {
     isPercent = true;
   }
 
-  strict = strict && (originalVal == val);
-
-  SetIntValueAndType(val,
-                     isPercent ? ePercent : eInteger,
-                     strict ? nullptr : &aString);
+  SetIntValueAndType(val, isPercent ? ePercent : eInteger,
+                     nonStrict ? &aString : nullptr);
   return true;
 }
 
 bool
 nsAttrValue::ParseIntWithBounds(const nsAString& aString,
                                 int32_t aMin, int32_t aMax)
 {
   NS_PRECONDITION(aMin < aMax, "bad boundaries");
 
   ResetIfSet();
 
-  nsresult ec;
-  bool strict;
-  int32_t originalVal = StringToInteger(aString, &strict, &ec);
-  if (NS_FAILED(ec)) {
+  nsContentUtils::ParseHTMLIntegerResultFlags result;
+  int32_t originalVal = nsContentUtils::ParseHTMLInteger(aString, &result);
+  if (result & nsContentUtils::eParseHTMLInteger_Error) {
     return false;
   }
 
   int32_t val = std::max(originalVal, aMin);
   val = std::min(val, aMax);
-  strict = strict && (originalVal == val);
-  SetIntValueAndType(val, eInteger, strict ? nullptr : &aString);
+  bool nonStrict = (val != originalVal) ||
+                   (result & nsContentUtils::eParseHTMLInteger_IsPercent) ||
+                   (result & nsContentUtils::eParseHTMLInteger_NonStandard) ||
+                   (result & nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput);
+
+  SetIntValueAndType(val, eInteger, nonStrict ? &aString : nullptr);
 
   return true;
 }
 
 bool
 nsAttrValue::ParseNonNegativeIntValue(const nsAString& aString)
 {
   ResetIfSet();
 
-  nsresult ec;
-  bool strict;
-  int32_t originalVal = StringToInteger(aString, &strict, &ec);
-  if (NS_FAILED(ec) || originalVal < 0) {
+  nsContentUtils::ParseHTMLIntegerResultFlags result;
+  int32_t originalVal = nsContentUtils::ParseHTMLInteger(aString, &result);
+  if ((result & nsContentUtils::eParseHTMLInteger_Error) || originalVal < 0) {
     return false;
   }
 
-  SetIntValueAndType(originalVal, eInteger, strict ? nullptr : &aString);
+  bool nonStrict = (result & nsContentUtils::eParseHTMLInteger_IsPercent) ||
+                   (result & nsContentUtils::eParseHTMLInteger_NonStandard) ||
+                   (result & nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput);
+
+  SetIntValueAndType(originalVal, eInteger, nonStrict ? &aString : nullptr);
 
   return true;
 }
 
 bool
 nsAttrValue::ParsePositiveIntValue(const nsAString& aString)
 {
   ResetIfSet();
 
-  nsresult ec;
-  bool strict;
-  int32_t originalVal = StringToInteger(aString, &strict, &ec);
-  if (NS_FAILED(ec) || originalVal <= 0) {
+  nsContentUtils::ParseHTMLIntegerResultFlags result;
+  int32_t originalVal = nsContentUtils::ParseHTMLInteger(aString, &result);
+  if ((result & nsContentUtils::eParseHTMLInteger_Error) || originalVal <= 0) {
     return false;
   }
 
-  SetIntValueAndType(originalVal, eInteger, strict ? nullptr : &aString);
+  bool nonStrict = (result & nsContentUtils::eParseHTMLInteger_IsPercent) ||
+                   (result & nsContentUtils::eParseHTMLInteger_NonStandard) ||
+                   (result & nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput);
+
+  SetIntValueAndType(originalVal, eInteger, nonStrict ? &aString : nullptr);
 
   return true;
 }
 
 void
 nsAttrValue::SetColorValue(nscolor aColor, const nsAString& aString)
 {
   nsStringBuffer* buf = GetStringBuffer(aString).take();
@@ -1873,88 +1881,16 @@ nsAttrValue::GetStringBuffer(const nsASt
     return nullptr;
   }
   char16_t *data = static_cast<char16_t*>(buf->Data());
   CopyUnicodeTo(aValue, 0, data, len);
   data[len] = char16_t(0);
   return buf.forget();
 }
 
-int32_t
-nsAttrValue::StringToInteger(const nsAString& aValue, bool* aStrict,
-                             nsresult* aErrorCode,
-                             bool aCanBePercent,
-                             bool* aIsPercent) const
-{
-  *aStrict = true;
-  *aErrorCode = NS_ERROR_ILLEGAL_VALUE;
-  if (aCanBePercent) {
-    *aIsPercent = false;
-  }
-
-  nsAString::const_iterator iter, end;
-  aValue.BeginReading(iter);
-  aValue.EndReading(end);
-
-  while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) {
-    *aStrict = false;
-    ++iter;
-  }
-
-  if (iter == end) {
-    return 0;
-  }
-
-  bool negate = false;
-  if (*iter == char16_t('-')) {
-    negate = true;
-    ++iter;
-  } else if (*iter == char16_t('+')) {
-    *aStrict = false;
-    ++iter;
-  }
-
-  int32_t value = 0;
-  int32_t pValue = 0; // Previous value, used to check integer overflow
-  while (iter != end) {
-    if (*iter >= char16_t('0') && *iter <= char16_t('9')) {
-      value = (value * 10) + (*iter - char16_t('0'));
-      ++iter;
-      // Checking for integer overflow.
-      if (pValue > value) {
-        *aStrict = false;
-        *aErrorCode = NS_ERROR_ILLEGAL_VALUE;
-        break;
-      } else {
-        pValue = value;
-        *aErrorCode = NS_OK;
-      }
-    } else if (aCanBePercent && *iter == char16_t('%')) {
-      ++iter;
-      *aIsPercent = true;
-      if (iter != end) {
-        *aStrict = false;
-        break;
-      }
-    } else {
-      *aStrict = false;
-      break;
-    }
-  }
-  if (negate) {
-    value = -value;
-    // Checking the special case of -0.
-    if (!value) {
-      *aStrict = false;
-    }
-  }
-
-  return value;
-}
-
 size_t
 nsAttrValue::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
 {
   size_t n = 0;
 
   switch (BaseType()) {
     case eStringBase:
     {
--- a/content/base/src/nsAttrValue.h
+++ b/content/base/src/nsAttrValue.h
@@ -420,23 +420,16 @@ private:
   // existing container.
   MiscContainer* ClearMiscContainer();
   // Like ClearMiscContainer, except allocates a new container if one does not
   // exist already.
   MiscContainer* EnsureEmptyMiscContainer();
   bool EnsureEmptyAtomArray();
   already_AddRefed<nsStringBuffer>
     GetStringBuffer(const nsAString& aValue) const;
-  // aStrict is set true if stringifying the return value equals with
-  // aValue.
-  int32_t StringToInteger(const nsAString& aValue,
-                          bool* aStrict,
-                          nsresult* aErrorCode,
-                          bool aCanBePercent = false,
-                          bool* aIsPercent = nullptr) const;
   // Given an enum table and a particular entry in that table, return
   // the actual integer value we should store.
   int32_t EnumTableEntryToValue(const EnumTable* aEnumTable,
                                 const EnumTable* aTableEntry);  
 
   static nsTArray<const EnumTable*>* sEnumTableArray;
 
   uintptr_t mBits;
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -873,16 +873,91 @@ nsContentUtils::InternalSerializeAutocom
       return eAutocompleteAttrState_Valid;
     }
     --index;
   }
 
   return eAutocompleteAttrState_Invalid;
 }
 
+// Parse an integer according to HTML spec
+int32_t
+nsContentUtils::ParseHTMLInteger(const nsAString& aValue,
+                                 ParseHTMLIntegerResultFlags *aResult)
+{
+  int result = eParseHTMLInteger_NoFlags;
+
+  nsAString::const_iterator iter, end;
+  aValue.BeginReading(iter);
+  aValue.EndReading(end);
+
+  while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) {
+    result |= eParseHTMLInteger_NonStandard;
+    ++iter;
+  }
+
+  if (iter == end) {
+    result |= eParseHTMLInteger_Error | eParseHTMLInteger_ErrorNoValue;
+    *aResult = (ParseHTMLIntegerResultFlags)result;
+    return 0;
+  }
+
+  bool negate = false;
+  if (*iter == char16_t('-')) {
+    negate = true;
+    ++iter;
+  } else if (*iter == char16_t('+')) {
+    result |= eParseHTMLInteger_NonStandard;
+    ++iter;
+  }
+
+  bool foundValue = false;
+  int32_t value = 0;
+  int32_t pValue = 0; // Previous value, used to check integer overflow
+  while (iter != end) {
+    if (*iter >= char16_t('0') && *iter <= char16_t('9')) {
+      value = (value * 10) + (*iter - char16_t('0'));
+      ++iter;
+      // Checking for integer overflow.
+      if (pValue > value) {
+        result |= eParseHTMLInteger_Error | eParseHTMLInteger_ErrorOverflow;
+        break;
+      } else {
+        foundValue = true;
+        pValue = value;
+      }
+    } else if (*iter == char16_t('%')) {
+      ++iter;
+      result |= eParseHTMLInteger_IsPercent;
+      break;
+    } else {
+      break;
+    }
+  }
+
+  if (!foundValue) {
+    result |= eParseHTMLInteger_Error | eParseHTMLInteger_ErrorNoValue;
+  }
+
+  if (negate) {
+    value = -value;
+    // Checking the special case of -0.
+    if (!value) {
+      result |= eParseHTMLInteger_NonStandard;
+    }
+  }
+
+  if (iter != end) {
+    result |= eParseHTMLInteger_DidNotConsumeAllInput;
+  }
+
+  *aResult = (ParseHTMLIntegerResultFlags)result;
+  return value;
+}
+
 #define SKIP_WHITESPACE(iter, end_iter, end_res)                 \
   while ((iter) != (end_iter) && nsCRT::IsAsciiSpace(*(iter))) { \
     ++(iter);                                                    \
   }                                                              \
   if ((iter) == (end_iter)) {                                    \
     return (end_res);                                            \
   }
 
@@ -6318,17 +6393,17 @@ nsContentUtils::GetContentSecurityPolicy
 // static
 bool
 nsContentUtils::IsPatternMatching(nsAString& aValue, nsAString& aPattern,
                                   nsIDocument* aDocument)
 {
   NS_ASSERTION(aDocument, "aDocument should be a valid pointer (not null)");
 
   AutoJSAPI jsapi;
-  if (NS_WARN_IF(!jsapi.InitUsingWin(aDocument->GetWindow()))) {
+  if (NS_WARN_IF(!jsapi.Init(aDocument->GetWindow()))) {
     return true;
   }
   JSContext* cx = jsapi.cx();
 
   // The pattern has to match the entire value.
   aPattern.Insert(NS_LITERAL_STRING("^(?:"), 0);
   aPattern.AppendLiteral(")$");
 
--- a/content/base/src/nsDOMDataChannel.cpp
+++ b/content/base/src/nsDOMDataChannel.cpp
@@ -379,17 +379,17 @@ nsDOMDataChannel::DoOnMessageAvailable(c
   LOG(("DoOnMessageAvailable%s\n",aBinary ? ((mBinaryType == DC_BINARY_TYPE_BLOB) ? " (blob)" : " (binary)") : ""));
 
   nsresult rv = CheckInnerWindowCorrectness();
   if (NS_FAILED(rv)) {
     return NS_OK;
   }
 
   AutoJSAPI jsapi;
-  if (NS_WARN_IF(!jsapi.InitUsingWin(GetOwner()))) {
+  if (NS_WARN_IF(!jsapi.Init(GetOwner()))) {
     return NS_ERROR_FAILURE;
   }
   JSContext* cx = jsapi.cx();
 
   JS::Rooted<JS::Value> jsData(cx);
 
   if (aBinary) {
     if (mBinaryType == DC_BINARY_TYPE_BLOB) {
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -2215,16 +2215,17 @@ GK_ATOM(marginBottom, "margin-bottom")
 GK_ATOM(marginLeft, "margin-left")
 GK_ATOM(marginRight, "margin-right")
 GK_ATOM(marginTop, "margin-top")
 GK_ATOM(menuitemcheckbox, "menuitemcheckbox")
 GK_ATOM(menuitemradio, "menuitemradio")
 GK_ATOM(mixed, "mixed")
 GK_ATOM(multiline, "multiline")
 GK_ATOM(password, "password")
+GK_ATOM(picture, "picture")
 GK_ATOM(posinset, "posinset")
 GK_ATOM(presentation, "presentation")
 GK_ATOM(progressbar, "progressbar")
 GK_ATOM(region, "region")
 GK_ATOM(rowgroup, "rowgroup")
 GK_ATOM(rowheader, "rowheader")
 GK_ATOM(select1, "select1")
 GK_ATOM(setsize, "setsize")
--- a/content/html/content/src/HTMLImageElement.cpp
+++ b/content/html/content/src/HTMLImageElement.cpp
@@ -21,16 +21,20 @@
 #include "nsContainerFrame.h"
 #include "nsNodeInfoManager.h"
 #include "mozilla/MouseEvents.h"
 #include "nsContentPolicyUtils.h"
 #include "nsIDOMWindow.h"
 #include "nsFocusManager.h"
 #include "mozilla/dom/HTMLFormElement.h"
 
+// Responsive images!
+#include "mozilla/dom/HTMLSourceElement.h"
+#include "mozilla/dom/ResponsiveImageSelector.h"
+
 #include "imgIContainer.h"
 #include "imgILoader.h"
 #include "imgINotificationObserver.h"
 #include "imgRequestProxy.h"
 
 #include "nsILoadGroup.h"
 
 #include "nsRuleData.h"
@@ -41,16 +45,31 @@
 
 #include "nsLayoutUtils.h"
 
 #include "mozilla/Preferences.h"
 static const char *kPrefSrcsetEnabled = "dom.image.srcset.enabled";
 
 NS_IMPL_NS_NEW_HTML_ELEMENT(Image)
 
+// Is aSubject a previous sibling of aNode.
+static bool IsPreviousSibling(nsINode *aSubject, nsINode *aNode)
+{
+  if (aSubject == aNode) {
+    return false;
+  }
+
+  nsINode *parent = aSubject->GetParentNode();
+  if (parent && parent == aNode->GetParentNode()) {
+    return parent->IndexOf(aSubject) < parent->IndexOf(aNode);
+  }
+
+  return false;
+}
+
 namespace mozilla {
 namespace dom {
 
 HTMLImageElement::HTMLImageElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
   : nsGenericHTMLElement(aNodeInfo)
   , mForm(nullptr)
 {
   // We start out broken
@@ -82,16 +101,17 @@ NS_IMPL_ELEMENT_CLONE(HTMLImageElement)
 
 NS_IMPL_STRING_ATTR(HTMLImageElement, Name, name)
 NS_IMPL_STRING_ATTR(HTMLImageElement, Align, align)
 NS_IMPL_STRING_ATTR(HTMLImageElement, Alt, alt)
 NS_IMPL_STRING_ATTR(HTMLImageElement, Border, border)
 NS_IMPL_INT_ATTR(HTMLImageElement, Hspace, hspace)
 NS_IMPL_BOOL_ATTR(HTMLImageElement, IsMap, ismap)
 NS_IMPL_URI_ATTR(HTMLImageElement, LongDesc, longdesc)
+NS_IMPL_STRING_ATTR(HTMLImageElement, Sizes, sizes)
 NS_IMPL_STRING_ATTR(HTMLImageElement, Lowsrc, lowsrc)
 NS_IMPL_URI_ATTR(HTMLImageElement, Src, src)
 NS_IMPL_STRING_ATTR(HTMLImageElement, Srcset, srcset)
 NS_IMPL_STRING_ATTR(HTMLImageElement, UseMap, usemap)
 NS_IMPL_INT_ATTR(HTMLImageElement, Vspace, vspace)
 
 bool
 HTMLImageElement::IsSrcsetEnabled()
@@ -345,16 +365,18 @@ HTMLImageElement::AfterSetAttr(int32_t a
       "Expected atom value for name/id");
     mForm->AddImageElementToTable(this,
       nsDependentAtomString(aValue->GetAtomValue()));
   }
 
   // Handle src/srcset/crossorigin updates. If aNotify is false, we are coming
   // from the parser or some such place; we'll get bound after all the
   // attributes have been set, so we'll do the image load from BindToTree.
+
+  nsCOMPtr<nsIContent> thisContent = AsContent();
   if (aName == nsGkAtoms::src &&
       aNameSpaceID == kNameSpaceID_None) {
     // SetAttr handles setting src in the non-responsive case, so only handle it
     // for responsive mode or unsetting
     if (!aValue) {
       CancelImageRequests(aNotify);
     } else if (mResponsiveSelector) {
       mResponsiveSelector->SetDefaultSource(aValue ? aValue->GetStringValue()
@@ -362,18 +384,24 @@ HTMLImageElement::AfterSetAttr(int32_t a
       LoadSelectedImage(false, aNotify);
     }
   } else if (aName == nsGkAtoms::srcset &&
              aNameSpaceID == kNameSpaceID_None &&
              aNotify &&
              AsContent()->IsInDoc() &&
              IsSrcsetEnabled()) {
     // We currently don't handle responsive mode until BindToTree
-    UpdateSourceSet(aValue->GetStringValue());
-    LoadSelectedImage(false, aNotify);
+    PictureSourceSrcsetChanged(thisContent,
+                               aValue ? aValue->GetStringValue() : EmptyString(),
+                               aNotify);
+  } else if (aName == nsGkAtoms::sizes &&
+             aNameSpaceID == kNameSpaceID_None &&
+             thisContent->IsInDoc() &&
+             HTMLPictureElement::IsPictureEnabled()) {
+    PictureSourceSizesChanged(thisContent, aValue->GetStringValue(), aNotify);
   } else if (aName == nsGkAtoms::crossorigin &&
              aNameSpaceID == kNameSpaceID_None &&
              aNotify) {
     // We want aForce == true in this LoadImage call, because we want to force
     // a new load of the image with the new cross origin policy.
     nsCOMPtr<nsIURI> currentURI;
     if (NS_SUCCEEDED(GetCurrentURI(getter_AddRefs(currentURI))) && currentURI) {
       LoadImage(currentURI, true, aNotify);
@@ -496,35 +524,31 @@ HTMLImageElement::BindToTree(nsIDocument
 
   nsImageLoadingContent::BindToTree(aDocument, aParent, aBindingParent,
                                     aCompileEventHandlers);
 
   if (aParent) {
     UpdateFormOwner();
   }
 
+  bool addedToPicture = aParent && aParent->Tag() == nsGkAtoms::picture &&
+                        HTMLPictureElement::IsPictureEnabled();
   bool haveSrcset = IsSrcsetEnabled() &&
                     HasAttr(kNameSpaceID_None, nsGkAtoms::srcset);
-  if (haveSrcset || HasAttr(kNameSpaceID_None, nsGkAtoms::src)) {
+  if (addedToPicture || haveSrcset ||
+      HasAttr(kNameSpaceID_None, nsGkAtoms::src)) {
     // FIXME: Bug 660963 it would be nice if we could just have
     // ClearBrokenState update our state and do it fast...
     ClearBrokenState();
     RemoveStatesSilently(NS_EVENT_STATE_BROKEN);
 
     // We don't handle responsive changes when not bound to a tree, update them
     // now if necessary
-    if (haveSrcset) {
-      nsAutoString srcset;
-      GetAttr(kNameSpaceID_None, nsGkAtoms::srcset, srcset);
-      UpdateSourceSet(srcset);
-      if (mResponsiveSelector) {
-        nsAutoString src;
-        GetAttr(kNameSpaceID_None, nsGkAtoms::src, src);
-        mResponsiveSelector->SetDefaultSource(src);
-      }
+    if (addedToPicture || haveSrcset) {
+      MaybeUpdateResponsiveSelector();
     }
 
     // If loading is temporarily disabled, don't even launch MaybeLoadImage.
     // Otherwise MaybeLoadImage may run later when someone has reenabled
     // loading.
     if (LoadingEnabled()) {
       nsContentUtils::AddScriptRunner(
         NS_NewRunnableMethod(this, &HTMLImageElement::MaybeLoadImage));
@@ -791,41 +815,228 @@ HTMLImageElement::LoadSelectedImage(bool
       }
     }
   }
 
   return rv;
 }
 
 void
+HTMLImageElement::PictureSourceSrcsetChanged(nsIContent *aSourceNode,
+                                             const nsAString& aNewValue,
+                                             bool aNotify)
+{
+  if (aSourceNode != AsContent() && !HTMLPictureElement::IsPictureEnabled()) {
+    // Don't consider <source> nodes if picture is pref'd off
+    return;
+  }
+
+  nsIContent *currentSrc = mResponsiveSelector ? mResponsiveSelector->Content()
+                                               : nullptr;
+
+  if (aSourceNode == currentSrc) {
+    // We're currently using this node as our responsive selector source.
+    mResponsiveSelector->SetCandidatesFromSourceSet(aNewValue);
+    // Search for a new source if we are no longer valid.
+    MaybeUpdateResponsiveSelector(currentSrc);
+
+    LoadSelectedImage(false, aNotify);
+  } else if (currentSrc && IsPreviousSibling(currentSrc, aSourceNode)) {
+      // If we have a source and it is previous to the one being updated, ignore
+      return;
+  } else {
+    // This is previous to our current source or we don't have a current source,
+    // use it if valid.
+    if (TryCreateResponsiveSelector(aSourceNode, &aNewValue, nullptr)) {
+      LoadSelectedImage(false, aNotify);
+    }
+  }
+}
+
+void
+HTMLImageElement::PictureSourceSizesChanged(nsIContent *aSourceNode,
+                                            const nsAString& aNewValue,
+                                            bool aNotify)
+{
+  if (!HTMLPictureElement::IsPictureEnabled()) {
+    // Don't consider sizes at all if picture support is disabled
+    return;
+  }
+
+  nsIContent *currentSrc = mResponsiveSelector ? mResponsiveSelector->Content()
+                                               : nullptr;
+
+  if (aSourceNode == currentSrc) {
+    // We're currently using this node as our responsive selector source.
+    mResponsiveSelector->SetSizesFromDescriptor(aNewValue);
+    LoadSelectedImage(false, aNotify);
+  }
+}
+
+void
+HTMLImageElement::PictureSourceAdded(nsIContent *aSourceNode)
+{
+  // If the source node is previous to our current one, or ourselves if we have
+  // no responsive source, try to use it as a responsive source.
+  nsIContent *currentSrc = mResponsiveSelector ? mResponsiveSelector->Content()
+                                               : AsContent();
+
+  if (HTMLPictureElement::IsPictureEnabled() &&
+      IsPreviousSibling(aSourceNode, currentSrc) &&
+      TryCreateResponsiveSelector(aSourceNode, nullptr, nullptr)) {
+    LoadSelectedImage(false, true);
+  }
+}
+
+void
+HTMLImageElement::PictureSourceRemoved(nsIContent *aSourceNode)
+{
+  // If this is our current source, we'll need to find another one or leave
+  // responsive mode.
+  if (mResponsiveSelector && mResponsiveSelector->Content() == aSourceNode) {
+    MaybeUpdateResponsiveSelector(aSourceNode, true);
+    LoadSelectedImage(false, true);
+  }
+}
+
+bool
+HTMLImageElement::MaybeUpdateResponsiveSelector(nsIContent *aCurrentSource,
+                                                bool aSourceRemoved)
+{
+  nsIContent *thisContent = AsContent();
+
+  if (!aCurrentSource && mResponsiveSelector) {
+    aCurrentSource = mResponsiveSelector->Content();
+  }
+
+  // If we have a source with candidates, no update is needed unless it is being
+  // removed
+  if (aCurrentSource && !aSourceRemoved &&
+      mResponsiveSelector->NumCandidates()) {
+    return false;
+  }
+
+  // Otherwise, invalidate
+  bool hadSelector = !!mResponsiveSelector;
+  mResponsiveSelector = nullptr;
+
+  if (!IsSrcsetEnabled()) {
+    return hadSelector;
+  }
+
+  // See if there's another source node we could use.
+  bool pictureEnabled = HTMLPictureElement::IsPictureEnabled();
+  nsIContent *nextSource = nullptr;
+  if (pictureEnabled && aCurrentSource && aCurrentSource != thisContent) {
+    // If current source is the <img> tag, there is no next candidate. Otherwise,
+    // it's the next sibling of the current source.
+    MOZ_ASSERT(IsPreviousSibling(aCurrentSource, thisContent) &&
+               thisContent->GetParentNode()->Tag() == nsGkAtoms::picture);
+    nextSource = aCurrentSource->GetNextSibling();
+  } else if (!aCurrentSource) {
+    // If no current source at all, start from the first possible source, which
+    // is the first node of the <picture> element or ourselves if we're not a
+    // picture
+    nsINode *parent = pictureEnabled ? thisContent->GetParentNode() : nullptr;
+    if (parent && parent->Tag() == nsGkAtoms::picture) {
+      nextSource = parent->GetFirstChild();
+    } else {
+      nextSource = thisContent;
+    }
+  }
+
+  while (nextSource) {
+    if (nextSource == thisContent) {
+      // We are the last possible source, so stop searching if we match or
+      // not
+      TryCreateResponsiveSelector(nextSource);
+      break;
+    } else if (nextSource->Tag() == nsGkAtoms::source &&
+               TryCreateResponsiveSelector(nextSource)) {
+      // If this led to a valid source, stop
+      break;
+    }
+
+    nextSource = nextSource->GetNextSibling();
+  }
+
+  // State changed unless we didn't make a selector and didn't start with one
+  return mResponsiveSelector || hadSelector;
+}
+
+bool
+HTMLImageElement::TryCreateResponsiveSelector(nsIContent *aSourceNode,
+                                              const nsAString *aSrcset,
+                                              const nsAString *aSizes)
+{
+  if (!IsSrcsetEnabled()) {
+    return false;
+  }
+
+  bool pictureEnabled = HTMLPictureElement::IsPictureEnabled();
+  // Skip if this is not a <source> with matching media query
+  bool isSourceTag = aSourceNode->Tag() == nsGkAtoms::source;
+  if (isSourceTag) {
+    DebugOnly<nsINode *> parent(nsINode::GetParentNode());
+    MOZ_ASSERT(parent && parent->Tag() == nsGkAtoms::picture);
+    MOZ_ASSERT(IsPreviousSibling(aSourceNode, AsContent()));
+    MOZ_ASSERT(pictureEnabled);
+
+    HTMLSourceElement *src = static_cast<HTMLSourceElement*>(aSourceNode);
+    if (!src->MatchesCurrentMedia()) {
+      return false;
+    }
+  } else if (aSourceNode->Tag() == nsGkAtoms::img) {
+    // Otherwise this is the <img> tag itself
+    MOZ_ASSERT(aSourceNode == AsContent());
+  }
+
+  // Skip if has no srcset or an empty srcset
+  nsString srcset;
+  if (aSrcset) {
+    srcset = *aSrcset;
+  } else if (!aSourceNode->GetAttr(kNameSpaceID_None, nsGkAtoms::srcset,
+                                   srcset)) {
+    return false;
+  }
+
+  if (srcset.IsEmpty()) {
+    return false;
+  }
+
+
+  // Try to parse
+  nsRefPtr<ResponsiveImageSelector> sel = new ResponsiveImageSelector(this);
+  if (!sel->SetCandidatesFromSourceSet(srcset)) {
+    // No possible candidates, don't need to bother parsing sizes
+    return false;
+  }
+
+  if (pictureEnabled && aSizes) {
+    sel->SetSizesFromDescriptor(*aSizes);
+  } else if (pictureEnabled) {
+    nsAutoString sizes;
+    aSourceNode->GetAttr(kNameSpaceID_None, nsGkAtoms::sizes, sizes);
+    sel->SetSizesFromDescriptor(sizes);
+  }
+
+  // If this is the <img> tag, also pull in src as the default source
+  if (!isSourceTag) {
+    MOZ_ASSERT(aSourceNode == AsContent());
+    nsAutoString src;
+    if (GetAttr(kNameSpaceID_None, nsGkAtoms::src, src) && !src.IsEmpty()) {
+      sel->SetDefaultSource(src);
+    }
+  }
+
+  mResponsiveSelector = sel;
+  return true;
+}
+
+void
 HTMLImageElement::DestroyContent()
 {
   mResponsiveSelector = nullptr;
 }
 
-void
-HTMLImageElement::UpdateSourceSet(const nsAString & aSrcset)
-{
-  MOZ_ASSERT(IsSrcsetEnabled());
-
-  bool haveSrcset = !aSrcset.IsEmpty();
-
-  if (haveSrcset && !mResponsiveSelector) {
-    mResponsiveSelector = new ResponsiveImageSelector(this);
-    mResponsiveSelector->SetCandidatesFromSourceSet(aSrcset);
-
-    // src may have been set before we decided we were responsive
-    nsAutoString src;
-    if (GetAttr(kNameSpaceID_None, nsGkAtoms::src, src) && src.Length()) {
-      mResponsiveSelector->SetDefaultSource(src);
-    }
-
-  } else if (haveSrcset) {
-    mResponsiveSelector->SetCandidatesFromSourceSet(aSrcset);
-  } else if (mResponsiveSelector) {
-    // Clearing srcset, don't need responsive selector anymore
-    mResponsiveSelector = nullptr;
-  }
-}
-
 } // namespace dom
 } // namespace mozilla
 
--- a/content/html/content/src/HTMLImageElement.h
+++ b/content/html/content/src/HTMLImageElement.h
@@ -7,26 +7,30 @@
 #define mozilla_dom_HTMLImageElement_h
 
 #include "mozilla/Attributes.h"
 #include "nsGenericHTMLElement.h"
 #include "nsImageLoadingContent.h"
 #include "nsIDOMHTMLImageElement.h"
 #include "imgRequestProxy.h"
 #include "Units.h"
-#include "mozilla/dom/ResponsiveImageSelector.h"
+
+// Only needed for IsPictureEnabled()
+#include "mozilla/dom/HTMLPictureElement.h"
 
 namespace mozilla {
 class EventChainPreVisitor;
 namespace dom {
 
+class ResponsiveImageSelector;
 class HTMLImageElement MOZ_FINAL : public nsGenericHTMLElement,
                                    public nsImageLoadingContent,
                                    public nsIDOMHTMLImageElement
 {
+  friend class HTMLSourceElement;
 public:
   explicit HTMLImageElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
   virtual ~HTMLImageElement();
 
   static already_AddRefed<HTMLImageElement>
     Image(const GlobalObject& aGlobal,
           const Optional<uint32_t>& aWidth,
           const Optional<uint32_t>& aHeight,
@@ -154,16 +158,20 @@ public:
   void SetAlign(const nsAString& aAlign, ErrorResult& aError)
   {
     SetHTMLAttr(nsGkAtoms::align, aAlign, aError);
   }
   void SetLongDesc(const nsAString& aLongDesc, ErrorResult& aError)
   {
     SetHTMLAttr(nsGkAtoms::longdesc, aLongDesc, aError);
   }
+  void SetSizes(const nsAString& aSizes, ErrorResult& aError)
+  {
+    SetHTMLAttr(nsGkAtoms::sizes, aSizes, aError);
+  }
   void SetBorder(const nsAString& aBorder, ErrorResult& aError)
   {
     SetHTMLAttr(nsGkAtoms::border, aBorder, aError);
   }
 
   int32_t X();
   int32_t Y();
   // Uses XPCOM GetLowsrc.
@@ -181,17 +189,37 @@ public:
   virtual void DestroyContent() MOZ_OVERRIDE;
 
 protected:
   // Resolve and load the current mResponsiveSelector (responsive mode) or src
   // attr image.
   nsresult LoadSelectedImage(bool aForce, bool aNotify);
 
   // Update/create/destroy mResponsiveSelector
-  void UpdateSourceSet(const nsAString & aSrcset);
+  void PictureSourceSrcsetChanged(nsIContent *aSourceNode,
+                                  const nsAString& aNewValue, bool aNotify);
+  void PictureSourceSizesChanged(nsIContent *aSourceNode,
+                                 const nsAString& aNewValue, bool aNotify);
+
+  void PictureSourceAdded(nsIContent *aSourceNode);
+  // This should be called prior to the unbind, such that nextsibling works
+  void PictureSourceRemoved(nsIContent *aSourceNode);
+
+  bool MaybeUpdateResponsiveSelector(nsIContent *aCurrentSource = nullptr,
+                                     bool aSourceRemoved = false);
+
+  // Given a <source> node that is a previous sibling *or* ourselves, try to
+  // create a ResponsiveSelector.
+
+  // If the node's srcset/sizes make for an invalid selector, returns
+  // false. This does not guarantee the resulting selector matches an image,
+  // only that it is valid.
+  bool TryCreateResponsiveSelector(nsIContent *aSourceNode,
+                                   const nsAString *aSrcset = nullptr,
+                                   const nsAString *aSizes = nullptr);
 
   CSSIntPoint GetXY();
   virtual void GetItemValueText(nsAString& text) MOZ_OVERRIDE;
   virtual void SetItemValueText(const nsAString& text) MOZ_OVERRIDE;
   virtual JSObject* WrapNode(JSContext *aCx) MOZ_OVERRIDE;
   void UpdateFormOwner();
 
   virtual nsresult BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
--- a/content/html/content/src/HTMLMediaElement.cpp
+++ b/content/html/content/src/HTMLMediaElement.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/HTMLMediaElement.h"
 #include "mozilla/dom/HTMLMediaElementBinding.h"
+#include "mozilla/dom/HTMLSourceElement.h"
 #include "mozilla/dom/ElementInlines.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/dom/MediaKeyNeededEvent.h"
 #include "mozilla/AsyncEventDispatcher.h"
 
 #include "base/basictypes.h"
 #include "nsIDOMHTMLMediaElement.h"
@@ -67,18 +68,16 @@
 #include "nsIScriptError.h"
 #include "nsHostObjectProtocolHandler.h"
 #include "mozilla/dom/MediaSource.h"
 #include "MediaMetadataManager.h"
 #include "MediaSourceDecoder.h"
 
 #include "AudioChannelService.h"
 
-#include "nsCSSParser.h"
-#include "nsIMediaList.h"
 #include "mozilla/dom/power/PowerManagerService.h"
 #include "mozilla/dom/WakeLock.h"
 
 #include "mozilla/dom/TextTrack.h"
 
 #include "ImageContainer.h"
 #include "nsRange.h"
 #include <algorithm>
@@ -899,27 +898,23 @@ void HTMLMediaElement::LoadFromSourceChi
       const char16_t* params[] = { type.get(), src.get() };
       ReportLoadError("MediaLoadUnsupportedTypeAttribute", params, ArrayLength(params));
       continue;
     }
     // TODO: "If candidate has a keySystem attribute whose value represents a
     //       Key System that the user agent knows it cannot use with type,
     //       then end the synchronous section[...]" (Bug 1016707)
     nsAutoString media;
-    if (child->GetAttr(kNameSpaceID_None, nsGkAtoms::media, media) && !media.IsEmpty()) {
-      nsCSSParser cssParser;
-      nsRefPtr<nsMediaList> mediaList(new nsMediaList());
-      cssParser.ParseMediaList(media, nullptr, 0, mediaList, false);
-      nsIPresShell* presShell = OwnerDoc()->GetShell();
-      if (presShell && !mediaList->Matches(presShell->GetPresContext(), nullptr)) {
-        DispatchAsyncSourceError(child);
-        const char16_t* params[] = { media.get(), src.get() };
-        ReportLoadError("MediaLoadSourceMediaNotMatched", params, ArrayLength(params));
-        continue;
-      }
+    HTMLSourceElement *childSrc = HTMLSourceElement::FromContent(child);
+    MOZ_ASSERT(childSrc, "Expect child to be HTMLSourceElement");
+    if (childSrc && !childSrc->MatchesCurrentMedia()) {
+      DispatchAsyncSourceError(child);
+      const char16_t* params[] = { media.get(), src.get() };
+      ReportLoadError("MediaLoadSourceMediaNotMatched", params, ArrayLength(params));
+      continue;
     }
     LOG(PR_LOG_DEBUG, ("%p Trying load from <source>=%s type=%s media=%s", this,
       NS_ConvertUTF16toUTF8(src).get(), NS_ConvertUTF16toUTF8(type).get(),
       NS_ConvertUTF16toUTF8(media).get()));
 
     nsCOMPtr<nsIURI> uri;
     NewURIFromString(src, getter_AddRefs(uri));
     if (!uri) {
new file mode 100644
--- /dev/null
+++ b/content/html/content/src/HTMLPictureElement.cpp
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/HTMLPictureElement.h"
+#include "mozilla/dom/HTMLPictureElementBinding.h"
+#include "mozilla/dom/HTMLImageElement.h"
+
+#include "mozilla/Preferences.h"
+static const char *kPrefPictureEnabled = "dom.image.picture.enabled";
+
+// Expand NS_IMPL_NS_NEW_HTML_ELEMENT(Picture) to add pref check.
+nsGenericHTMLElement*
+NS_NewHTMLPictureElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
+                         mozilla::dom::FromParser aFromParser)
+{
+  if (!mozilla::dom::HTMLPictureElement::IsPictureEnabled()) {
+    return new mozilla::dom::HTMLUnknownElement(aNodeInfo);
+  }
+
+  return new mozilla::dom::HTMLPictureElement(aNodeInfo);
+}
+
+namespace mozilla {
+namespace dom {
+
+HTMLPictureElement::HTMLPictureElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
+  : nsGenericHTMLElement(aNodeInfo)
+{
+}
+
+HTMLPictureElement::~HTMLPictureElement()
+{
+}
+
+NS_IMPL_ISUPPORTS_INHERITED(HTMLPictureElement, nsGenericHTMLElement,
+                            nsIDOMHTMLPictureElement)
+
+NS_IMPL_ELEMENT_CLONE(HTMLPictureElement)
+
+bool
+HTMLPictureElement::IsPictureEnabled()
+{
+  return HTMLImageElement::IsSrcsetEnabled() &&
+         Preferences::GetBool(kPrefPictureEnabled, false);
+}
+
+JSObject*
+HTMLPictureElement::WrapNode(JSContext* aCx)
+{
+  return HTMLPictureElementBinding::Wrap(aCx, this);
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/html/content/src/HTMLPictureElement.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_HTMLPictureElement_h
+#define mozilla_dom_HTMLPictureElement_h
+
+#include "mozilla/Attributes.h"
+#include "nsIDOMHTMLPictureElement.h"
+#include "nsGenericHTMLElement.h"
+
+#include "mozilla/dom/HTMLUnknownElement.h"
+
+namespace mozilla {
+namespace dom {
+
+class HTMLPictureElement MOZ_FINAL : public nsGenericHTMLElement,
+                                    public nsIDOMHTMLPictureElement
+{
+public:
+  HTMLPictureElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
+  virtual ~HTMLPictureElement();
+
+  // nsISupports
+  NS_DECL_ISUPPORTS_INHERITED
+
+  // nsIDOMHTMLPictureElement
+  NS_DECL_NSIDOMHTMLPICTUREELEMENT
+
+  virtual nsresult Clone(mozilla::dom::NodeInfo* aNodeInfo, nsINode** aResult) const MOZ_OVERRIDE;
+
+  static bool IsPictureEnabled();
+
+protected:
+  virtual JSObject* WrapNode(JSContext* aCx) MOZ_OVERRIDE;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_HTMLPictureElement_h
--- a/content/html/content/src/HTMLSourceElement.cpp
+++ b/content/html/content/src/HTMLSourceElement.cpp
@@ -2,16 +2,26 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/HTMLSourceElement.h"
 #include "mozilla/dom/HTMLSourceElementBinding.h"
 
+#include "mozilla/dom/HTMLImageElement.h"
+#include "mozilla/dom/ResponsiveImageSelector.h"
+
+#include "nsGkAtoms.h"
+
+#include "nsIMediaList.h"
+#include "nsCSSParser.h"
+
+#include "mozilla/Preferences.h"
+
 NS_IMPL_NS_NEW_HTML_ELEMENT(Source)
 
 namespace mozilla {
 namespace dom {
 
 HTMLSourceElement::HTMLSourceElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
   : nsGenericHTMLElement(aNodeInfo)
 {
@@ -21,21 +31,76 @@ HTMLSourceElement::~HTMLSourceElement()
 {
 }
 
 NS_IMPL_ISUPPORTS_INHERITED(HTMLSourceElement, nsGenericHTMLElement,
                             nsIDOMHTMLSourceElement)
 
 NS_IMPL_ELEMENT_CLONE(HTMLSourceElement)
 
-
 NS_IMPL_URI_ATTR(HTMLSourceElement, Src, src)
 NS_IMPL_STRING_ATTR(HTMLSourceElement, Type, type)
+NS_IMPL_STRING_ATTR(HTMLSourceElement, Srcset, srcset)
+NS_IMPL_STRING_ATTR(HTMLSourceElement, Sizes, sizes)
 NS_IMPL_STRING_ATTR(HTMLSourceElement, Media, media)
 
+bool
+HTMLSourceElement::MatchesCurrentMedia()
+{
+  if (mMediaList) {
+    nsIPresShell* presShell = OwnerDoc()->GetShell();
+    nsPresContext* pctx = presShell ? presShell->GetPresContext() : nullptr;
+    return pctx && mMediaList->Matches(pctx, nullptr);
+  }
+
+  // No media specified
+  return true;
+}
+
+nsresult
+HTMLSourceElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
+                                const nsAttrValue* aValue, bool aNotify)
+{
+  // If we are associated with a <picture> with a valid <img>, notify it of
+  // responsive parameter changes
+  nsINode *parent = nsINode::GetParentNode();
+  if (aNameSpaceID == kNameSpaceID_None &&
+      (aName == nsGkAtoms::srcset || aName == nsGkAtoms::sizes) &&
+      parent && parent->Tag() == nsGkAtoms::picture && MatchesCurrentMedia()) {
+
+    nsString strVal = aValue ? aValue->GetStringValue() : EmptyString();
+    // Find all img siblings after this <source> and notify them of the change
+    nsCOMPtr<nsINode> sibling = AsContent();
+    while ( (sibling = sibling->GetNextSibling()) ) {
+      if (sibling->Tag() == nsGkAtoms::img) {
+        HTMLImageElement *img = static_cast<HTMLImageElement*>(sibling.get());
+        if (aName == nsGkAtoms::srcset) {
+          img->PictureSourceSrcsetChanged(AsContent(), strVal, aNotify);
+        } else if (aName == nsGkAtoms::sizes) {
+          img->PictureSourceSizesChanged(AsContent(), strVal, aNotify);
+        }
+      }
+    }
+
+  } else if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::media) {
+    mMediaList = nullptr;
+    if (aValue) {
+      nsString mediaStr = aValue->GetStringValue();
+      if (!mediaStr.IsEmpty()) {
+        nsCSSParser cssParser;
+        mMediaList = new nsMediaList();
+        cssParser.ParseMediaList(mediaStr, nullptr, 0, mMediaList, false);
+      }
+    }
+  }
+
+  return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName,
+                                            aValue, aNotify);
+}
+
 void
 HTMLSourceElement::GetItemValueText(nsAString& aValue)
 {
   GetSrc(aValue);
 }
 
 void
 HTMLSourceElement::SetItemValueText(const nsAString& aValue)
@@ -50,25 +115,51 @@ HTMLSourceElement::BindToTree(nsIDocumen
                               bool aCompileEventHandlers)
 {
   nsresult rv = nsGenericHTMLElement::BindToTree(aDocument,
                                                  aParent,
                                                  aBindingParent,
                                                  aCompileEventHandlers);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  if (!aParent || !aParent->IsNodeOfType(nsINode::eMEDIA))
-    return NS_OK;
-
-  HTMLMediaElement* media = static_cast<HTMLMediaElement*>(aParent);
-  media->NotifyAddedSource();
+  if (aParent && aParent->IsNodeOfType(nsINode::eMEDIA)) {
+    HTMLMediaElement* media = static_cast<HTMLMediaElement*>(aParent);
+    media->NotifyAddedSource();
+  } else if (aParent && aParent->Tag() == nsGkAtoms::picture) {
+    // Find any img siblings after this <source> and notify them
+    nsCOMPtr<nsINode> sibling = AsContent();
+    while ( (sibling = sibling->GetNextSibling()) ) {
+      if (sibling->Tag() == nsGkAtoms::img) {
+        HTMLImageElement *img = static_cast<HTMLImageElement*>(sibling.get());
+        img->PictureSourceAdded(AsContent());
+      }
+    }
+  }
 
   return NS_OK;
 }
 
+void
+HTMLSourceElement::UnbindFromTree(bool aDeep, bool aNullParent)
+{
+  nsINode *parent = nsINode::GetParentNode();
+  if (parent && parent->Tag() == nsGkAtoms::picture) {
+    // Find all img siblings after this <source> and notify them of our demise
+    nsCOMPtr<nsINode> sibling = AsContent();
+    while ( (sibling = sibling->GetNextSibling()) ) {
+      if (sibling->Tag() == nsGkAtoms::img) {
+        HTMLImageElement *img = static_cast<HTMLImageElement*>(sibling.get());
+        img->PictureSourceRemoved(AsContent());
+      }
+    }
+  }
+
+  nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
+}
+
 JSObject*
 HTMLSourceElement::WrapNode(JSContext* aCx)
 {
   return HTMLSourceElementBinding::Wrap(aCx, this);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/content/html/content/src/HTMLSourceElement.h
+++ b/content/html/content/src/HTMLSourceElement.h
@@ -7,39 +7,49 @@
 #ifndef mozilla_dom_HTMLSourceElement_h
 #define mozilla_dom_HTMLSourceElement_h
 
 #include "mozilla/Attributes.h"
 #include "nsIDOMHTMLSourceElement.h"
 #include "nsGenericHTMLElement.h"
 #include "mozilla/dom/HTMLMediaElement.h"
 
+class nsMediaList;
+
 namespace mozilla {
 namespace dom {
 
+class ResponsiveImageSelector;
 class HTMLSourceElement MOZ_FINAL : public nsGenericHTMLElement,
                                     public nsIDOMHTMLSourceElement
 {
 public:
   HTMLSourceElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
   virtual ~HTMLSourceElement();
 
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
+  NS_IMPL_FROMCONTENT_HTML_WITH_TAG(HTMLSourceElement, source)
+
   // nsIDOMHTMLSourceElement
   NS_DECL_NSIDOMHTMLSOURCEELEMENT
 
   virtual nsresult Clone(mozilla::dom::NodeInfo* aNodeInfo, nsINode** aResult) const MOZ_OVERRIDE;
 
   // Override BindToTree() so that we can trigger a load when we add a
   // child source element.
   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                               nsIContent* aBindingParent,
                               bool aCompileEventHandlers) MOZ_OVERRIDE;
+  virtual void UnbindFromTree(bool aDeep, bool aNullParent) MOZ_OVERRIDE;
+
+  // If this element's media attr matches for its owner document.  Returns true
+  // if no media attr was set.
+  bool MatchesCurrentMedia();
 
   // WebIDL
   void GetSrc(nsString& aSrc)
   {
     GetURIAttr(nsGkAtoms::src, nullptr, aSrc);
   }
   void SetSrc(const nsAString& aSrc, mozilla::ErrorResult& rv)
   {
@@ -50,16 +60,34 @@ public:
   {
     GetHTMLAttr(nsGkAtoms::type, aType);
   }
   void SetType(const nsAString& aType, ErrorResult& rv)
   {
     SetHTMLAttr(nsGkAtoms::type, aType, rv);
   }
 
+  void GetSrcset(nsString& aSrcset)
+  {
+    GetHTMLAttr(nsGkAtoms::srcset, aSrcset);
+  }
+  void SetSrcset(const nsAString& aSrcset, mozilla::ErrorResult& rv)
+  {
+    SetHTMLAttr(nsGkAtoms::srcset, aSrcset, rv);
+  }
+
+  void GetSizes(nsString& aSizes)
+  {
+    GetHTMLAttr(nsGkAtoms::sizes, aSizes);
+  }
+  void SetSizes(const nsAString& aSizes, mozilla::ErrorResult& rv)
+  {
+    SetHTMLAttr(nsGkAtoms::sizes, aSizes, rv);
+  }
+
   void GetMedia(nsString& aMedia)
   {
     GetHTMLAttr(nsGkAtoms::media, aMedia);
   }
   void SetMedia(const nsAString& aMedia, mozilla::ErrorResult& rv)
   {
     SetHTMLAttr(nsGkAtoms::media, aMedia, rv);
   }
@@ -75,14 +103,22 @@ public:
   }
 
 protected:
   virtual JSObject* WrapNode(JSContext* aCx) MOZ_OVERRIDE;
 
 protected:
   virtual void GetItemValueText(nsAString& text) MOZ_OVERRIDE;
   virtual void SetItemValueText(const nsAString& text) MOZ_OVERRIDE;
+
+  virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
+                                const nsAttrValue* aValue,
+                                bool aNotify) MOZ_OVERRIDE;
+
+
+private:
+  nsRefPtr<nsMediaList> mMediaList;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_HTMLSourceElement_h
--- a/content/html/content/src/moz.build
+++ b/content/html/content/src/moz.build
@@ -42,16 +42,17 @@ EXPORTS.mozilla.dom += [
     'HTMLMeterElement.h',
     'HTMLModElement.h',
     'HTMLObjectElement.h',
     'HTMLOptGroupElement.h',
     'HTMLOptionElement.h',
     'HTMLOptionsCollection.h',
     'HTMLOutputElement.h',
     'HTMLParagraphElement.h',
+    'HTMLPictureElement.h',
     'HTMLPreElement.h',
     'HTMLProgressElement.h',
     'HTMLScriptElement.h',
     'HTMLSelectElement.h',
     'HTMLShadowElement.h',
     'HTMLSharedElement.h',
     'HTMLSharedListElement.h',
     'HTMLSharedObjectElement.h',
@@ -113,16 +114,17 @@ UNIFIED_SOURCES += [
     'HTMLMeterElement.cpp',
     'HTMLModElement.cpp',
     'HTMLObjectElement.cpp',
     'HTMLOptGroupElement.cpp',
     'HTMLOptionElement.cpp',
     'HTMLOptionsCollection.cpp',
     'HTMLOutputElement.cpp',
     'HTMLParagraphElement.cpp',
+    'HTMLPictureElement.cpp',
     'HTMLPreElement.cpp',
     'HTMLProgressElement.cpp',
     'HTMLPropertiesCollection.cpp',
     'HTMLScriptElement.cpp',
     'HTMLSelectElement.cpp',
     'HTMLShadowElement.cpp',
     'HTMLSharedElement.cpp',
     'HTMLSharedListElement.cpp',
--- a/content/html/content/src/nsGenericHTMLElement.h
+++ b/content/html/content/src/nsGenericHTMLElement.h
@@ -1759,16 +1759,17 @@ NS_DECLARE_NS_NEW_HTML_ELEMENT(Menu)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(MenuItem)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Meta)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Meter)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Object)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(OptGroup)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Option)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Output)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Paragraph)
+NS_DECLARE_NS_NEW_HTML_ELEMENT(Picture)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Pre)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Progress)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Script)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Select)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Shadow)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Source)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Span)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Style)
--- a/content/media/webaudio/AudioDestinationNode.cpp
+++ b/content/media/webaudio/AudioDestinationNode.cpp
@@ -122,17 +122,17 @@ public:
   {
     AudioContext* context = aNode->Context();
     context->Shutdown();
     // Shutdown drops self reference, but the context is still referenced by aNode,
     // which is strongly referenced by the runnable that called
     // AudioDestinationNode::FireOfflineCompletionEvent.
 
     AutoJSAPI jsapi;
-    if (NS_WARN_IF(!jsapi.InitUsingWin(aNode->GetOwner()))) {
+    if (NS_WARN_IF(!jsapi.Init(aNode->GetOwner()))) {
       return;
     }
     JSContext* cx = jsapi.cx();
 
     // Create the input buffer
     ErrorResult rv;
     nsRefPtr<AudioBuffer> renderedBuffer =
       AudioBuffer::Create(context, mInputChannels.Length(),
--- a/content/media/webaudio/AudioProcessingEvent.cpp
+++ b/content/media/webaudio/AudioProcessingEvent.cpp
@@ -37,17 +37,17 @@ AudioProcessingEvent::WrapObject(JSConte
   return AudioProcessingEventBinding::Wrap(aCx, this);
 }
 
 already_AddRefed<AudioBuffer>
 AudioProcessingEvent::LazilyCreateBuffer(uint32_t aNumberOfChannels,
                                          ErrorResult& aRv)
 {
   AutoJSAPI jsapi;
-  if (NS_WARN_IF(!jsapi.InitUsingWin(mNode->GetOwner()))) {
+  if (NS_WARN_IF(!jsapi.Init(mNode->GetOwner()))) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
   JSContext* cx = jsapi.cx();
 
   nsRefPtr<AudioBuffer> buffer =
     AudioBuffer::Create(mNode->Context(), aNumberOfChannels,
                         mNode->BufferSize(),
--- a/content/media/webaudio/MediaBufferDecoder.cpp
+++ b/content/media/webaudio/MediaBufferDecoder.cpp
@@ -409,17 +409,17 @@ MediaDecodeTask::CallbackTheResult()
 
 bool
 WebAudioDecodeJob::AllocateBuffer()
 {
   MOZ_ASSERT(!mOutput);
   MOZ_ASSERT(NS_IsMainThread());
 
   AutoJSAPI jsapi;
-  if (NS_WARN_IF(!jsapi.InitUsingWin(mContext->GetOwner()))) {
+  if (NS_WARN_IF(!jsapi.Init(mContext->GetOwner()))) {
     return false;
   }
   JSContext* cx = jsapi.cx();
 
   // Now create the AudioBuffer
   ErrorResult rv;
   mOutput = AudioBuffer::Create(mContext, mChannelBuffers.Length(),
                                 mWriteIndex, mContext->SampleRate(), cx, rv);
--- a/content/media/webaudio/ScriptProcessorNode.cpp
+++ b/content/media/webaudio/ScriptProcessorNode.cpp
@@ -398,17 +398,17 @@ private:
           MutexAutoLock lock(mStream->Engine()->NodeMutex());
           node = static_cast<ScriptProcessorNode*>(mStream->Engine()->Node());
         }
         if (!node || !node->Context()) {
           return NS_OK;
         }
 
         AutoJSAPI jsapi;
-        if (NS_WARN_IF(!jsapi.InitUsingWin(node->GetOwner()))) {
+        if (NS_WARN_IF(!jsapi.Init(node->GetOwner()))) {
           return NS_OK;
         }
         JSContext* cx = jsapi.cx();
 
         // Create the input buffer
         nsRefPtr<AudioBuffer> inputBuffer;
         if (!mNullInput) {
           ErrorResult rv;
--- a/dom/archivereader/ArchiveRequest.cpp
+++ b/dom/archivereader/ArchiveRequest.cpp
@@ -127,17 +127,17 @@ ArchiveRequest::ReaderReady(nsTArray<nsC
   if (NS_FAILED(aStatus)) {
     FireError(aStatus);
     return NS_OK;
   }
 
   nsresult rv;
 
   AutoJSAPI jsapi;
-  if (NS_WARN_IF(!jsapi.InitUsingWin(GetOwner()))) {
+  if (NS_WARN_IF(!jsapi.Init(GetOwner()))) {
     return NS_ERROR_UNEXPECTED;
   }
   JSContext* cx = jsapi.cx();
 
   JS::Rooted<JS::Value> result(cx);
   switch (mOperation) {
     case GetFilenames:
       rv = GetFilenamesResult(cx, result.address(), aFileList);
--- a/dom/base/ScriptSettings.cpp
+++ b/dom/base/ScriptSettings.cpp
@@ -289,33 +289,51 @@ bool
 AutoJSAPI::InitWithLegacyErrorReporting(nsIGlobalObject* aGlobalObject)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   return Init(aGlobalObject, FindJSContext(aGlobalObject));
 }
 
 bool
-AutoJSAPI::InitUsingWin(nsPIDOMWindow* aWindow, JSContext* aCx)
+AutoJSAPI::Init(nsPIDOMWindow* aWindow, JSContext* aCx)
 {
   return Init(static_cast<nsGlobalWindow*>(aWindow), aCx);
 }
 
 bool
-AutoJSAPI::InitUsingWin(nsPIDOMWindow* aWindow)
+AutoJSAPI::Init(nsPIDOMWindow* aWindow)
 {
   return Init(static_cast<nsGlobalWindow*>(aWindow));
 }
 
 bool
-AutoJSAPI::InitWithLegacyErrorReportingUsingWin(nsPIDOMWindow* aWindow)
+AutoJSAPI::Init(nsGlobalWindow* aWindow, JSContext* aCx)
+{
+  return Init(static_cast<nsIGlobalObject*>(aWindow), aCx);
+}
+
+bool
+AutoJSAPI::Init(nsGlobalWindow* aWindow)
+{
+  return Init(static_cast<nsIGlobalObject*>(aWindow));
+}
+
+bool
+AutoJSAPI::InitWithLegacyErrorReporting(nsPIDOMWindow* aWindow)
 {
   return InitWithLegacyErrorReporting(static_cast<nsGlobalWindow*>(aWindow));
 }
 
+bool
+AutoJSAPI::InitWithLegacyErrorReporting(nsGlobalWindow* aWindow)
+{
+  return InitWithLegacyErrorReporting(static_cast<nsIGlobalObject*>(aWindow));
+}
+
 AutoEntryScript::AutoEntryScript(nsIGlobalObject* aGlobalObject,
                                  bool aIsMainThread,
                                  JSContext* aCx)
   : AutoJSAPI(aGlobalObject, aIsMainThread,
               aCx ? aCx : FindJSContext(aGlobalObject))
   , ScriptSettingsStackEntry(aGlobalObject, /* aCandidate = */ true)
   , mWebIDLCallerPrincipal(nullptr)
 {
--- a/dom/base/ScriptSettings.h
+++ b/dom/base/ScriptSettings.h
@@ -12,16 +12,17 @@
 #include "nsCxPusher.h"
 #include "MainThreadUtils.h"
 #include "nsIGlobalObject.h"
 #include "nsIPrincipal.h"
 
 #include "mozilla/Maybe.h"
 
 class nsPIDOMWindow;
+class nsGlobalWindow;
 
 namespace mozilla {
 namespace dom {
 
 /*
  * System-wide setup/teardown routines. Init and Destroy should be invoked
  * once each, at startup and shutdown (respectively).
  */
@@ -149,21 +150,26 @@ public:
   // it uses the SafeJSContext. It then enters the compartment of aGlobalObject.
   // This means that existing error reporting mechanisms that use the JSContext
   // to find the JSErrorReporter should still work as before.
   // We should be able to remove this around bug 981198.
   // If aGlobalObject or its associated JS global are null then it returns
   // false and use of cx() will cause an assertion.
   bool InitWithLegacyErrorReporting(nsIGlobalObject* aGlobalObject);
 
-  // Convenience functions to take an nsPIDOMWindow*, when it is more easily
-  // available than an nsIGlobalObject.
-  bool InitUsingWin(nsPIDOMWindow* aWindow);
-  bool InitUsingWin(nsPIDOMWindow* aWindow, JSContext* aCx);
-  bool InitWithLegacyErrorReportingUsingWin(nsPIDOMWindow* aWindow);
+  // Convenience functions to take an nsPIDOMWindow* or nsGlobalWindow*,
+  // when it is more easily available than an nsIGlobalObject.
+  bool Init(nsPIDOMWindow* aWindow);
+  bool Init(nsPIDOMWindow* aWindow, JSContext* aCx);
+
+  bool Init(nsGlobalWindow* aWindow);
+  bool Init(nsGlobalWindow* aWindow, JSContext* aCx);
+
+  bool InitWithLegacyErrorReporting(nsPIDOMWindow* aWindow);
+  bool InitWithLegacyErrorReporting(nsGlobalWindow* aWindow);
 
   JSContext* cx() const {
     MOZ_ASSERT(mCx, "Must call Init before using an AutoJSAPI");
     return mCx;
   }
 
   bool CxPusherIsStackTop() { return mCxPusher.ref().IsStackTop(); }
 
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -5886,17 +5886,17 @@ def getUnionMemberName(type):
 class MethodNotNewObjectError(Exception):
     def __init__(self, typename):
         self.typename = typename
 
 # A counter for making sure that when we're wrapping up things in
 # nested sequences we don't use the same variable name to iterate over
 # different sequences.
 sequenceWrapLevel = 0
-
+mapWrapLevel = 0
 
 def wrapTypeIntoCurrentCompartment(type, value, isMember=True):
     """
     Take the thing named by "value" and if it contains "any",
     "object", or spidermonkey-interface types inside return a CGThing
     that will wrap them into the current compartment.
     """
     if type.isAny():
@@ -5946,16 +5946,41 @@ def wrapTypeIntoCurrentCompartment(type,
         wrapCode = CGWrapper(CGIndenter(wrapElement),
                              pre=("for (uint32_t %s = 0; %s < %s.Length(); ++%s) {\n" %
                                   (index, index, value, index)),
                              post="}\n")
         if origType.nullable():
             wrapCode = CGIfWrapper(wrapCode, "!%s.IsNull()" % origValue)
         return wrapCode
 
+    if type.isMozMap():
+        origValue = value
+        origType = type
+        if type.nullable():
+            type = type.inner
+            value = "%s.Value()" % value
+        global mapWrapLevel
+        key = "mapName%d" % mapWrapLevel
+        mapWrapLevel += 1
+        wrapElement = wrapTypeIntoCurrentCompartment(type.inner,
+                                                     "%s.Get(%sKeys[%sIndex])" % (value, key, key))
+        mapWrapLevel -= 1
+        if not wrapElement:
+            return None
+        wrapCode = CGWrapper(CGIndenter(wrapElement),
+                             pre=("""
+                                  nsTArray<nsString> %sKeys;
+                                  %s.GetKeys(%sKeys);
+                                  for (uint32_t %sIndex = 0; %sIndex < %sKeys.Length(); ++%sIndex) {
+                                  """ % (key, value, key, key, key, key, key)),
+                             post="}\n")
+        if origType.nullable():
+            wrapCode = CGIfWrapper(wrapCode, "!%s.IsNull()" % origValue)
+        return wrapCode
+
     if type.isDictionary():
         assert not type.nullable()
         myDict = type.inner
         memberWraps = []
         while myDict:
             for member in myDict.members:
                 memberWrap = wrapArgIntoCurrentCompartment(
                     member,
@@ -6627,16 +6652,20 @@ class CGMethodCall(CGThing):
             # Now append all the overloads that take an array or sequence or
             # dictionary or callback interface:
             objectSigs.extend(s for s in possibleSignatures
                               if (distinguishingType(s).isArray() or
                                   distinguishingType(s).isSequence() or
                                   distinguishingType(s).isDictionary() or
                                   distinguishingType(s).isCallbackInterface()))
 
+            # Now append all the overloads that take MozMap:
+            objectSigs.extend(s for s in possibleSignatures
+                              if (distinguishingType(s).isMozMap()))
+
             # There might be more than one thing in objectSigs; we need to check
             # which ones we unwrap to.
             if len(objectSigs) > 0:
                 # Here it's enough to guard on our argument being an object. The
                 # code for unwrapping non-callback interfaces, typed arrays,
                 # sequences, arrays, and Dates will just bail out and move on to
                 # the next overload if the object fails to unwrap correctly,
                 # while "object" accepts any object anyway.  We could even not
--- a/dom/bindings/MozMap.h
+++ b/dom/bindings/MozMap.h
@@ -64,16 +64,23 @@ public:
   // The return value is only safe to use until an AddEntry call.
   const DataType& Get(const nsAString& aKey) const
   {
     const EntryType* ent = this->GetEntry(aKey);
     MOZ_ASSERT(ent, "Why are you using a key we didn't claim to have?");
     return ent->mData;
   }
 
+  DataType& Get(const nsAString& aKey)
+  {
+    EntryType* ent = this->GetEntry(aKey);
+    MOZ_ASSERT(ent, "Why are you using a key we didn't claim to have?");
+    return ent->mData;
+  }
+
   // The return value is only safe to use until an AddEntry call.
   const DataType* GetIfExists(const nsAString& aKey) const
   {
     const EntryType* ent = this->GetEntry(aKey);
     if (!ent) {
       return nullptr;
     }
     return &ent->mData;
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -1700,17 +1700,17 @@ class IDLSequenceType(IDLType):
     def unroll(self):
         return self.inner.unroll()
 
     def isDistinguishableFrom(self, other):
         if other.isUnion():
             # Just forward to the union; it'll deal
             return other.isDistinguishableFrom(self)
         return (other.isPrimitive() or other.isString() or other.isEnum() or
-                other.isDate() or other.isNonCallbackInterface())
+                other.isDate() or other.isNonCallbackInterface() or other.isMozMap())
 
     def _getDependentObjects(self):
         return self.inner._getDependentObjects()
 
 class IDLMozMapType(IDLType):
     # XXXbz This is pretty similar to IDLSequenceType in various ways.
     # And maybe to IDLNullableType.  Should we have a superclass for
     # "type containing this other type"?  Bug 1015318.
@@ -1754,17 +1754,17 @@ class IDLMozMapType(IDLType):
         # needed.
         return self
 
     def isDistinguishableFrom(self, other):
         if other.isUnion():
             # Just forward to the union; it'll deal
             return other.isDistinguishableFrom(self)
         return (other.isPrimitive() or other.isString() or other.isEnum() or
-                other.isDate() or other.isNonCallbackInterface())
+                other.isDate() or other.isNonCallbackInterface() or other.isSequence())
 
     def _getDependentObjects(self):
         return self.inner._getDependentObjects()
 
 class IDLUnionType(IDLType):
     def __init__(self, location, memberTypes):
         IDLType.__init__(self, location, "")
         self.memberTypes = memberTypes
--- a/dom/bindings/parser/tests/test_distinguishability.py
+++ b/dom/bindings/parser/tests/test_distinguishability.py
@@ -179,16 +179,18 @@ def WebIDLTest(parser, harness):
     nullables = ["long?", "short?", "boolean?", "Interface?",
                  "CallbackInterface?", "optional Dict", "optional Dict2",
                  "Date?", "any"]
     dates = [ "Date", "Date?" ]
     nonUserObjects = nonObjects + interfaces + dates
     otherObjects = allBut(argTypes, nonUserObjects + ["object"])
     notRelatedInterfaces = (nonObjects + ["UnrelatedInterface"] +
                             otherObjects + dates)
+    mozMaps = [ "MozMap<object>", "MozMap<Dict>", "MozMap<long>" ]
+    sequences = [ "sequence<long>", "sequence<short>" ]
 
     # Build a representation of the distinguishability table as a dict
     # of dicts, holding True values where needed, holes elsewhere.
     data = dict();
     for type in argTypes:
         data[type] = dict()
     def setDistinguishable(type, types):
         for other in types:
@@ -213,21 +215,21 @@ def WebIDLTest(parser, harness):
     setDistinguishable("CallbackInterface", nonUserObjects)
     setDistinguishable("CallbackInterface?", allBut(nonUserObjects, nullables))
     setDistinguishable("CallbackInterface2", nonUserObjects)
     setDistinguishable("object", nonObjects)
     setDistinguishable("Callback", nonUserObjects)
     setDistinguishable("Callback2", nonUserObjects)
     setDistinguishable("optional Dict", allBut(nonUserObjects, nullables))
     setDistinguishable("optional Dict2", allBut(nonUserObjects, nullables))
-    setDistinguishable("sequence<long>", nonUserObjects)
-    setDistinguishable("sequence<short>", nonUserObjects)
-    setDistinguishable("MozMap<object>", nonUserObjects)
-    setDistinguishable("MozMap<Dict>", nonUserObjects)
-    setDistinguishable("MozMap<long>", nonUserObjects)
+    setDistinguishable("sequence<long>", nonUserObjects + mozMaps)
+    setDistinguishable("sequence<short>", nonUserObjects + mozMaps)
+    setDistinguishable("MozMap<object>", nonUserObjects + sequences)
+    setDistinguishable("MozMap<Dict>", nonUserObjects + sequences)
+    setDistinguishable("MozMap<long>", nonUserObjects + sequences)
     setDistinguishable("long[]", nonUserObjects)
     setDistinguishable("short[]", nonUserObjects)
     setDistinguishable("Date", allBut(argTypes, dates + ["object"]))
     setDistinguishable("Date?", allBut(argTypes, dates + nullables + ["object"]))
     setDistinguishable("any", [])
 
     def areDistinguishable(type1, type2):
         return data[type1].get(type2, False)
--- a/dom/bindings/test/TestBindingHeader.h
+++ b/dom/bindings/test/TestBindingHeader.h
@@ -156,16 +156,21 @@ public:
                                         JS::Handle<JSObject*>,
                                         JS::Handle<JSObject*>,
                                         const Sequence<Dict>&,
                                         JS::Handle<JS::Value>,
                                         const Optional<JS::Handle<JSObject*> >&,
                                         const Optional<JS::Handle<JSObject*> >&,
                                         ErrorResult&);
 
+  static
+  already_AddRefed<TestInterface> Test3(const GlobalObject&,
+                                        const LongOrAnyMozMap&,
+                                        ErrorResult&);
+
   // Integer types
   int8_t ReadonlyByte();
   int8_t WritableByte();
   void SetWritableByte(int8_t);
   void PassByte(int8_t);
   int8_t ReceiveByte();
   void PassOptionalByte(const Optional<int8_t>&);
   void PassOptionalByteBeforeRequired(const Optional<int8_t>&, int8_t);
@@ -578,16 +583,19 @@ public:
   void PassUnion17(const LongSequenceOrNullOrLong&);
   void PassUnion18(JSContext*, const ObjectSequenceOrLong&);
   void PassUnion19(JSContext*, const Optional<ObjectSequenceOrLong>&);
   void PassUnion20(JSContext*, const ObjectSequenceOrLong&);
   void PassUnion21(const LongMozMapOrLong&);
   void PassUnion22(JSContext*, const ObjectMozMapOrLong&);
   void PassUnionWithCallback(const EventHandlerNonNullOrNullOrLong& arg);
   void PassUnionWithByteString(const ByteStringOrLong&);
+  void PassUnionWithMozMap(const StringMozMapOrString&);
+  void PassUnionWithMozMapAndSequence(const StringMozMapOrStringSequence&);
+  void PassUnionWithSequenceAndMozMap(const StringSequenceOrStringMozMap&);
 #endif
   void PassNullableUnion(JSContext*, const Nullable<ObjectOrLong>&);
   void PassOptionalUnion(JSContext*, const Optional<ObjectOrLong>&);
   void PassOptionalNullableUnion(JSContext*, const Optional<Nullable<ObjectOrLong> >&);
   void PassOptionalNullableUnionWithDefaultValue(JSContext*, const Nullable<ObjectOrLong>&);
   //void PassUnionWithInterfaces(const TestInterfaceOrTestExternalInterface& arg);
   //void PassUnionWithInterfacesAndNullable(const TestInterfaceOrNullOrTestExternalInterface& arg);
   void PassUnionWithArrayBuffer(const ArrayBufferOrLong&);
@@ -730,16 +738,20 @@ public:
   void Overload13(const Nullable<int32_t>&);
   void Overload13(bool);
   void Overload14(const Optional<int32_t>&);
   void Overload14(TestInterface&);
   void Overload15(int32_t);
   void Overload15(const Optional<NonNull<TestInterface> >&);
   void Overload16(int32_t);
   void Overload16(const Optional<TestInterface*>&);
+  void Overload17(const Sequence<int32_t>&);
+  void Overload17(const MozMap<int32_t>&);
+  void Overload18(const MozMap<nsString>&);
+  void Overload18(const Sequence<nsString>&);
 
   // Variadic handling
   void PassVariadicThirdArg(const nsAString&, int32_t,
                             const Sequence<OwningNonNull<TestInterface> >&);
 
   // Conditionally exposed methods/attributes
   bool Prefable1();
   bool Prefable2();
--- a/dom/bindings/test/TestCodeGen.webidl
+++ b/dom/bindings/test/TestCodeGen.webidl
@@ -108,17 +108,18 @@ interface OnlyForUseInConstructor {
  Constructor(long arg1, IndirectlyImplementedInterface iface),
  Constructor(Date arg1),
  // Constructor(long arg1, long arg2, (TestInterface or OnlyForUseInConstructor) arg3),
  AvailableIn=CertifiedApps,
  NamedConstructor=Test,
  NamedConstructor=Test(DOMString str),
  NamedConstructor=Test2(DictForConstructor dict, any any1, object obj1,
                         object? obj2, sequence<Dict> seq, optional any any2,
-                        optional object obj3, optional object? obj4)
+                        optional object obj3, optional object? obj4),
+ NamedConstructor=Test3((long or MozMap<any>) arg1)
  ]
 interface TestInterface {
   // Integer types
   // XXXbz add tests for throwing versions of all the integer stuff
   readonly attribute byte readonlyByte;
   attribute byte writableByte;
   void passByte(byte arg);
   byte receiveByte();
@@ -524,16 +525,19 @@ interface TestInterface {
   void passUnion17(optional (sequence<long>? or long) arg = 5);
   void passUnion18((sequence<object> or long) arg);
   void passUnion19(optional (sequence<object> or long) arg);
   void passUnion20(optional (sequence<object> or long) arg = []);
   void passUnion21((MozMap<long> or long) arg);
   void passUnion22((MozMap<object> or long) arg);
   void passUnionWithCallback((EventHandler or long) arg);
   void passUnionWithByteString((ByteString or long) arg);
+  void passUnionWithMozMap((MozMap<DOMString> or DOMString) arg);
+  void passUnionWithMozMapAndSequence((MozMap<DOMString> or sequence<DOMString>) arg);
+  void passUnionWithSequenceAndMozMap((sequence<DOMString> or MozMap<DOMString>) arg);
 #endif
   void passUnionWithNullable((object? or long) arg);
   void passNullableUnion((object or long)? arg);
   void passOptionalUnion(optional (object or long) arg);
   void passOptionalNullableUnion(optional (object or long)? arg);
   void passOptionalNullableUnionWithDefaultValue(optional (object or long)? arg = null);
   //void passUnionWithInterfaces((TestInterface or TestExternalInterface) arg);
   //void passUnionWithInterfacesAndNullable((TestInterface? or TestExternalInterface) arg);
@@ -692,16 +696,20 @@ interface TestInterface {
   void overload13(long? arg);
   void overload13(boolean arg);
   void overload14(optional long arg);
   void overload14(TestInterface arg);
   void overload15(long arg);
   void overload15(optional TestInterface arg);
   void overload16(long arg);
   void overload16(optional TestInterface? arg);
+  void overload17(sequence<long> arg);
+  void overload17(MozMap<long> arg);
+  void overload18(MozMap<DOMString> arg);
+  void overload18(sequence<DOMString> arg);
 
   // Variadic handling
   void passVariadicThirdArg(DOMString arg1, long arg2, TestInterface... arg3);
 
   // Conditionally exposed methods/attributes
   [Pref="abc.def"]
   readonly attribute boolean prefable1;
   [Pref="abc.def"]
--- a/dom/bindings/test/TestExampleGen.webidl
+++ b/dom/bindings/test/TestExampleGen.webidl
@@ -416,16 +416,19 @@ interface TestExampleInterface {
   void passUnion16(optional (sequence<long> or long) arg);
   void passUnion17(optional (sequence<long>? or long) arg = 5);
   void passUnion18((sequence<object> or long) arg);
   void passUnion19(optional (sequence<object> or long) arg);
   void passUnion20(optional (sequence<object> or long) arg = []);
   void passUnion21((MozMap<long> or long) arg);
   void passUnion22((MozMap<object> or long) arg);
   void passUnionWithCallback((EventHandler or long) arg);
+  void passUnionWithMozMap((MozMap<DOMString> or DOMString) arg);
+  void passUnionWithMozMapAndSequence((MozMap<DOMString> or sequence<DOMString>) arg);
+  void passUnionWithSequenceAndMozMap((sequence<DOMString> or MozMap<DOMString>) arg);
 #endif
   void passUnionWithNullable((object? or long) arg);
   void passNullableUnion((object or long)? arg);
   void passOptionalUnion(optional (object or long) arg);
   void passOptionalNullableUnion(optional (object or long)? arg);
   void passOptionalNullableUnionWithDefaultValue(optional (object or long)? arg = null);
   //void passUnionWithInterfaces((TestInterface or TestExternalInterface) arg);
   //void passUnionWithInterfacesAndNullable((TestInterface? or TestExternalInterface) arg);
--- a/dom/bindings/test/TestJSImplGen.webidl
+++ b/dom/bindings/test/TestJSImplGen.webidl
@@ -440,16 +440,19 @@ interface TestJSImplInterface {
   void passUnion16(optional (sequence<long> or long) arg);
   void passUnion17(optional (sequence<long>? or long) arg = 5);
   void passUnion18((sequence<object> or long) arg);
   void passUnion19(optional (sequence<object> or long) arg);
   void passUnion20(optional (sequence<object> or long) arg = []);
   void passUnion21((MozMap<long> or long) arg);
   void passUnion22((MozMap<object> or long) arg);
   void passUnionWithCallback((EventHandler or long) arg);
+  void passUnionWithMozMap((MozMap<DOMString> or DOMString) arg);
+  void passUnionWithMozMapAndSequence((MozMap<DOMString> or sequence<DOMString>) arg);
+  void passUnionWithSequenceAndMozMap((sequence<DOMString> or MozMap<DOMString>) arg);
 #endif
   void passUnionWithNullable((object? or long) arg);
   void passNullableUnion((object or long)? arg);
   void passOptionalUnion(optional (object or long) arg);
   void passOptionalNullableUnion(optional (object or long)? arg);
   void passOptionalNullableUnionWithDefaultValue(optional (object or long)? arg = null);
   //void passUnionWithInterfaces((TestJSImplInterface or TestExternalInterface) arg);
   //void passUnionWithInterfacesAndNullable((TestJSImplInterface? or TestExternalInterface) arg);
--- a/dom/bluetooth/BluetoothAdapter.cpp
+++ b/dom/bluetooth/BluetoothAdapter.cpp
@@ -91,17 +91,17 @@ public:
       nsRefPtr<BluetoothDevice> d =
         BluetoothDevice::Create(mAdapterPtr->GetOwner(),
                                 mAdapterPtr->GetPath(),
                                 properties);
       devices.AppendElement(d);
     }
 
     AutoJSAPI jsapi;
-    if (!jsapi.InitUsingWin(mAdapterPtr->GetOwner())) {
+    if (!jsapi.Init(mAdapterPtr->GetOwner())) {
       BT_WARNING("Failed to initialise AutoJSAPI!");
       SetError(NS_LITERAL_STRING("BluetoothAutoJSAPIInitError"));
       return false;
     }
     JSContext* cx = jsapi.cx();
     JS::Rooted<JSObject*> JsDevices(cx);
     if (NS_FAILED(nsTArrayToJSArray(cx, devices, &JsDevices))) {
       BT_WARNING("Cannot create JS array!");
@@ -247,33 +247,33 @@ BluetoothAdapter::SetPropertyByValue(con
   } else if (name.EqualsLiteral("DiscoverableTimeout")) {
     mDiscoverableTimeout = value.get_uint32_t();
   } else if (name.EqualsLiteral("Class")) {
     mClass = value.get_uint32_t();
   } else if (name.EqualsLiteral("UUIDs")) {
     mUuids = value.get_ArrayOfnsString();
 
     AutoJSAPI jsapi;
-    if (!jsapi.InitUsingWin(GetOwner())) {
+    if (!jsapi.Init(GetOwner())) {
       BT_WARNING("Failed to initialise AutoJSAPI!");
       return;
     }
     JSContext* cx = jsapi.cx();
     JS::Rooted<JSObject*> uuids(cx);
     if (NS_FAILED(nsTArrayToJSArray(cx, mUuids, &uuids))) {
       BT_WARNING("Cannot set JS UUIDs object!");
       return;
     }
     mJsUuids = uuids;
     Root();
   } else if (name.EqualsLiteral("Devices")) {
     mDeviceAddresses = value.get_ArrayOfnsString();
 
     AutoJSAPI jsapi;
-    if (!jsapi.InitUsingWin(GetOwner())) {
+    if (!jsapi.Init(GetOwner())) {
       BT_WARNING("Failed to initialise AutoJSAPI!");
       return;
     }
     JSContext* cx = jsapi.cx();
     JS::Rooted<JSObject*> deviceAddresses(cx);
     if (NS_FAILED(nsTArrayToJSArray(cx, mDeviceAddresses, &deviceAddresses))) {
       BT_WARNING("Cannot set JS Devices object!");
       return;
--- a/dom/bluetooth/BluetoothDevice.cpp
+++ b/dom/bluetooth/BluetoothDevice.cpp
@@ -127,33 +127,33 @@ BluetoothDevice::SetPropertyByValue(cons
   } else if (name.EqualsLiteral("Connected")) {
     mConnected = value.get_bool();
   } else if (name.EqualsLiteral("Paired")) {
     mPaired = value.get_bool();
   } else if (name.EqualsLiteral("UUIDs")) {
     mUuids = value.get_ArrayOfnsString();
 
     AutoJSAPI jsapi;
-    if (!jsapi.InitUsingWin(GetOwner())) {
+    if (!jsapi.Init(GetOwner())) {
       BT_WARNING("Failed to initialise AutoJSAPI!");
       return;
     }
     JSContext* cx = jsapi.cx();
     JS::Rooted<JSObject*> uuids(cx);
     if (NS_FAILED(nsTArrayToJSArray(cx, mUuids, &uuids))) {
       BT_WARNING("Cannot set JS UUIDs object!");
       return;
     }
     mJsUuids = uuids;
     Root();
   } else if (name.EqualsLiteral("Services")) {
     mServices = value.get_ArrayOfnsString();
 
     AutoJSAPI jsapi;
-    if (!jsapi.InitUsingWin(GetOwner())) {
+    if (!jsapi.Init(GetOwner())) {
       BT_WARNING("Failed to initialise AutoJSAPI!");
       return;
     }
     JSContext* cx = jsapi.cx();
     JS::Rooted<JSObject*> services(cx);
     if (NS_FAILED(nsTArrayToJSArray(cx, mServices, &services))) {
       BT_WARNING("Cannot set JS Services object!");
       return;
--- a/dom/bluetooth/BluetoothManager.cpp
+++ b/dom/bluetooth/BluetoothManager.cpp
@@ -64,17 +64,17 @@ public:
     }
 
     const InfallibleTArray<BluetoothNamedValue>& values =
       v.get_ArrayOfBluetoothNamedValue();
     nsRefPtr<BluetoothAdapter> adapter =
       BluetoothAdapter::Create(mManagerPtr->GetOwner(), values);
 
     dom::AutoJSAPI jsapi;
-    if (!jsapi.InitUsingWin(mManagerPtr->GetOwner())) {
+    if (!jsapi.Init(mManagerPtr->GetOwner())) {
       BT_WARNING("Failed to initialise AutoJSAPI!");
       SetError(NS_LITERAL_STRING("BluetoothAutoJSAPIInitError"));
       return false;
     }
     JSContext* cx = jsapi.cx();
     if (NS_FAILED(nsContentUtils::WrapNative(cx, adapter, aValue))) {
       BT_WARNING("Cannot create native object!");
       SetError(NS_LITERAL_STRING("BluetoothNativeObjectError"));
--- a/dom/bluetooth2/BluetoothAdapter.cpp
+++ b/dom/bluetooth2/BluetoothAdapter.cpp
@@ -90,17 +90,17 @@ public:
       }
       nsRefPtr<BluetoothDevice> d =
         BluetoothDevice::Create(mAdapterPtr->GetOwner(),
                                 properties);
       devices.AppendElement(d);
     }
 
     AutoJSAPI jsapi;
-    if (!jsapi.InitUsingWin(mAdapterPtr->GetOwner())) {
+    if (!jsapi.Init(mAdapterPtr->GetOwner())) {
       BT_WARNING("Failed to initialise AutoJSAPI!");
       SetError(NS_LITERAL_STRING("BluetoothAutoJSAPIInitError"));
       return false;
     }
     JSContext* cx = jsapi.cx();
     JS::Rooted<JSObject*> JsDevices(cx);
     if (NS_FAILED(nsTArrayToJSArray(cx, devices, &JsDevices))) {
       BT_WARNING("Cannot create JS array!");
@@ -249,33 +249,33 @@ BluetoothAdapter::SetPropertyByValue(con
   } else if (name.EqualsLiteral("DiscoverableTimeout")) {
     mDiscoverableTimeout = value.get_uint32_t();
   } else if (name.EqualsLiteral("Class")) {
     mClass = value.get_uint32_t();
   } else if (name.EqualsLiteral("UUIDs")) {
     mUuids = value.get_ArrayOfnsString();
 
     AutoJSAPI jsapi;
-    if (!jsapi.InitUsingWin(GetOwner())) {
+    if (!jsapi.Init(GetOwner())) {
       BT_WARNING("Failed to initialise AutoJSAPI!");
       return;
     }
     JSContext* cx = jsapi.cx();
     JS::Rooted<JSObject*> uuids(cx);
     if (NS_FAILED(nsTArrayToJSArray(cx, mUuids, &uuids))) {
       BT_WARNING("Cannot set JS UUIDs object!");
       return;
     }
     mJsUuids = uuids;
     Root();
   } else if (name.EqualsLiteral("Devices")) {
     mDeviceAddresses = value.get_ArrayOfnsString();
 
     AutoJSAPI jsapi;
-    if (!jsapi.InitUsingWin(GetOwner())) {
+    if (!jsapi.Init(GetOwner())) {
       BT_WARNING("Failed to initialise AutoJSAPI!");
       return;
     }
     JSContext* cx = jsapi.cx();
     JS::Rooted<JSObject*> deviceAddresses(cx);
     if (NS_FAILED(nsTArrayToJSArray(cx, mDeviceAddresses, &deviceAddresses))) {
       BT_WARNING("Cannot set JS Devices object!");
       return;
--- a/dom/bluetooth2/BluetoothDevice.cpp
+++ b/dom/bluetooth2/BluetoothDevice.cpp
@@ -121,33 +121,33 @@ BluetoothDevice::SetPropertyByValue(cons
   } else if (name.EqualsLiteral("Connected")) {
     mConnected = value.get_bool();
   } else if (name.EqualsLiteral("Paired")) {
     mPaired = value.get_bool();
   } else if (name.EqualsLiteral("UUIDs")) {
     mUuids = value.get_ArrayOfnsString();
 
     AutoJSAPI jsapi;
-    if (!jsapi.InitUsingWin(GetOwner())) {
+    if (!jsapi.Init(GetOwner())) {
       BT_WARNING("Failed to initialise AutoJSAPI!");
       return;
     }
     JSContext* cx = jsapi.cx();
     JS::Rooted<JSObject*> uuids(cx);
     if (NS_FAILED(nsTArrayToJSArray(cx, mUuids, &uuids))) {
       BT_WARNING("Cannot set JS UUIDs object!");
       return;
     }
     mJsUuids = uuids;
     Root();
   } else if (name.EqualsLiteral("Services")) {
     mServices = value.get_ArrayOfnsString();
 
     AutoJSAPI jsapi;
-    if (!jsapi.InitUsingWin(GetOwner())) {
+    if (!jsapi.Init(GetOwner())) {
       BT_WARNING("Failed to initialise AutoJSAPI!");
       return;
     }
     JSContext* cx = jsapi.cx();
     JS::Rooted<JSObject*> services(cx);
     if (NS_FAILED(nsTArrayToJSArray(cx, mServices, &services))) {
       BT_WARNING("Cannot set JS Services object!");
       return;
--- a/dom/devicestorage/nsDeviceStorage.cpp
+++ b/dom/devicestorage/nsDeviceStorage.cpp
@@ -1721,17 +1721,17 @@ nsIFileToJsval(nsPIDOMWindow* aWindow, D
 }
 
 JS::Value StringToJsval(nsPIDOMWindow* aWindow, nsAString& aString)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aWindow);
 
   AutoJSAPI jsapi;
-  if (NS_WARN_IF(!jsapi.InitUsingWin(aWindow))) {
+  if (NS_WARN_IF(!jsapi.Init(aWindow))) {
     return JSVAL_NULL;
   }
   JSContext* cx = jsapi.cx();
 
   JS::Rooted<JS::Value> result(cx);
   if (!xpc::StringToJsval(cx, aString, &result)) {
     return JSVAL_NULL;
   }
--- a/dom/icc/src/Icc.cpp
+++ b/dom/icc/src/Icc.cpp
@@ -44,17 +44,17 @@ Icc::NotifyEvent(const nsAString& aName)
 {
   return DispatchTrustedEvent(aName);
 }
 
 nsresult
 Icc::NotifyStkEvent(const nsAString& aName, const nsAString& aMessage)
 {
   AutoJSAPI jsapi;
-  if (NS_WARN_IF(!jsapi.InitWithLegacyErrorReportingUsingWin(GetOwner()))) {
+  if (NS_WARN_IF(!jsapi.InitWithLegacyErrorReporting(GetOwner()))) {
     return NS_ERROR_UNEXPECTED;
   }
   JSContext* cx = jsapi.cx();
   JS::Rooted<JS::Value> value(cx);
 
   if (!aMessage.IsEmpty()) {
     nsCOMPtr<nsIJSON> json(new nsJSON());
     nsresult rv = json->DecodeToJSVal(aMessage, cx, &value);
--- a/dom/interfaces/html/moz.build
+++ b/dom/interfaces/html/moz.build
@@ -40,16 +40,17 @@ XPIDL_SOURCES += [
     'nsIDOMHTMLMenuItemElement.idl',
     'nsIDOMHTMLMetaElement.idl',
     'nsIDOMHTMLObjectElement.idl',
     'nsIDOMHTMLOListElement.idl',
     'nsIDOMHTMLOptGroupElement.idl',
     'nsIDOMHTMLOptionElement.idl',
     'nsIDOMHTMLOptionsCollection.idl',
     'nsIDOMHTMLParagraphElement.idl',
+    'nsIDOMHTMLPictureElement.idl',
     'nsIDOMHTMLPreElement.idl',
     'nsIDOMHTMLQuoteElement.idl',
     'nsIDOMHTMLScriptElement.idl',
     'nsIDOMHTMLSelectElement.idl',
     'nsIDOMHTMLSourceElement.idl',
     'nsIDOMHTMLStyleElement.idl',
     'nsIDOMHTMLTableCaptionElem.idl',
     'nsIDOMHTMLTableCellElement.idl',
--- a/dom/interfaces/html/nsIDOMHTMLImageElement.idl
+++ b/dom/interfaces/html/nsIDOMHTMLImageElement.idl
@@ -11,22 +11,23 @@
  *
  * This interface is trying to follow the DOM Level 2 HTML specification:
  * http://www.w3.org/TR/DOM-Level-2-HTML/
  *
  * with changes from the work-in-progress WHATWG HTML specification:
  * http://www.whatwg.org/specs/web-apps/current-work/
  */
 
-[uuid(939f4ea1-cb8d-49d0-a4e1-23bce758f4af)]
+[uuid(e83e726a-0aef-4292-938b-253fec691e2f)]
 interface nsIDOMHTMLImageElement : nsISupports
 {
            attribute DOMString        alt;
            attribute DOMString        src;
            attribute DOMString        srcset;
+           attribute DOMString        sizes;
            attribute DOMString        crossOrigin;
            attribute DOMString        useMap;
            attribute boolean          isMap;
            attribute unsigned long    width;
            attribute unsigned long    height;
   readonly attribute unsigned long    naturalWidth;
   readonly attribute unsigned long    naturalHeight;
   readonly attribute boolean          complete;
new file mode 100644
--- /dev/null
+++ b/dom/interfaces/html/nsIDOMHTMLPictureElement.idl
@@ -0,0 +1,12 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIDOMHTMLElement.idl"
+
+[scriptable, uuid(e0e5ac7f-b969-494c-a61e-9d740e38abba)]
+interface nsIDOMHTMLPictureElement : nsISupports
+{
+};
--- a/dom/interfaces/html/nsIDOMHTMLSourceElement.idl
+++ b/dom/interfaces/html/nsIDOMHTMLSourceElement.idl
@@ -11,15 +11,17 @@
  * <source> element.
  *
  * For more information on this interface, please see
  * http://www.whatwg.org/specs/web-apps/current-work/#source
  *
  * @status UNDER_DEVELOPMENT
  */
 
-[uuid(7adbaf06-572d-4c99-bd59-ac673ddcca93)]
+[uuid(1deb68f8-2ed6-4a41-b8c8-e0f86510f799)]
 interface nsIDOMHTMLSourceElement : nsISupports
 {
            attribute DOMString src;
            attribute DOMString type;
+           attribute DOMString srcset;
+           attribute DOMString sizes;
            attribute DOMString media;
 };
--- a/dom/locales/en-US/chrome/layout/css.properties
+++ b/dom/locales/en-US/chrome/layout/css.properties
@@ -16,16 +16,18 @@ PERuleTrailing=Expected end of rule but 
 PESkipAtRuleEOF2=end of at-rule
 PEUnknownAtRule=Unrecognized at-rule or error parsing at-rule '%1$S'.
 PECharsetRuleEOF=charset string in @charset rule
 PECharsetRuleNotString=Expected charset string but found '%1$S'.
 PEGatherMediaEOF=end of media list in @import or @media rule
 PEGatherMediaNotComma=Expected ',' in media list but found '%1$S'.
 PEGatherMediaNotIdent=Expected identifier in media list but found '%1$S'.
 PEGatherMediaReservedMediaType=Found reserved keyword '%1$S' when looking for media type.
+PEParseSourceSizeListEOF=length value for matched media condition
+PEParseSourceSizeListNotComma=Expected ',' after value but found '%1$S'
 PEImportNotURI=Expected URI in @import rule but found '%1$S'.
 PEImportBadURI=Invalid URI in @import rule: '%1$S'.
 PEImportUnexpected=Found unexpected '%1$S' within @import.
 PEGroupRuleEOF2=end of @media, @supports or @-moz-document rule
 PEGroupRuleNestedAtRule=%1$S rule not allowed within @media or @-moz-document rule.
 PEMozDocRuleBadFunc2=Expected url(), url-prefix(), domain() or regexp() in @-moz-document rule but found '%1$S'.
 PEMozDocRuleNotURI=Expected URI in @-moz-document rule but found '%1$S'.
 PEMozDocRuleNotString=Expected string in @-moz-document rule regexp() function but found '%1$S'.
--- a/dom/mobileconnection/src/MobileConnection.cpp
+++ b/dom/mobileconnection/src/MobileConnection.cpp
@@ -600,17 +600,17 @@ MobileConnection::SetCallForwardingOptio
   }
 
   if (!mProvider) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   AutoJSAPI jsapi;
-  if (!NS_WARN_IF(jsapi.InitUsingWin(GetOwner()))) {
+  if (!NS_WARN_IF(jsapi.Init(GetOwner()))) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   JSContext *cx = jsapi.cx();
   JS::Rooted<JS::Value> options(cx);
   if (!ToJSValue(cx, aOptions, &options)) {
     aRv.Throw(NS_ERROR_TYPE_ERR);
@@ -637,17 +637,17 @@ MobileConnection::GetCallBarringOption(c
   }
 
   if (!mProvider) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   AutoJSAPI jsapi;
-  if (!NS_WARN_IF(jsapi.InitUsingWin(GetOwner()))) {
+  if (!NS_WARN_IF(jsapi.Init(GetOwner()))) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   JSContext *cx = jsapi.cx();
   JS::Rooted<JS::Value> options(cx);
   if (!ToJSValue(cx, aOptions, &options)) {
     aRv.Throw(NS_ERROR_TYPE_ERR);
@@ -674,17 +674,17 @@ MobileConnection::SetCallBarringOption(c
   }
 
   if (!mProvider) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   AutoJSAPI jsapi;
-  if (!NS_WARN_IF(jsapi.InitUsingWin(GetOwner()))) {
+  if (!NS_WARN_IF(jsapi.Init(GetOwner()))) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   JSContext *cx = jsapi.cx();
   JS::Rooted<JS::Value> options(cx);
   if (!ToJSValue(cx, aOptions, &options)) {
     aRv.Throw(NS_ERROR_TYPE_ERR);
@@ -711,17 +711,17 @@ MobileConnection::ChangeCallBarringPassw
   }
 
   if (!mProvider) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   AutoJSAPI jsapi;
-  if (!NS_WARN_IF(jsapi.InitUsingWin(GetOwner()))) {
+  if (!NS_WARN_IF(jsapi.Init(GetOwner()))) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   JSContext *cx = jsapi.cx();
   JS::Rooted<JS::Value> options(cx);
   if (!ToJSValue(cx, aOptions, &options)) {
     aRv.Throw(NS_ERROR_TYPE_ERR);
--- a/dom/mobilemessage/src/MobileMessageManager.cpp
+++ b/dom/mobilemessage/src/MobileMessageManager.cpp
@@ -225,17 +225,17 @@ MobileMessageManager::SendMMS(const MmsP
     rv = mmsService->GetMmsDefaultServiceId(&serviceId);
     if (NS_FAILED(rv)) {
       aRv.Throw(rv);
       return nullptr;
     }
   }
 
   AutoJSAPI jsapi;
-  if (!NS_WARN_IF(jsapi.InitUsingWin(GetOwner()))) {
+  if (!NS_WARN_IF(jsapi.Init(GetOwner()))) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   JSContext *cx = jsapi.cx();
   JS::Rooted<JS::Value> val(cx);
   if (!ToJSValue(cx, aParams, &val)) {
     aRv.Throw(NS_ERROR_TYPE_ERR);
--- a/dom/tests/mochitest/general/mochitest.ini
+++ b/dom/tests/mochitest/general/mochitest.ini
@@ -46,16 +46,17 @@ skip-if = buildapp == 'b2g'
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 # [test_network_events.html]
 # Disable this test until bug 795711 is fixed.
 [test_offsets.html]
 [test_offsets.js]
 [test_outerHTML.html]
 [test_outerHTML.xhtml]
 [test_paste_selection.html]
+[test_picture_pref.html]
 [test_resource_timing.html]
 skip-if = buildapp == 'b2g' # b2g(No clipboard) b2g-debug(No clipboard) b2g-desktop(No clipboard)
 [test_performance_now.html]
 [test_srcset_pref.html]
 [test_showModalDialog.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' #Don't run modal tests on Android # b2g(showmodaldialog) b2g-debug(showmodaldialog) b2g-desktop(showmodaldialog)
 [test_stylesheetPI.html]
 [test_vibrator.html]
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -497,16 +497,18 @@ var interfaceNamesInGlobalScope =
     "HTMLOutputElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLParagraphElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLParamElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLPreElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "HTMLPictureElement", pref: "dom.image.picture.enabled"},
+// IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLProgressElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLPropertiesCollection",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLQuoteElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLScriptElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/general/test_picture_pref.html
@@ -0,0 +1,90 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=870022
+-->
+<head>
+  <title>Test for dom.image.picture.enabled (Bug 870022)</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="runTest()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=870022">Mozilla Bug 870022</a>
+
+<!-- Tests that the picture pref is off pending bug 1017875, accounting
+     for dom.images.srcset.enabled being flipped on in the mean time. -->
+
+
+<!-- these should all load red.png (naturalWidth 1) not big.png (natural
+     width 3000) regardless of dom.images.srcset.enabled being on or off -->
+<picture>
+  <source srcset="http://example.com/tests/image/test/mochitest/big.png">
+  <img id="img1" src="http://example.com/tests/image/test/mochitest/red.png">
+</picture>
+
+<picture>
+  <source srcset="http://example.com/tests/image/test/mochitest/big.png 500w"
+          sizes="500w">
+  <img id="img2"
+       src="http://example.com/tests/image/test/mochitest/red.png"
+       srcset="http://example.com/tests/image/test/mochitest/big.png 500w"
+       sizes="50px">
+</picture>
+
+<!-- Should load red.png with srcset on, otherwise nothing -->
+<img id="img-srcset-only"
+     srcset="http://example.com/tests/image/test/mochitest/big.png 500w, http://example.com/tests/image/test/mochitest/red.png 1x"
+     sizes="50px">
+
+<!-- Should not load regardless of srcset pref -->
+<img id="img-never"
+     srcset="http://example.com/tests/image/test/mochitest/big.png 500w"
+     sizes="50px">
+
+<script type="application/javascript">
+
+  const srcsetPref  = 'dom.image.srcset.enabled';
+  const picturePref = 'dom.image.picture.enabled';
+
+  SimpleTest.waitForExplicitFinish();
+
+  var srcsetEnabled = SpecialPowers.getBoolPref(srcsetPref);
+  var pictureEnabled = SpecialPowers.getBoolPref(picturePref);
+
+  is(pictureEnabled, false, "picture should be disabled pending bug 1017875");
+
+  function runTest() {
+    var img = document.querySelector("img");
+    var source = document.querySelector("source");
+
+    is(img.sizes, undefined, "sizes should not be visible on <img>");
+    is(source.sizes, undefined, "sizes should not be visible on <source>");
+    is(source.srcset, undefined, "srcset should not be visible on <source>");
+
+    var imgSizesDesc = Object.getOwnPropertyDescriptor(HTMLImageElement.prototype, "sizes");
+    var sourceSizesDesc = Object.getOwnPropertyDescriptor(HTMLSourceElement.prototype, "sizes");
+    var sourceSrcsetDesc = Object.getOwnPropertyDescriptor(HTMLSourceElement.prototype, "srcset");
+    is(imgSizesDesc, undefined, "HTMLImageElement should know nothing of sizes");
+    is(sourceSizesDesc, undefined, "HTMLSourceElement should know nothing of sizes");
+    is(sourceSrcsetDesc, undefined, "HTMLSourceElement should know nothing of srcset");
+
+    // Make sure the test images loaded red.png, which is 1x1, not big.png
+    for (var id of [ 'img1', 'img2' ]) {
+      var testImg = document.getElementById(id);
+      is(testImg.naturalWidth, 1, "Image should have loaded small source");
+    }
+
+    var srcsetOnlyImg = document.getElementById("img-srcset-only");
+    is(srcsetOnlyImg.naturalWidth, srcsetEnabled ? 1 : 0,
+       "srcset image should only load if srcset is enabled, and never the computed width candidate");
+
+    var neverImg = document.getElementById("img-never");
+    is(neverImg.naturalWidth, 0, "Image should not have loaded");
+
+    SimpleTest.finish();
+  }
+
+</script>
+
+</body>
+</html>
--- a/dom/webidl/HTMLImageElement.webidl
+++ b/dom/webidl/HTMLImageElement.webidl
@@ -54,16 +54,18 @@ partial interface HTMLImageElement {
            attribute DOMString longDesc;
 
   [TreatNullAs=EmptyString,SetterThrows] attribute DOMString border;
 };
 
 // [Update me: not in whatwg spec yet]
 // http://picture.responsiveimages.org/#the-img-element
 partial interface HTMLImageElement {
+           [SetterThrows, Pref="dom.image.picture.enabled"]
+           attribute DOMString sizes;
            [Pref="dom.image.srcset.enabled"]
   readonly attribute DOMString? currentSrc;
 };
 
 // Mozilla extensions.
 partial interface HTMLImageElement {
            attribute DOMString lowsrc;
 
new file mode 100644
--- /dev/null
+++ b/dom/webidl/HTMLPictureElement.webidl
@@ -0,0 +1,9 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+[Pref="dom.image.picture.enabled"]
+interface HTMLPictureElement : HTMLElement {
+};
--- a/dom/webidl/HTMLSourceElement.webidl
+++ b/dom/webidl/HTMLSourceElement.webidl
@@ -11,16 +11,23 @@
  * and create derivative works of this document.
  */
 
 interface HTMLSourceElement : HTMLElement {
            [SetterThrows]
            attribute DOMString src;
            [SetterThrows]
            attribute DOMString type;
+};
+
+partial interface HTMLSourceElement {
+           [SetterThrows, Pref="dom.image.picture.enabled"]
+           attribute DOMString srcset;
+           [SetterThrows, Pref="dom.image.picture.enabled"]
+           attribute DOMString sizes;
            [SetterThrows]
            attribute DOMString media;
 };
 
 // Encrypted Media Extensions
 partial interface HTMLSourceElement {
   [Pref="media.eme.enabled"]
   attribute DOMString keySystem;
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -173,16 +173,17 @@ WEBIDL_FILES = [
     'HTMLObjectElement.webidl',
     'HTMLOListElement.webidl',
     'HTMLOptGroupElement.webidl',
     'HTMLOptionElement.webidl',
     'HTMLOptionsCollection.webidl',
     'HTMLOutputElement.webidl',
     'HTMLParagraphElement.webidl',
     'HTMLParamElement.webidl',
+    'HTMLPictureElement.webidl',
     'HTMLPreElement.webidl',
     'HTMLProgressElement.webidl',
     'HTMLPropertiesCollection.webidl',
     'HTMLQuoteElement.webidl',
     'HTMLScriptElement.webidl',
     'HTMLSelectElement.webidl',
     'HTMLShadowElement.webidl',
     'HTMLSourceElement.webidl',
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -3133,17 +3133,17 @@ WorkerPrivateParent<Derived>::BroadcastE
     nsRefPtr<SharedWorker>& sharedWorker = sharedWorkers[index];
 
     // May be null.
     nsPIDOMWindow* window = sharedWorker->GetOwner();
 
     size_t actionsIndex = windowActions.LastIndexOf(WindowAction(window));
 
     AutoJSAPI jsapi;
-    if (NS_WARN_IF(!jsapi.InitWithLegacyErrorReportingUsingWin(sharedWorker->GetOwner()))) {
+    if (NS_WARN_IF(!jsapi.InitWithLegacyErrorReporting(sharedWorker->GetOwner()))) {
       continue;
     }
     JSContext* cx = jsapi.cx();
 
     RootedDictionary<ErrorEventInit> errorInit(aCx);
     errorInit.mBubbles = false;
     errorInit.mCancelable = true;
     errorInit.mMessage = aMessage;
--- a/editor/libeditor/html/nsHTMLEditUtils.cpp
+++ b/editor/libeditor/html/nsHTMLEditUtils.cpp
@@ -521,30 +521,30 @@ nsHTMLEditUtils::SupportsAlignAttr(nsIDO
 // GROUP_ values (OR'ed together).
 // Testing containment then simply consists of checking whether the
 // mCanContainGroups bitmask of an element and the mGroup bitmask of a
 // potential child overlap.
 
 #define GROUP_NONE             0
 
 // body, head, html
-#define GROUP_TOPLEVEL         (1 << 1)  
+#define GROUP_TOPLEVEL         (1 << 1)
 
 // base, link, meta, script, style, title
 #define GROUP_HEAD_CONTENT     (1 << 2)
 
 // b, big, i, s, small, strike, tt, u
 #define GROUP_FONTSTYLE        (1 << 3)
 
 // abbr, acronym, cite, code, datalist, del, dfn, em, ins, kbd, mark, rb, rp
 // rt, rtc, ruby, samp, strong, var
 #define GROUP_PHRASE           (1 << 4)
 
 // a, applet, basefont, bdo, br, font, iframe, img, map, meter, object, output,
-// progress, q, script, span, sub, sup
+// picture, progress, q, script, span, sub, sup
 #define GROUP_SPECIAL          (1 << 5)
 
 // button, form, input, label, select, textarea
 #define GROUP_FORMCONTROL      (1 << 6)
 
 // address, applet, article, aside, blockquote, button, center, del, dir, div,
 // dl, fieldset, figure, footer, form, h1, h2, h3, h4, h5, h6, header, hgroup,
 // hr, iframe, ins, main, map, menu, nav, noframes, noscript, object, ol, p,
@@ -585,26 +585,29 @@ nsHTMLEditUtils::SupportsAlignAttr(nsIDO
 #define GROUP_DL_CONTENT       (1 << 18)
 
 // p
 #define GROUP_P                (1 << 19)
 
 // text, whitespace, newline, comment
 #define GROUP_LEAF             (1 << 20)
 
-// XXX This is because the editor does sublists illegally. 
+// XXX This is because the editor does sublists illegally.
 // ol, ul
 #define GROUP_OL_UL            (1 << 21)
 
 // h1, h2, h3, h4, h5, h6
 #define GROUP_HEADING          (1 << 22)
 
 // figcaption
 #define GROUP_FIGCAPTION       (1 << 23)
 
+// picture members (img, source)
+#define GROUP_PICTURE_CONTENT  (1 << 24)
+
 #define GROUP_INLINE_ELEMENT \
   (GROUP_FONTSTYLE | GROUP_PHRASE | GROUP_SPECIAL | GROUP_FORMCONTROL | \
    GROUP_LEAF)
 
 #define GROUP_FLOW_ELEMENT (GROUP_INLINE_ELEMENT | GROUP_BLOCK)
 
 struct nsElementInfo
 {
@@ -694,17 +697,17 @@ static const nsElementInfo kElements[eHT
   ELEM(header, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT),
   ELEM(hgroup, true, false, GROUP_BLOCK, GROUP_HEADING),
   ELEM(hr, false, false, GROUP_BLOCK, GROUP_NONE),
   ELEM(html, true, false, GROUP_TOPLEVEL, GROUP_TOPLEVEL),
   ELEM(i, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT),
   ELEM(iframe, true, true, GROUP_SPECIAL | GROUP_BLOCK,
        GROUP_FLOW_ELEMENT),
   ELEM(image, false, false, GROUP_NONE, GROUP_NONE),
-  ELEM(img, false, false, GROUP_SPECIAL, GROUP_NONE),
+  ELEM(img, false, false, GROUP_SPECIAL | GROUP_PICTURE_CONTENT, GROUP_NONE),
   ELEM(input, false, false, GROUP_FORMCONTROL, GROUP_NONE),
   ELEM(ins, true, true, GROUP_PHRASE | GROUP_BLOCK, GROUP_FLOW_ELEMENT),
   ELEM(kbd, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
   ELEM(keygen, false, false, GROUP_FORMCONTROL, GROUP_NONE),
   ELEM(label, true, false, GROUP_FORMCONTROL, GROUP_INLINE_ELEMENT),
   ELEM(legend, true, true, GROUP_NONE, GROUP_INLINE_ELEMENT),
   ELEM(li, true, false, GROUP_LI, GROUP_FLOW_ELEMENT),
   ELEM(link, false, false, GROUP_HEAD_CONTENT, GROUP_NONE),
@@ -730,16 +733,17 @@ static const nsElementInfo kElements[eHT
        GROUP_LI | GROUP_OL_UL),
   ELEM(optgroup, true, false, GROUP_SELECT_CONTENT,
        GROUP_OPTIONS),
   ELEM(option, true, false,
        GROUP_SELECT_CONTENT | GROUP_OPTIONS, GROUP_LEAF),
   ELEM(output, true, true, GROUP_SPECIAL, GROUP_INLINE_ELEMENT),
   ELEM(p, true, false, GROUP_BLOCK | GROUP_P, GROUP_INLINE_ELEMENT),
   ELEM(param, false, false, GROUP_OBJECT_CONTENT, GROUP_NONE),
+  ELEM(picture, true, false, GROUP_SPECIAL, GROUP_PICTURE_CONTENT),
   ELEM(plaintext, false, false, GROUP_NONE, GROUP_NONE),
   ELEM(pre, true, true, GROUP_BLOCK, GROUP_INLINE_ELEMENT),
   ELEM(progress, true, false, GROUP_SPECIAL, GROUP_FLOW_ELEMENT),
   ELEM(q, true, true, GROUP_SPECIAL, GROUP_INLINE_ELEMENT),
   ELEM(rb, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
   ELEM(rp, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
   ELEM(rt, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
   ELEM(rtc, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
@@ -747,17 +751,17 @@ static const nsElementInfo kElements[eHT
   ELEM(s, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT),
   ELEM(samp, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
   ELEM(script, true, false, GROUP_HEAD_CONTENT | GROUP_SPECIAL,
        GROUP_LEAF),
   ELEM(section, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT),
   ELEM(select, true, false, GROUP_FORMCONTROL, GROUP_SELECT_CONTENT),
   ELEM(shadow, true, false, GROUP_NONE, GROUP_INLINE_ELEMENT),
   ELEM(small, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT),
-  ELEM(source, false, false, GROUP_NONE, GROUP_NONE),
+  ELEM(source, false, false, GROUP_PICTURE_CONTENT, GROUP_NONE),
   ELEM(span, true, true, GROUP_SPECIAL, GROUP_INLINE_ELEMENT),
   ELEM(strike, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT),
   ELEM(strong, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
   ELEM(style, true, false, GROUP_HEAD_CONTENT, GROUP_LEAF),
   ELEM(sub, true, true, GROUP_SPECIAL, GROUP_INLINE_ELEMENT),
   ELEM(sup, true, true, GROUP_SPECIAL, GROUP_INLINE_ELEMENT),
   ELEM(table, true, false, GROUP_BLOCK, GROUP_TABLE_CONTENT),
   ELEM(tbody, true, false, GROUP_TABLE_CONTENT, GROUP_TBODY_CONTENT),
--- a/gfx/layers/opengl/TextureHostOGL.cpp
+++ b/gfx/layers/opengl/TextureHostOGL.cpp
@@ -179,31 +179,17 @@ bool
 TextureHostOGL::SetReleaseFence(const android::sp<android::Fence>& aReleaseFence)
 {
   if (!aReleaseFence.get() || !aReleaseFence->isValid()) {
     // HWC might not provide Fence.
     // In this case, HWC implicitly handles buffer's fence.
     return false;
   }
 
-  if (!mReleaseFence.get()) {
-    mReleaseFence = aReleaseFence;
-  } else {
-    android::sp<android::Fence> mergedFence = android::Fence::merge(
-                  android::String8::format("TextureHostOGL"),
-                  mReleaseFence, aReleaseFence);
-    if (!mergedFence.get()) {
-      // synchronization is broken, the best we can do is hope fences
-      // signal in order so the new fence will act like a union.
-      // This error handling is same as android::ConsumerBase does.
-      mReleaseFence = aReleaseFence;
-      return false;
-    }
-    mReleaseFence = mergedFence;
-  }
+  mReleaseFence = aReleaseFence;
   return true;
 }
 
 android::sp<android::Fence>
 TextureHostOGL::GetAndResetReleaseFence()
 {
   // Hold previous ReleaseFence to prevent Fence delivery failure via gecko IPC.
   mPrevReleaseFence = mReleaseFence;
--- a/gfx/tests/gtest/TestAsyncPanZoomController.cpp
+++ b/gfx/tests/gtest/TestAsyncPanZoomController.cpp
@@ -31,30 +31,24 @@ using ::testing::InSequence;
 class Task;
 
 class AsyncPanZoomControllerTester : public ::testing::Test {
 protected:
   virtual void SetUp() {
     gfxPrefs::GetSingleton();
     AsyncPanZoomController::SetThreadAssertionsEnabled(false);
   }
-  virtual void TearDown() {
-    gfxPrefs::DestroySingleton();
-  }
 };
 
 class APZCTreeManagerTester : public ::testing::Test {
 protected:
   virtual void SetUp() {
     gfxPrefs::GetSingleton();
     AsyncPanZoomController::SetThreadAssertionsEnabled(false);
   }
-  virtual void TearDown() {
-    gfxPrefs::DestroySingleton();
-  }
 };
 
 class MockContentController : public GeckoContentController {
 public:
   MOCK_METHOD1(RequestContentRepaint, void(const FrameMetrics&));
   MOCK_METHOD2(AcknowledgeScrollUpdate, void(const FrameMetrics::ViewID&, const uint32_t& aScrollGeneration));
   MOCK_METHOD3(HandleDoubleTap, void(const CSSPoint&, int32_t, const ScrollableLayerGuid&));
   MOCK_METHOD3(HandleSingleTap, void(const CSSPoint&, int32_t, const ScrollableLayerGuid&));
--- a/gfx/tests/gtest/TestGfxPrefs.cpp
+++ b/gfx/tests/gtest/TestGfxPrefs.cpp
@@ -11,43 +11,35 @@
 #endif
 
 // If the default values for any of these preferences change,
 // just modify the test to match. We are only testing against
 // a particular value to make sure we receive the correct
 // result through this API.
 
 TEST(GfxPrefs, Singleton) {
-  ASSERT_FALSE(gfxPrefs::SingletonExists());
   gfxPrefs::GetSingleton();
   ASSERT_TRUE(gfxPrefs::SingletonExists());
-  gfxPrefs::DestroySingleton();
-  ASSERT_FALSE(gfxPrefs::SingletonExists());
 }
 
 TEST(GfxPrefs, LiveValues) {
-  ASSERT_FALSE(gfxPrefs::SingletonExists());
   gfxPrefs::GetSingleton();
   ASSERT_TRUE(gfxPrefs::SingletonExists());
 
   // Live boolean, default false
   ASSERT_FALSE(gfxPrefs::CanvasAzureAccelerated());
 
   // Live int32_t, default 23456
   ASSERT_TRUE(gfxPrefs::LayerScopePort() == 23456);
 
   // Live uint32_t, default 2
   ASSERT_TRUE(gfxPrefs::MSAALevel() == 2);
-
-  gfxPrefs::DestroySingleton();
-  ASSERT_FALSE(gfxPrefs::SingletonExists());
 }
 
 TEST(GfxPrefs, OnceValues) {
-  ASSERT_FALSE(gfxPrefs::SingletonExists());
   gfxPrefs::GetSingleton();
   ASSERT_TRUE(gfxPrefs::SingletonExists());
 
   // Once boolean, default true
   ASSERT_TRUE(gfxPrefs::WorkAroundDriverBugs());
 
   // Once boolean, default false
   ASSERT_FALSE(gfxPrefs::LayersDump());
@@ -55,13 +47,10 @@ TEST(GfxPrefs, OnceValues) {
   // Once int32_t, default 95
   ASSERT_TRUE(gfxPrefs::CanvasSkiaGLCacheSize() == 96);
 
   // Once uint32_t, default 5
   ASSERT_TRUE(gfxPrefs::APZMaxVelocityQueueSize() == 5);
 
   // Once float, default -1 (should be OK with ==)
   ASSERT_TRUE(gfxPrefs::APZMaxVelocity() == -1.0f);
-
-  gfxPrefs::DestroySingleton();
-  ASSERT_FALSE(gfxPrefs::SingletonExists());
 }
 
--- a/gfx/thebes/gfxDWriteFontList.cpp
+++ b/gfx/thebes/gfxDWriteFontList.cpp
@@ -469,17 +469,17 @@ public:
         : mFontFace(aFontFace), mContext(aContext)
     { }
 
     ~FontTableRec() {
         mFontFace->ReleaseFontTable(mContext);
     }
 
 private:
-    IDWriteFontFace *mFontFace;
+    nsRefPtr<IDWriteFontFace> mFontFace;
     void            *mContext;
 };
 
 static void
 DestroyBlobFunc(void* aUserData)
 {
     FontTableRec *ftr = static_cast<FontTableRec*>(aUserData);
     delete ftr;
--- a/gfx/thebes/gfxPrefs.cpp
+++ b/gfx/thebes/gfxPrefs.cpp
@@ -6,24 +6,27 @@
 #include "gfxPrefs.h"
 
 #include "mozilla/Preferences.h"
 #include "MainThreadUtils.h"
 
 using namespace mozilla;
 
 gfxPrefs* gfxPrefs::sInstance = nullptr;
+bool gfxPrefs::sInstanceHasBeenDestroyed = false;
 
 void
 gfxPrefs::DestroySingleton()
 {
   if (sInstance) {
     delete sInstance;
     sInstance = nullptr;
+    sInstanceHasBeenDestroyed = true;
   }
+  MOZ_ASSERT(!SingletonExists());
 }
 
 bool
 gfxPrefs::SingletonExists()
 {
   return sInstance != nullptr;
 }
 
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -229,26 +229,29 @@ private:
   DECL_GFX_PREF(Live, "ui.click_hold_context_menus.delay",     UiClickHoldContextMenusDelay, int32_t, 500);
 
   DECL_GFX_PREF(Once, "webgl.force-layers-readback",           WebGLForceLayersReadback, bool, false);
 
 public:
   // Manage the singleton:
   static gfxPrefs& GetSingleton()
   {
+    MOZ_ASSERT(!sInstanceHasBeenDestroyed, "Should never recreate a gfxPrefs instance!");
     if (!sInstance) {
       sInstance = new gfxPrefs;
     }
+    MOZ_ASSERT(SingletonExists());
     return *sInstance;
   }
   static void DestroySingleton();
   static bool SingletonExists();
 
 private:
   static gfxPrefs* sInstance;
+  static bool sInstanceHasBeenDestroyed;
 
 private:
   // Creating these to avoid having to include Preferences.h in the .h
   static void PrefAddVarCache(bool*, const char*, bool);
   static void PrefAddVarCache(int32_t*, const char*, int32_t);
   static void PrefAddVarCache(uint32_t*, const char*, uint32_t);
   static void PrefAddVarCache(float*, const char*, float);
   static bool PrefGet(const char*, bool);
--- a/image/encoders/ico/nsICOEncoder.cpp
+++ b/image/encoders/ico/nsICOEncoder.cpp
@@ -152,17 +152,19 @@ nsICOEncoder::AddImageFrame(const uint8_
     mImageBufferSize = ICONFILEHEADERSIZE + ICODIRENTRYSIZE + 
                        BMPImageBufferSize + andMaskSize;
     mImageBufferStart = static_cast<uint8_t*>(moz_malloc(mImageBufferSize));
     if (!mImageBufferStart) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
     mImageBufferCurr = mImageBufferStart;
 
-    // The icon buffer does not include the BFH at all.
+    // Icon files that wrap a BMP file must not include the BITMAPFILEHEADER
+    // section at the beginning of the encoded BMP data, so we must skip over
+    // BFH_LENGTH bytes when adding the BMP content to the icon file.
     mICODirEntry.mBytesInRes = BMPImageBufferSize - BFH_LENGTH + andMaskSize;
 
     // Encode the icon headers
     EncodeFileHeader();
     EncodeInfoHeader();
 
     char *imageBuffer;
     rv = mContainedEncoder->GetImageBuffer(&imageBuffer);
--- a/image/src/ICOFileHeaders.h
+++ b/image/src/ICOFileHeaders.h
@@ -9,38 +9,73 @@
 namespace mozilla {
   namespace image {
 
     #define ICONFILEHEADERSIZE 6
     #define ICODIRENTRYSIZE 16
     #define PNGSIGNATURESIZE 8
     #define BMPFILEHEADERSIZE 14
 
+    /**
+     * The header that comes right at the start of an icon file. (This
+     * corresponds to the Windows ICONDIR structure.)
+     */
     struct IconFileHeader
     {
+      /**
+       * Must be set to 0;
+       */
       uint16_t   mReserved;
+      /**
+       * 1 for icon (.ICO) image (or 2 for cursor (.CUR) image (icon with the
+       * addition of a hotspot), but we don't support cursor).
+       */
       uint16_t   mType;
+      /**
+       * The number of BMP/PNG images contained in the icon file.
+       */
       uint16_t   mCount;
     };
 
+    /**
+     * For each BMP/PNG image that the icon file containts there must be a
+     * corresponding icon dir entry. (This corresponds to the Windows
+     * ICONDIRENTRY structure.) These entries are encoded directly after the
+     * IconFileHeader.
+     */
     struct IconDirEntry
     {
       uint8_t   mWidth;
       uint8_t   mHeight;
+      /**
+       * The number of colors in the color palette of the BMP/PNG that this dir
+       * entry corresponds to, or 0 if the image does not use a color palette.
+       */
       uint8_t   mColorCount;
+      /**
+       * Should be set to 0.
+       */
       uint8_t   mReserved;
       union {
         uint16_t mPlanes;   // ICO
         uint16_t mXHotspot; // CUR
       };
       union {
-        uint16_t mBitCount; // ICO
+        uint16_t mBitCount; // ICO (bits per pixel)
         uint16_t mYHotspot; // CUR
       };
+      /**
+       * "bytes in resource" is the length of the encoded BMP/PNG that this dir
+       * entry corresponds to.
+       */
       uint32_t  mBytesInRes;
+      /**
+       * The offset of the start of the encoded BMP/PNG that this dir entry
+       * corresponds to (from the start of the icon file).
+       */
       uint32_t  mImageOffset;
     };
 
 
   } // namespace image
 } // namespace mozilla
 
 #endif
--- a/image/src/SurfaceCache.cpp
+++ b/image/src/SurfaceCache.cpp
@@ -535,16 +535,18 @@ SurfaceCache::Discard(Image* aImageKey)
   MOZ_ASSERT(NS_IsMainThread());
 
   return sInstance->Discard(aImageKey);
 }
 
 /* static */ void
 SurfaceCache::DiscardAll()
 {
-  MOZ_ASSERT(sInstance, "Should be initialized");
   MOZ_ASSERT(NS_IsMainThread());
 
-  return sInstance->DiscardAll();
+  if (sInstance) {
+    sInstance->DiscardAll();
+  }
+  // nothing to discard
 }
 
 } // namespace image
 } // namespace mozilla
--- a/js/src/builtin/Intl.cpp
+++ b/js/src/builtin/Intl.cpp
@@ -413,16 +413,26 @@ IntlInitialize(JSContext *cx, HandleObje
     args.setThis(NullValue());
     args[0].setObject(*obj);
     args[1].set(locales);
     args[2].set(options);
 
     return Invoke(cx, args);
 }
 
+static bool
+CreateDefaultOptions(JSContext *cx, MutableHandleValue defaultOptions)
+{
+    RootedObject options(cx, NewObjectWithGivenProto(cx, &JSObject::class_, nullptr, cx->global()));
+    if (!options)
+        return false;
+    defaultOptions.setObject(*options);
+    return true;
+}
+
 // CountAvailable and GetAvailable describe the signatures used for ICU API
 // to determine available locales for various functionality.
 typedef int32_t
 (* CountAvailable)(void);
 
 typedef const char *
 (* GetAvailable)(int32_t localeIndex);
 
@@ -696,22 +706,23 @@ InitCollatorClass(JSContext *cx, HandleO
         return nullptr;
     if (!JSObject::defineProperty(cx, proto, cx->names().compare, UndefinedHandleValue,
                                   JS_DATA_TO_FUNC_PTR(JSPropertyOp, &getter.toObject()),
                                   nullptr, JSPROP_GETTER))
     {
         return nullptr;
     }
 
+    RootedValue options(cx);
+    if (!CreateDefaultOptions(cx, &options))
+        return nullptr;
+
     // 10.2.1 and 10.3
-    if (!IntlInitialize(cx, proto, cx->names().InitializeCollator, UndefinedHandleValue,
-                        UndefinedHandleValue))
-    {
+    if (!IntlInitialize(cx, proto, cx->names().InitializeCollator, UndefinedHandleValue, options))
         return nullptr;
-    }
 
     // 8.1
     RootedValue ctorValue(cx, ObjectValue(*ctor));
     if (!JSObject::defineProperty(cx, Intl, cx->names().Collator, ctorValue,
                                   JS_PropertyStub, JS_StrictPropertyStub, 0))
     {
         return nullptr;
     }
@@ -1186,22 +1197,23 @@ InitNumberFormatClass(JSContext *cx, Han
         return nullptr;
     if (!JSObject::defineProperty(cx, proto, cx->names().format, UndefinedHandleValue,
                                   JS_DATA_TO_FUNC_PTR(JSPropertyOp, &getter.toObject()),
                                   nullptr, JSPROP_GETTER))
     {
         return nullptr;
     }
 
+    RootedValue options(cx);
+    if (!CreateDefaultOptions(cx, &options))
+        return nullptr;
+
     // 11.2.1 and 11.3
-    if (!IntlInitialize(cx, proto, cx->names().InitializeNumberFormat, UndefinedHandleValue,
-                        UndefinedHandleValue))
-    {
+    if (!IntlInitialize(cx, proto, cx->names().InitializeNumberFormat, UndefinedHandleValue, options))
         return nullptr;
-    }
 
     // 8.1
     RootedValue ctorValue(cx, ObjectValue(*ctor));
     if (!JSObject::defineProperty(cx, Intl, cx->names().NumberFormat, ctorValue,
                                   JS_PropertyStub, JS_StrictPropertyStub, 0))
     {
         return nullptr;
     }
@@ -1641,22 +1653,23 @@ InitDateTimeFormatClass(JSContext *cx, H
         return nullptr;
     if (!JSObject::defineProperty(cx, proto, cx->names().format, UndefinedHandleValue,
                                   JS_DATA_TO_FUNC_PTR(JSPropertyOp, &getter.toObject()),
                                   nullptr, JSPROP_GETTER))
     {
         return nullptr;
     }
 
+    RootedValue options(cx);
+    if (!CreateDefaultOptions(cx, &options))
+        return nullptr;
+
     // 12.2.1 and 12.3
-    if (!IntlInitialize(cx, proto, cx->names().InitializeDateTimeFormat, UndefinedHandleValue,
-                        UndefinedHandleValue))
-    {
+    if (!IntlInitialize(cx, proto, cx->names().InitializeDateTimeFormat, UndefinedHandleValue, options))
         return nullptr;
-    }
 
     // 8.1
     RootedValue ctorValue(cx, ObjectValue(*ctor));
     if (!JSObject::defineProperty(cx, Intl, cx->names().DateTimeFormat, ctorValue,
                                   JS_PropertyStub, JS_StrictPropertyStub, 0))
     {
         return nullptr;
     }
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug920484.js
@@ -0,0 +1,33 @@
+load(libdir + "asserts.js");
+
+// Add an invalid "localeMatcher" option to Object.prototype, the exact value does not matter,
+// any value except "best fit" or "lookup" is okay.
+Object.prototype.localeMatcher = "invalid matcher option";
+
+// The Intl API may not be available in the testing environment. Note that |hasOwnProperty("Intl")|
+// initializes the Intl API if present, so this if-statement needs to appear after "localeMatcher"
+// was added to Object.prototype.
+if (this.hasOwnProperty("Intl")) {
+    // Intl prototypes are properly initialized despite changed Object.prototype.
+    Intl.Collator.prototype.compare("a", "b");
+    Intl.NumberFormat.prototype.format(10);
+    Intl.DateTimeFormat.prototype.format(new Date);
+
+    // Intl constructors no longer work properly, because "localeMatcher" defaults to the invalid
+    // value from Object.prototype. Except for Intl.DateTimeFormat, cf. ECMA-402 ToDateTimeOptions.
+    assertThrowsInstanceOf(() => new Intl.Collator(), RangeError);
+    assertThrowsInstanceOf(() => new Intl.NumberFormat(), RangeError);
+    new Intl.DateTimeFormat().format(new Date);
+
+    // If an explicit "localeMatcher" option is given, the default from Object.prototype is ignored.
+    new Intl.Collator(void 0, {localeMatcher: "lookup"}).compare("a", "b");
+    new Intl.NumberFormat(void 0, {localeMatcher: "lookup"}).format(10);
+    new Intl.DateTimeFormat(void 0, {localeMatcher: "lookup"}).format(new Date);
+
+    delete Object.prototype.localeMatcher;
+
+    // After removing the default option from Object.prototype, everything works again as expected.
+    new Intl.Collator().compare("a", "b");
+    new Intl.NumberFormat().format(10);
+    new Intl.DateTimeFormat().format(new Date);
+}
--- a/js/src/jit/AsmJS.cpp
+++ b/js/src/jit/AsmJS.cpp
@@ -6016,18 +6016,18 @@ GenerateEntry(ModuleCompiler &m, const A
             masm.loadDouble(src, iter->fpu());
             break;
           case ABIArg::Stack:
             if (iter.mirType() == MIRType_Int32) {
                 masm.load32(src, scratch);
                 masm.storePtr(scratch, Address(StackPointer, iter->offsetFromArgBase()));
             } else {
                 JS_ASSERT(iter.mirType() == MIRType_Double || iter.mirType() == MIRType_Float32);
-                masm.loadDouble(src, ScratchFloatReg);
-                masm.storeDouble(ScratchFloatReg, Address(StackPointer, iter->offsetFromArgBase()));
+                masm.loadDouble(src, ScratchDoubleReg);
+                masm.storeDouble(ScratchDoubleReg, Address(StackPointer, iter->offsetFromArgBase()));
             }
             break;
         }
     }
 
     // Call into the real function.
     AssertStackAlignment(masm);
     masm.call(CallSiteDesc::Entry(), func.code());
@@ -6040,21 +6040,21 @@ GenerateEntry(ModuleCompiler &m, const A
     // Store the return value in argv[0]
     switch (func.sig().retType().which()) {
       case RetType::Void:
         break;
       case RetType::Signed:
         masm.storeValue(JSVAL_TYPE_INT32, ReturnReg, Address(argv, 0));
         break;
       case RetType::Float:
-        masm.convertFloat32ToDouble(ReturnFloatReg, ReturnFloatReg);
-        // Fall through as ReturnFloatReg now contains a Double
+        masm.convertFloat32ToDouble(ReturnFloat32Reg, ReturnDoubleReg);
+        // Fall through as ReturnDoubleReg now contains a Double
       case RetType::Double:
-        masm.canonicalizeDouble(ReturnFloatReg);
-        masm.storeDouble(ReturnFloatReg, Address(argv, 0));
+        masm.canonicalizeDouble(ReturnDoubleReg);
+        masm.storeDouble(ReturnDoubleReg, Address(argv, 0));
         break;
     }
 
     // Restore clobbered non-volatile registers of the caller.
     masm.PopRegsInMask(NonVolatileRegs);
 
     JS_ASSERT(masm.framePushed() == 0);
 
@@ -6191,19 +6191,19 @@ FillArgumentArray(ModuleCompiler &m, con
                 masm.load32(src, scratch);
                 masm.storeValue(JSVAL_TYPE_INT32, scratch, dstAddr);
 #else
                 masm.memIntToValue(src, dstAddr);
 #endif
             } else {
                 JS_ASSERT(i.mirType() == MIRType_Double);
                 Address src(StackPointer, offsetToCallerStackArgs + i->offsetFromArgBase());
-                masm.loadDouble(src, ScratchFloatReg);
-                masm.canonicalizeDouble(ScratchFloatReg);
-                masm.storeDouble(ScratchFloatReg, dstAddr);
+                masm.loadDouble(src, ScratchDoubleReg);
+                masm.canonicalizeDouble(ScratchDoubleReg);
+                masm.storeDouble(ScratchDoubleReg, dstAddr);
             }
             break;
         }
     }
 }
 
 static void
 GenerateFFIInterpreterExit(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &exit,
@@ -6296,17 +6296,17 @@ GenerateFFIInterpreterExit(ModuleCompile
       case RetType::Signed:
         masm.callExit(AsmJSImmPtr(AsmJSImm_InvokeFromAsmJS_ToInt32), i.stackBytesConsumedSoFar());
         masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
         masm.unboxInt32(argv, ReturnReg);
         break;
       case RetType::Double:
         masm.callExit(AsmJSImmPtr(AsmJSImm_InvokeFromAsmJS_ToNumber), i.stackBytesConsumedSoFar());
         masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
-        masm.loadDouble(argv, ReturnFloatReg);
+        masm.loadDouble(argv, ReturnDoubleReg);
         break;
       case RetType::Float:
         MOZ_ASSUME_UNREACHABLE("Float32 shouldn't be returned from a FFI");
         break;
     }
 
     // Note: the caller is IonMonkey code which means there are no non-volatile
     // registers to restore.
@@ -6367,17 +6367,17 @@ GenerateOOLConvert(ModuleCompiler &m, Re
       case RetType::Signed:
         masm.callExit(AsmJSImmPtr(AsmJSImm_CoerceInPlace_ToInt32), i.stackBytesConsumedSoFar());
         masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
         masm.unboxInt32(Address(StackPointer, offsetToArgv), ReturnReg);
         break;
       case RetType::Double:
         masm.callExit(AsmJSImmPtr(AsmJSImm_CoerceInPlace_ToNumber), i.stackBytesConsumedSoFar());
         masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
-        masm.loadDouble(Address(StackPointer, offsetToArgv), ReturnFloatReg);
+        masm.loadDouble(Address(StackPointer, offsetToArgv), ReturnDoubleReg);
         break;
       default:
         MOZ_ASSUME_UNREACHABLE("Unsupported convert type");
     }
 }
 
 static void
 GenerateFFIIonExit(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &exit,
@@ -6577,21 +6577,21 @@ GenerateFFIIonExit(ModuleCompiler &m, co
     masm.branchTestMagic(Assembler::Equal, JSReturnOperand, throwLabel);
 #endif
 
     uint32_t oolConvertFramePushed = masm.framePushed();
     switch (exit.sig().retType().which()) {
       case RetType::Void:
         break;
       case RetType::Signed:
-        masm.convertValueToInt32(JSReturnOperand, ReturnFloatReg, ReturnReg, &oolConvert,
+        masm.convertValueToInt32(JSReturnOperand, ReturnDoubleReg, ReturnReg, &oolConvert,
                                  /* -0 check */ false);
         break;
       case RetType::Double:
-        masm.convertValueToDouble(JSReturnOperand, ReturnFloatReg, &oolConvert);
+        masm.convertValueToDouble(JSReturnOperand, ReturnDoubleReg, &oolConvert);
         break;
       case RetType::Float:
         MOZ_ASSUME_UNREACHABLE("Float shouldn't be returned from a FFI");
         break;
     }
 
     masm.bind(&done);
     masm.freeStack(stackDec);
--- a/js/src/jit/BacktrackingAllocator.cpp
+++ b/js/src/jit/BacktrackingAllocator.cpp
@@ -204,17 +204,17 @@ BacktrackingAllocator::canAddToGroup(Vir
 bool
 BacktrackingAllocator::tryGroupRegisters(uint32_t vreg0, uint32_t vreg1)
 {
     // See if reg0 and reg1 can be placed in the same group, following the
     // restrictions imposed by VirtualRegisterGroup and any other registers
     // already grouped with reg0 or reg1.
     BacktrackingVirtualRegister *reg0 = &vregs[vreg0], *reg1 = &vregs[vreg1];
 
-    if (reg0->isFloatReg() != reg1->isFloatReg())
+    if (!reg0->isCompatibleVReg(*reg1))
         return true;
 
     VirtualRegisterGroup *group0 = reg0->group(), *group1 = reg1->group();
 
     if (!group0 && group1)
         return tryGroupRegisters(vreg1, vreg0);
 
     if (group0) {
@@ -698,17 +698,17 @@ bool
 BacktrackingAllocator::tryAllocateGroupRegister(PhysicalRegister &r, VirtualRegisterGroup *group,
                                                 bool *psuccess, bool *pfixed, LiveInterval **pconflicting)
 {
     *psuccess = false;
 
     if (!r.allocatable)
         return true;
 
-    if (r.reg.isFloat() != vregs[group->registers[0]].isFloatReg())
+    if (!vregs[group->registers[0]].isCompatibleReg(r.reg))
         return true;
 
     bool allocatable = true;
     LiveInterval *conflicting = nullptr;
 
     for (size_t i = 0; i < group->registers.length(); i++) {
         VirtualRegister &reg = vregs[group->registers[i]];
         JS_ASSERT(reg.numIntervals() <= 2);
@@ -748,43 +748,47 @@ BacktrackingAllocator::tryAllocateRegist
                                            bool *success, bool *pfixed, LiveInterval **pconflicting)
 {
     *success = false;
 
     if (!r.allocatable)
         return true;
 
     BacktrackingVirtualRegister *reg = &vregs[interval->vreg()];
-    if (reg->isFloatReg() != r.reg.isFloat())
+    if (!reg->isCompatibleReg(r.reg))
         return true;
 
     JS_ASSERT_IF(interval->requirement()->kind() == Requirement::FIXED,
                  interval->requirement()->allocation() == LAllocation(r.reg));
 
     for (size_t i = 0; i < interval->numRanges(); i++) {
         AllocatedRange range(interval, interval->getRange(i)), existing;
-        if (r.allocations.contains(range, &existing)) {
-            if (existing.interval->hasVreg()) {
-                if (IonSpewEnabled(IonSpew_RegAlloc)) {
+        for (int a = 0; a < r.reg.numAliased(); a++) {
+            PhysicalRegister &rAlias = registers[r.reg.aliased(a).code()];
+            if (rAlias.allocations.contains(range, &existing)) {
+                if (existing.interval->hasVreg()) {
+                    if (IonSpewEnabled(IonSpew_RegAlloc)) {
                     IonSpew(IonSpew_RegAlloc, "  %s collides with v%u[%u] %s [weight %lu]",
-                            r.reg.name(), existing.interval->vreg(),
-                            existing.interval->index(),
                             existing.range->toString(),
-                            computeSpillWeight(existing.interval));
+                                rAlias.reg.name(), existing.interval->vreg(),
+                                existing.interval->index(),
+                                existing.range->toString(),
+                                computeSpillWeight(existing.interval));
+                    }
+                    if (!*pconflicting || computeSpillWeight(existing.interval) < computeSpillWeight(*pconflicting))
+                        *pconflicting = existing.interval;
+                } else {
+                    if (IonSpewEnabled(IonSpew_RegAlloc)) {
+                    IonSpew(IonSpew_RegAlloc, "  %s collides with fixed use %s",
+                                rAlias.reg.name(), existing.range->toString());
+                    }
+                    *pfixed = true;
                 }
-                if (!*pconflicting || computeSpillWeight(existing.interval) < computeSpillWeight(*pconflicting))
-                    *pconflicting = existing.interval;
-            } else {
-                if (IonSpewEnabled(IonSpew_RegAlloc)) {
-                    IonSpew(IonSpew_RegAlloc, "  %s collides with fixed use %s",
-                            r.reg.name(), existing.range->toString());
-                }
-                *pfixed = true;
+                return true;
             }
-            return true;
         }
     }
 
     IonSpew(IonSpew_RegAlloc, "  allocated to %s", r.reg.name());
 
     for (size_t i = 0; i < interval->numRanges(); i++) {
         AllocatedRange range(interval, interval->getRange(i));
         if (!r.allocations.insert(range))
@@ -1298,17 +1302,17 @@ BacktrackingAllocator::dumpFixedRanges()
 #endif // DEBUG
 }
 
 #ifdef DEBUG
 struct BacktrackingAllocator::PrintLiveIntervalRange
 {
     bool &first_;
 
-    explicit PrintLiveIntervalRange(bool first) : first_(first) {}
+    explicit PrintLiveIntervalRange(bool &first) : first_(first) {}
 
     void operator()(const AllocatedRange &item)
     {
         if (item.range == item.interval->getRange(0)) {
             if (first_)
                 first_ = false;
             else
                 fprintf(stderr, " /");
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -2887,17 +2887,17 @@ ICBinaryArith_Double::Compiler::generate
       case JSOP_DIV:
         masm.divDouble(FloatReg1, FloatReg0);
         break;
       case JSOP_MOD:
         masm.setupUnalignedABICall(2, R0.scratchReg());
         masm.passABIArg(FloatReg0, MoveOp::DOUBLE);
         masm.passABIArg(FloatReg1, MoveOp::DOUBLE);
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, NumberMod), MoveOp::DOUBLE);
-        JS_ASSERT(ReturnFloatReg == FloatReg0);
+        JS_ASSERT(ReturnDoubleReg == FloatReg0);
         break;
       default:
         MOZ_ASSUME_UNREACHABLE("Unexpected op");
     }
 
     masm.boxDouble(FloatReg0, R0);
     EmitReturnFromIC(masm);
 
@@ -5498,18 +5498,18 @@ ICSetElem_TypedArray::Compiler::generate
     regs.take(scratchReg);
     Register secondScratch = regs.takeAny();
 
     if (type_ == ScalarTypeDescr::TYPE_FLOAT32 || type_ == ScalarTypeDescr::TYPE_FLOAT64) {
         masm.ensureDouble(value, FloatReg0, &failure);
         if (LIRGenerator::allowFloat32Optimizations() &&
             type_ == ScalarTypeDescr::TYPE_FLOAT32)
         {
-            masm.convertDoubleToFloat32(FloatReg0, ScratchFloatReg);
-            masm.storeToTypedFloatArray(type_, ScratchFloatReg, dest);
+            masm.convertDoubleToFloat32(FloatReg0, ScratchFloat32Reg);
+            masm.storeToTypedFloatArray(type_, ScratchFloat32Reg, dest);
         } else {
             masm.storeToTypedFloatArray(type_, FloatReg0, dest);
         }
         EmitReturnFromIC(masm);
     } else if (type_ == ScalarTypeDescr::TYPE_UINT8_CLAMPED) {
         Label notInt32;
         masm.branchTestInt32(Assembler::NotEqual, value, &notInt32);
         masm.unboxInt32(value, secondScratch);
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -322,18 +322,24 @@ CodeGenerator::visitValueToFloat32(LValu
         masm.jump(&done);
     }
 
     masm.bind(&isInt32);
     masm.int32ValueToFloat32(operand, output);
     masm.jump(&done);
 
     masm.bind(&isDouble);
+    // ARM may not have a double register available if we've allocated output as a float32.
+#ifdef JS_CODEGEN_ARM
+    masm.unboxDouble(operand, ScratchDoubleReg);
+    masm.convertDoubleToFloat32(ScratchDoubleReg, output);
+#else
     masm.unboxDouble(operand, output);
     masm.convertDoubleToFloat32(output, output);
+#endif
     masm.bind(&done);
 
     return true;
 }
 
 bool
 CodeGenerator::visitInt32ToDouble(LInt32ToDouble *lir)
 {
@@ -3466,32 +3472,32 @@ bool CodeGenerator::visitAtan2D(LAtan2D 
     FloatRegister y = ToFloatRegister(lir->y());
     FloatRegister x = ToFloatRegister(lir->x());
 
     masm.setupUnalignedABICall(2, temp);
     masm.passABIArg(y, MoveOp::DOUBLE);
     masm.passABIArg(x, MoveOp::DOUBLE);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ecmaAtan2), MoveOp::DOUBLE);
 
-    JS_ASSERT(ToFloatRegister(lir->output()) == ReturnFloatReg);
+    JS_ASSERT(ToFloatRegister(lir->output()) == ReturnDoubleReg);
     return true;
 }
 
 bool 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);
 
-    JS_ASSERT(ToFloatRegister(lir->output()) == ReturnFloatReg);
+    JS_ASSERT(ToFloatRegister(lir->output()) == ReturnDoubleReg);
     return true;
 }
 
 bool
 CodeGenerator::visitNewArray(LNewArray *lir)
 {
     JS_ASSERT(gen->info().executionMode() == SequentialExecution);
     Register objReg = ToRegister(lir->output());
@@ -4407,59 +4413,59 @@ CodeGenerator::visitPowI(LPowI *ins)
     // In all implementations, setupUnalignedABICall() relinquishes use of
     // its scratch register. We can therefore save an input register by
     // reusing the scratch register to pass constants to callWithABI.
     masm.setupUnalignedABICall(2, temp);
     masm.passABIArg(value, MoveOp::DOUBLE);
     masm.passABIArg(power);
 
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, js::powi), MoveOp::DOUBLE);
-    JS_ASSERT(ToFloatRegister(ins->output()) == ReturnFloatReg);
+    JS_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
 
     return true;
 }
 
 bool
 CodeGenerator::visitPowD(LPowD *ins)
 {
     FloatRegister value = ToFloatRegister(ins->value());
     FloatRegister power = ToFloatRegister(ins->power());
     Register temp = ToRegister(ins->temp());
 
     masm.setupUnalignedABICall(2, temp);
     masm.passABIArg(value, MoveOp::DOUBLE);
     masm.passABIArg(power, MoveOp::DOUBLE);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ecmaPow), MoveOp::DOUBLE);
 
-    JS_ASSERT(ToFloatRegister(ins->output()) == ReturnFloatReg);
+    JS_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
     return true;
 }
 
 bool
 CodeGenerator::visitRandom(LRandom *ins)
 {
     Register temp = ToRegister(ins->temp());
     Register temp2 = ToRegister(ins->temp2());
 
     masm.loadJSContext(temp);
 
     masm.setupUnalignedABICall(1, temp2);
     masm.passABIArg(temp);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, math_random_no_outparam), MoveOp::DOUBLE);
 
-    JS_ASSERT(ToFloatRegister(ins->output()) == ReturnFloatReg);
+    JS_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
     return true;
 }
 
 bool
 CodeGenerator::visitMathFunctionD(LMathFunctionD *ins)
 {
     Register temp = ToRegister(ins->temp());
     FloatRegister input = ToFloatRegister(ins->input());
-    JS_ASSERT(ToFloatRegister(ins->output()) == ReturnFloatReg);
+    JS_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
 
     const MathCache *mathCache = ins->mir()->cache();
 
     masm.setupUnalignedABICall(mathCache ? 2 : 1, temp);
     if (mathCache) {
         masm.movePtr(ImmPtr(mathCache), temp);
         masm.passABIArg(temp);
     }
@@ -4551,17 +4557,17 @@ CodeGenerator::visitMathFunctionD(LMathF
     return true;
 }
 
 bool
 CodeGenerator::visitMathFunctionF(LMathFunctionF *ins)
 {
     Register temp = ToRegister(ins->temp());
     FloatRegister input = ToFloatRegister(ins->input());
-    JS_ASSERT(ToFloatRegister(ins->output()) == ReturnFloatReg);
+    JS_ASSERT(ToFloatRegister(ins->output()) == ReturnFloat32Reg);
 
     masm.setupUnalignedABICall(1, temp);
     masm.passABIArg(input, MoveOp::FLOAT32);
 
     void *funptr = nullptr;
     switch (ins->mir()->function()) {
       case MMathFunction::Floor: funptr = JS_FUNC_TO_DATA_PTR(void *, floorf);           break;
       case MMathFunction::Round: funptr = JS_FUNC_TO_DATA_PTR(void *, math_roundf_impl); break;
@@ -4576,17 +4582,17 @@ CodeGenerator::visitMathFunctionF(LMathF
 
 bool
 CodeGenerator::visitModD(LModD *ins)
 {
     FloatRegister lhs = ToFloatRegister(ins->lhs());
     FloatRegister rhs = ToFloatRegister(ins->rhs());
     Register temp = ToRegister(ins->temp());
 
-    JS_ASSERT(ToFloatRegister(ins->output()) == ReturnFloatReg);
+    JS_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
 
     masm.setupUnalignedABICall(2, temp);
     masm.passABIArg(lhs, MoveOp::DOUBLE);
     masm.passABIArg(rhs, MoveOp::DOUBLE);
 
     if (gen->compilingAsmJS())
         masm.callWithABI(AsmJSImm_ModD, MoveOp::DOUBLE);
     else
@@ -7100,29 +7106,30 @@ CodeGenerator::addSetPropertyCache(LInst
       }
       default:
         MOZ_ASSUME_UNREACHABLE("Bad execution mode");
     }
 }
 
 bool
 CodeGenerator::addSetElementCache(LInstruction *ins, Register obj, Register unboxIndex,
-                                  Register temp, FloatRegister tempFloat, ValueOperand index,
+                                  Register temp, FloatRegister tempDouble,
+                                  FloatRegister tempFloat32, ValueOperand index,
                                   ConstantOrRegister value, bool strict, bool guardHoles,
                                   jsbytecode *profilerLeavePc)
 {
     switch (gen->info().executionMode()) {
       case SequentialExecution: {
-        SetElementIC cache(obj, unboxIndex, temp, tempFloat, index, value, strict,
+          SetElementIC cache(obj, unboxIndex, temp, tempDouble, tempFloat32, index, value, strict,
                            guardHoles);
         cache.setProfilerLeavePC(profilerLeavePc);
         return addCache(ins, allocateCache(cache));
       }
       case ParallelExecution: {
-        SetElementParIC cache(obj, unboxIndex, temp, tempFloat, index, value, strict,
+          SetElementParIC cache(obj, unboxIndex, temp, tempDouble, tempFloat32, index, value, strict,
                               guardHoles);
         cache.setProfilerLeavePC(profilerLeavePc);
         return addCache(ins, allocateCache(cache));
       }
       default:
         MOZ_ASSUME_UNREACHABLE("Bad execution mode");
     }
 }
@@ -7272,41 +7279,43 @@ CodeGenerator::visitGetElementIC(OutOfLi
 }
 
 bool
 CodeGenerator::visitSetElementCacheV(LSetElementCacheV *ins)
 {
     Register obj = ToRegister(ins->object());
     Register unboxIndex = ToTempUnboxRegister(ins->tempToUnboxIndex());
     Register temp = ToRegister(ins->temp());
-    FloatRegister tempFloat = ToFloatRegister(ins->tempFloat());
+    FloatRegister tempDouble = ToFloatRegister(ins->tempDouble());
+    FloatRegister tempFloat32 = ToFloatRegister(ins->tempFloat32());
     ValueOperand index = ToValue(ins, LSetElementCacheV::Index);
     ConstantOrRegister value = TypedOrValueRegister(ToValue(ins, LSetElementCacheV::Value));
 
-    return addSetElementCache(ins, obj, unboxIndex, temp, tempFloat, index, value,
+    return addSetElementCache(ins, obj, unboxIndex, temp, tempDouble, tempFloat32, index, value,
                               ins->mir()->strict(), ins->mir()->guardHoles(),
                               ins->mir()->profilerLeavePc());
 }
 
 bool
 CodeGenerator::visitSetElementCacheT(LSetElementCacheT *ins)
 {
     Register obj = ToRegister(ins->object());
     Register unboxIndex = ToTempUnboxRegister(ins->tempToUnboxIndex());
     Register temp = ToRegister(ins->temp());
-    FloatRegister tempFloat = ToFloatRegister(ins->tempFloat());
+    FloatRegister tempDouble = ToFloatRegister(ins->tempDouble());
+    FloatRegister tempFloat32 = ToFloatRegister(ins->tempFloat32());
     ValueOperand index = ToValue(ins, LSetElementCacheT::Index);
     ConstantOrRegister value;
     const LAllocation *tmp = ins->value();
     if (tmp->isConstant())
         value = *tmp->toConstant();
     else
         value = TypedOrValueRegister(ins->mir()->value()->type(), ToAnyRegister(tmp));
 
-    return addSetElementCache(ins, obj, unboxIndex, temp, tempFloat, index, value,
+    return addSetElementCache(ins, obj, unboxIndex, temp, tempDouble, tempFloat32, index, value,
                               ins->mir()->strict(), ins->mir()->guardHoles(),
                               ins->mir()->profilerLeavePc());
 }
 
 typedef bool (*SetElementICFn)(JSContext *, size_t, HandleObject, HandleValue, HandleValue);
 const VMFunction SetElementIC::UpdateInfo =
     FunctionInfo<SetElementICFn>(SetElementIC::update);
 
@@ -8711,21 +8720,26 @@ CodeGenerator::visitAssertRangeD(LAssert
     return emitAssertRangeD(r, input, temp);
 }
 
 bool
 CodeGenerator::visitAssertRangeF(LAssertRangeF *ins)
 {
     FloatRegister input = ToFloatRegister(ins->input());
     FloatRegister temp = ToFloatRegister(ins->temp());
+    FloatRegister dest = input;
+    if (hasMultiAlias())
+        dest = ToFloatRegister(ins->armtemp());
+
     const Range *r = ins->range();
 
-    masm.convertFloat32ToDouble(input, input);
-    bool success = emitAssertRangeD(r, input, temp);
-    masm.convertDoubleToFloat32(input, input);
+    masm.convertFloat32ToDouble(input, dest);
+    bool success = emitAssertRangeD(r, dest, temp);
+    if (dest == input)
+        masm.convertDoubleToFloat32(input, input);
     return success;
 }
 
 bool
 CodeGenerator::visitAssertRangeV(LAssertRangeV *ins)
 {
     const Range *r = ins->range();
     const ValueOperand value = ToValue(ins, LAssertRangeV::Input);
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -364,17 +364,18 @@ class CodeGenerator : public CodeGenerat
                              bool monitoredResult, jsbytecode *profilerLeavePc);
     bool addGetElementCache(LInstruction *ins, Register obj, ConstantOrRegister index,
                             TypedOrValueRegister output, bool monitoredResult,
                             bool allowDoubleResult, jsbytecode *profilerLeavePc);
     bool addSetPropertyCache(LInstruction *ins, RegisterSet liveRegs, Register objReg,
                              PropertyName *name, ConstantOrRegister value, bool strict,
                              bool needsTypeBarrier, jsbytecode *profilerLeavePc);
     bool addSetElementCache(LInstruction *ins, Register obj, Register unboxIndex, Register temp,
-                            FloatRegister tempFloat, ValueOperand index, ConstantOrRegister value,
+                            FloatRegister tempDouble, FloatRegister tempFloat32,
+                            ValueOperand index, ConstantOrRegister value,
                             bool strict, bool guardHoles, jsbytecode *profilerLeavePc);
 
     bool generateBranchV(const ValueOperand &value, Label *ifTrue, Label *ifFalse, FloatRegister fr);
 
     bool emitAllocateGCThingPar(LInstruction *lir, Register objReg, Register cxReg,
                                 Register tempReg1, Register tempReg2,
                                 JSObject *templateObj);
 
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -1900,17 +1900,19 @@ IonCompile(JSContext *cx, JSScript *scri
 
     RootedScript builderScript(cx, builder->script());
 
     if (recompile) {
         JS_ASSERT(executionMode == SequentialExecution);
         builderScript->ionScript()->setRecompiling();
     }
 
-    IonSpewNewFunction(graph, builderScript);
+#ifdef DEBUG
+    IonSpewFunction ionSpewFunction(graph, builderScript);
+#endif
 
     bool succeeded = builder->build();
     builder->clearForBackEnd();
 
     if (!succeeded)
         return builder->abortReason();
 
     // If possible, compile the script off thread.
@@ -1936,18 +1938,16 @@ IonCompile(JSContext *cx, JSScript *scri
     ScopedJSDeletePtr<CodeGenerator> codegen(CompileBackEnd(builder));
     if (!codegen) {
         IonSpew(IonSpew_Abort, "Failed during back-end compilation.");
         return AbortReason_Disable;
     }
 
     bool success = codegen->link(cx, builder->constraints());
 
-    IonSpewEndFunction();
-
     return success ? AbortReason_NoAbort : AbortReason_Disable;
 }
 
 static bool
 CheckFrame(BaselineFrame *frame)
 {
     JS_ASSERT(!frame->isGeneratorFrame());
     JS_ASSERT(!frame->isDebuggerFrame());
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -3568,22 +3568,22 @@ StoreDenseElement(MacroAssembler &masm, 
                       &convert);
     masm.bind(&storeValue);
     masm.storeTypedOrValue(reg, target);
     masm.jump(&done);
 
     masm.bind(&convert);
     if (reg.hasValue()) {
         masm.branchTestInt32(Assembler::NotEqual, reg.valueReg(), &storeValue);
-        masm.int32ValueToDouble(reg.valueReg(), ScratchFloatReg);
-        masm.storeDouble(ScratchFloatReg, target);
+        masm.int32ValueToDouble(reg.valueReg(), ScratchDoubleReg);
+        masm.storeDouble(ScratchDoubleReg, target);
     } else {
         JS_ASSERT(reg.type() == MIRType_Int32);
-        masm.convertInt32ToDouble(reg.typedReg().gpr(), ScratchFloatReg);
-        masm.storeDouble(ScratchFloatReg, target);
+        masm.convertInt32ToDouble(reg.typedReg().gpr(), ScratchDoubleReg);
+        masm.storeDouble(ScratchDoubleReg, target);
     }
 
     masm.bind(&done);
 }
 
 static bool
 GenerateSetDenseElement(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
                         JSObject *obj, const Value &idval, bool guardHoles, Register object,
@@ -3697,17 +3697,18 @@ SetElementIC::attachDenseElement(JSConte
                             "dense array";
     return linkAndAttachStub(cx, masm, attacher, ion, message);
 }
 
 static bool
 GenerateSetTypedArrayElement(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
                              HandleTypedArrayObject tarr, Register object,
                              ValueOperand indexVal, ConstantOrRegister value,
-                             Register tempUnbox, Register temp, FloatRegister tempFloat)
+                             Register tempUnbox, Register temp, FloatRegister tempDouble,
+                             FloatRegister tempFloat32)
 {
     Label failures, done, popObjectAndFail;
 
     // Guard on the shape.
     Shape *shape = tarr->lastProperty();
     if (!shape)
         return false;
     masm.branchTestObjShape(Assembler::NotEqual, object, shape, &failures);
@@ -3726,42 +3727,46 @@ GenerateSetTypedArrayElement(JSContext *
     masm.loadPtr(Address(object, TypedArrayObject::dataOffset()), elements);
 
     // Set the value.
     int arrayType = tarr->type();
     int width = TypedArrayObject::slotWidth(arrayType);
     BaseIndex target(elements, index, ScaleFromElemWidth(width));
 
     if (arrayType == ScalarTypeDescr::TYPE_FLOAT32) {
+        FloatRegister ftemp;
         if (LIRGenerator::allowFloat32Optimizations()) {
-            if (!masm.convertConstantOrRegisterToFloat(cx, value, tempFloat, &failures))
-                return false;
-        } else {
-            if (!masm.convertConstantOrRegisterToDouble(cx, value, tempFloat, &failures))
+            JS_ASSERT(tempFloat32 != InvalidFloatReg);
+            if (!masm.convertConstantOrRegisterToFloat(cx, value, tempFloat32, &failures))
                 return false;
+            ftemp = tempFloat32;
+        } else {
+            if (!masm.convertConstantOrRegisterToDouble(cx, value, tempDouble, &failures))
+                return false;
+            ftemp = tempDouble;
         }
-        masm.storeToTypedFloatArray(arrayType, tempFloat, target);
+        masm.storeToTypedFloatArray(arrayType, ftemp, target);
     } else if (arrayType == ScalarTypeDescr::TYPE_FLOAT64) {
-        if (!masm.convertConstantOrRegisterToDouble(cx, value, tempFloat, &failures))
+        if (!masm.convertConstantOrRegisterToDouble(cx, value, tempDouble, &failures))
             return false;
-        masm.storeToTypedFloatArray(arrayType, tempFloat, target);
+        masm.storeToTypedFloatArray(arrayType, tempDouble, target);
     } else {
         // On x86 we only have 6 registers available to use, so reuse the object
         // register to compute the intermediate value to store and restore it
         // afterwards.
         masm.push(object);
 
         if (arrayType == ScalarTypeDescr::TYPE_UINT8_CLAMPED) {
-            if (!masm.clampConstantOrRegisterToUint8(cx, value, tempFloat, object,
+            if (!masm.clampConstantOrRegisterToUint8(cx, value, tempDouble, object,
                                                      &popObjectAndFail))
             {
                 return false;
             }
         } else {
-            if (!masm.truncateConstantOrRegisterToInt32(cx, value, tempFloat, object,
+            if (!masm.truncateConstantOrRegisterToInt32(cx, value, tempDouble, object,
                                                         &popObjectAndFail))
             {
                 return false;
             }
         }
         masm.storeToTypedIntArray(arrayType, object, target);
 
         masm.pop(object);
@@ -3784,17 +3789,17 @@ GenerateSetTypedArrayElement(JSContext *
 bool
 SetElementIC::attachTypedArrayElement(JSContext *cx, HandleScript outerScript, IonScript *ion,
                                       HandleTypedArrayObject tarr)
 {
     MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
     RepatchStubAppender attacher(*this);
     if (!GenerateSetTypedArrayElement(cx, masm, attacher, tarr,
                                       object(), index(), value(),
-                                      tempToUnboxIndex(), temp(), tempFloat()))
+                                      tempToUnboxIndex(), temp(), tempDouble(), tempFloat32()))
     {
         return false;
     }
 
     return linkAndAttachStub(cx, masm, attacher, ion, "typed array");
 }
 
 bool
@@ -3855,17 +3860,17 @@ SetElementParIC::attachDenseElement(Lock
 bool
 SetElementParIC::attachTypedArrayElement(LockedJSContext &cx, IonScript *ion,
                                          HandleTypedArrayObject tarr)
 {
     MacroAssembler masm(cx, ion);
     DispatchStubPrepender attacher(*this);
     if (!GenerateSetTypedArrayElement(cx, masm, attacher, tarr,
                                       object(), index(), value(),
-                                      tempToUnboxIndex(), temp(), tempFloat()))
+                                      tempToUnboxIndex(), temp(), tempDouble(), tempFloat32()))
     {
         return false;
     }
 
     return linkAndAttachStub(cx, masm, attacher, ion, "parallel typed array");
 }
 
 bool
--- a/js/src/jit/IonCaches.h
+++ b/js/src/jit/IonCaches.h
@@ -846,32 +846,35 @@ class GetElementIC : public RepatchIonCa
 };
 
 class SetElementIC : public RepatchIonCache
 {
   protected:
     Register object_;
     Register tempToUnboxIndex_;
     Register temp_;
-    FloatRegister tempFloat_;
+    FloatRegister tempDouble_;
+    FloatRegister tempFloat32_;
     ValueOperand index_;
     ConstantOrRegister value_;
     bool strict_;
     bool guardHoles_;
 
     bool hasDenseStub_ : 1;
 
   public:
     SetElementIC(Register object, Register tempToUnboxIndex, Register temp,
-                 FloatRegister tempFloat, ValueOperand index, ConstantOrRegister value,
+                 FloatRegister tempDouble, FloatRegister tempFloat32,
+                 ValueOperand index, ConstantOrRegister value,
                  bool strict, bool guardHoles)
       : object_(object),
         tempToUnboxIndex_(tempToUnboxIndex),
         temp_(temp),
-        tempFloat_(tempFloat),
+        tempDouble_(tempDouble),
+        tempFloat32_(tempFloat32),
         index_(index),
         value_(value),
         strict_(strict),
         guardHoles_(guardHoles),
         hasDenseStub_(false)
     {
     }
 
@@ -883,18 +886,21 @@ class SetElementIC : public RepatchIonCa
         return object_;
     }
     Register tempToUnboxIndex() const {
         return tempToUnboxIndex_;
     }
     Register temp() const {
         return temp_;
     }
-    FloatRegister tempFloat() const {
-        return tempFloat_;
+    FloatRegister tempDouble() const {
+        return tempDouble_;
+    }
+    FloatRegister tempFloat32() const {
+        return tempFloat32_;
     }
     ValueOperand index() const {
         return index_;
     }
     ConstantOrRegister value() const {
         return value_;
     }
     bool strict() const {
@@ -1242,30 +1248,32 @@ class SetPropertyParIC : public Parallel
 };
 
 class SetElementParIC : public ParallelIonCache
 {
   protected:
     Register object_;
     Register tempToUnboxIndex_;
     Register temp_;
-    FloatRegister tempFloat_;
+    FloatRegister tempDouble_;
+    FloatRegister tempFloat32_;
     ValueOperand index_;
     ConstantOrRegister value_;
     bool strict_;
     bool guardHoles_;
 
   public:
     SetElementParIC(Register object, Register tempToUnboxIndex, Register temp,
-                    FloatRegister tempFloat, ValueOperand index, ConstantOrRegister value,
+                    FloatRegister tempDouble, FloatRegister tempFloat32, ValueOperand index, ConstantOrRegister value,
                     bool strict, bool guardHoles)
       : object_(object),
         tempToUnboxIndex_(tempToUnboxIndex),
         temp_(temp),
-        tempFloat_(tempFloat),
+        tempDouble_(tempDouble),
+        tempFloat32_(tempFloat32),
         index_(index),
         value_(value),
         strict_(strict),
         guardHoles_(guardHoles)
     {
     }
 
     CACHE_HEADER(SetElementPar)
@@ -1280,18 +1288,21 @@ class SetElementParIC : public ParallelI
         return object_;
     }
     Register tempToUnboxIndex() const {
         return tempToUnboxIndex_;
     }
     Register temp() const {
         return temp_;
     }
-    FloatRegister tempFloat() const {
-        return tempFloat_;
+    FloatRegister tempDouble() const {
+        return tempDouble_;
+    }
+    FloatRegister tempFloat32() const {
+        return tempFloat32_;
     }
     ValueOperand index() const {
         return index_;
     }
     ConstantOrRegister value() const {
         return value_;
     }
     bool strict() const {
--- a/js/src/jit/IonMacroAssembler.cpp
+++ b/js/src/jit/IonMacroAssembler.cpp
@@ -279,18 +279,18 @@ StoreToTypedFloatArray(MacroAssembler &m
       case ScalarTypeDescr::TYPE_FLOAT32:
         if (LIRGenerator::allowFloat32Optimizations()) {
             masm.storeFloat32(value, dest);
         } else {
 #ifdef JS_MORE_DETERMINISTIC
             // See the comment in TypedArrayObjectTemplate::doubleToNative.
             masm.canonicalizeDouble(value);
 #endif
-            masm.convertDoubleToFloat32(value, ScratchFloatReg);
-            masm.storeFloat32(ScratchFloatReg, dest);
+            masm.convertDoubleToFloat32(value, ScratchFloat32Reg);
+            masm.storeFloat32(ScratchFloat32Reg, dest);
         }
         break;
       case ScalarTypeDescr::TYPE_FLOAT64:
 #ifdef JS_MORE_DETERMINISTIC
         // See the comment in TypedArrayObjectTemplate::doubleToNative.
         masm.canonicalizeDouble(value);
 #endif
         masm.storeDouble(value, dest);
@@ -395,37 +395,37 @@ MacroAssembler::loadFromTypedArray(int a
             Label done, isDouble;
             branchTest32(Assembler::Signed, temp, temp, &isDouble);
             {
                 tagValue(JSVAL_TYPE_INT32, temp, dest);
                 jump(&done);
             }
             bind(&isDouble);
             {
-                convertUInt32ToDouble(temp, ScratchFloatReg);
-                boxDouble(ScratchFloatReg, dest);
+                convertUInt32ToDouble(temp, ScratchDoubleReg);
+                boxDouble(ScratchDoubleReg, dest);
             }
             bind(&done);
         } else {
             // Bailout if the value does not fit in an int32.
             branchTest32(Assembler::Signed, temp, temp, fail);
             tagValue(JSVAL_TYPE_INT32, temp, dest);
         }
         break;
       case ScalarTypeDescr::TYPE_FLOAT32:
-        loadFromTypedArray(arrayType, src, AnyRegister(ScratchFloatReg), dest.scratchReg(),
+        loadFromTypedArray(arrayType, src, AnyRegister(ScratchFloat32Reg), dest.scratchReg(),
                            nullptr);
         if (LIRGenerator::allowFloat32Optimizations())
-            convertFloat32ToDouble(ScratchFloatReg, ScratchFloatReg);
-        boxDouble(ScratchFloatReg, dest);
+            convertFloat32ToDouble(ScratchFloat32Reg, ScratchDoubleReg);
+        boxDouble(ScratchDoubleReg, dest);
         break;
       case ScalarTypeDescr::TYPE_FLOAT64:
-        loadFromTypedArray(arrayType, src, AnyRegister(ScratchFloatReg), dest.scratchReg(),
+        loadFromTypedArray(arrayType, src, AnyRegister(ScratchDoubleReg), dest.scratchReg(),
                            nullptr);
-        boxDouble(ScratchFloatReg, dest);
+        boxDouble(ScratchDoubleReg, dest);
         break;
       default:
         MOZ_ASSUME_UNREACHABLE("Invalid typed array type");
     }
 }
 
 template void MacroAssembler::loadFromTypedArray(int arrayType, const Address &src, const ValueOperand &dest,
                                                  bool allowDouble, Register temp, Label *fail);
@@ -1506,18 +1506,18 @@ MacroAssembler::tracelogStop(Register lo
 }
 #endif
 
 void
 MacroAssembler::convertInt32ValueToDouble(const Address &address, Register scratch, Label *done)
 {
     branchTestInt32(Assembler::NotEqual, address, done);
     unboxInt32(address, scratch);
-    convertInt32ToDouble(scratch, ScratchFloatReg);
-    storeDouble(ScratchFloatReg, address);
+    convertInt32ToDouble(scratch, ScratchDoubleReg);
+    storeDouble(ScratchDoubleReg, address);
 }
 
 void
 MacroAssembler::convertValueToFloatingPoint(ValueOperand value, FloatRegister output,
                                             Label *fail, MIRType outputType)
 {
     Register tag = splitTagForTest(value);
 
--- a/js/src/jit/IonMacroAssembler.h
+++ b/js/src/jit/IonMacroAssembler.h
@@ -414,18 +414,18 @@ class MacroAssembler : public MacroAssem
 
     template <typename T>
     void storeTypedOrValue(TypedOrValueRegister src, const T &dest) {
         if (src.hasValue()) {
             storeValue(src.valueReg(), dest);
         } else if (IsFloatingPointType(src.type())) {
             FloatRegister reg = src.typedReg().fpu();
             if (src.type() == MIRType_Float32) {
-                convertFloat32ToDouble(reg, ScratchFloatReg);
-                reg = ScratchFloatReg;
+                convertFloat32ToDouble(reg, ScratchDoubleReg);
+                reg = ScratchDoubleReg;
             }
             storeDouble(reg, dest);
         } else {
             storeValue(ValueTypeFromMIRType(src.type()), src.typedReg().gpr(), dest);
         }
     }
 
     template <typename T>
@@ -437,18 +437,18 @@ class MacroAssembler : public MacroAssem
     }
 
     void storeCallResult(Register reg) {
         if (reg != ReturnReg)
             mov(ReturnReg, reg);
     }
 
     void storeCallFloatResult(FloatRegister reg) {
-        if (reg != ReturnFloatReg)
-            moveDouble(ReturnFloatReg, reg);
+        if (reg != ReturnDoubleReg)
+            moveDouble(ReturnDoubleReg, reg);
     }
 
     void storeCallResultValue(AnyRegister dest) {
 #if defined(JS_NUNBOX32)
         unboxValue(ValueOperand(JSReturnReg_Type, JSReturnReg_Data), dest);
 #elif defined(JS_PUNBOX64)
         unboxValue(ValueOperand(JSReturnReg), dest);
 #else
@@ -560,18 +560,18 @@ class MacroAssembler : public MacroAssem
     }
 
     void Push(TypedOrValueRegister v) {
         if (v.hasValue()) {
             Push(v.valueReg());
         } else if (IsFloatingPointType(v.type())) {
             FloatRegister reg = v.typedReg().fpu();
             if (v.type() == MIRType_Float32) {
-                convertFloat32ToDouble(reg, ScratchFloatReg);
-                reg = ScratchFloatReg;
+                convertFloat32ToDouble(reg, ScratchDoubleReg);
+                reg = ScratchDoubleReg;
             }
             Push(reg);
         } else {
             Push(ValueTypeFromMIRType(v.type()), v.typedReg().gpr());
         }
     }
 
     void Push(ConstantOrRegister v) {
--- a/js/src/jit/IonSpewer.cpp
+++ b/js/src/jit/IonSpewer.cpp
@@ -420,10 +420,20 @@ jit::EnableChannel(IonSpewChannel channe
 
 void
 jit::DisableChannel(IonSpewChannel channel)
 {
     JS_ASSERT(LoggingChecked);
     LoggingBits &= ~(1 << uint32_t(channel));
 }
 
+IonSpewFunction::IonSpewFunction(MIRGraph *graph, JS::HandleScript function)
+{
+    IonSpewNewFunction(graph, function);
+}
+
+IonSpewFunction::~IonSpewFunction()
+{
+    IonSpewEndFunction();
+}
+
 #endif /* DEBUG */
 
--- a/js/src/jit/IonSpewer.h
+++ b/js/src/jit/IonSpewer.h
@@ -110,16 +110,23 @@ class IonSpewer
     bool init();
     void beginFunction(MIRGraph *graph, JS::HandleScript);
     bool isSpewingFunction() const;
     void spewPass(const char *pass);
     void spewPass(const char *pass, LinearScanAllocator *ra);
     void endFunction();
 };
 
+class IonSpewFunction
+{
+  public:
+    IonSpewFunction(MIRGraph *graph, JS::HandleScript function);
+    ~IonSpewFunction();
+};
+
 void IonSpewNewFunction(MIRGraph *graph, JS::HandleScript function);
 void IonSpewPass(const char *pass);
 void IonSpewPass(const char *pass, LinearScanAllocator *ra);
 void IonSpewEndFunction();
 
 void CheckLogging();
 extern FILE *IonSpewFile;
 void IonSpew(IonSpewChannel channel, const char *fmt, ...);
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -5086,65 +5086,75 @@ class LSetPropertyCacheT : public LInstr
         return StringFromMIRType(valueType_);
     }
 
     const LDefinition *tempForDispatchCache() {
         return getTemp(1);
     }
 };
 
-class LSetElementCacheV : public LInstructionHelper<0, 1 + 2 * BOX_PIECES, 3>
+class LSetElementCacheV : public LInstructionHelper<0, 1 + 2 * BOX_PIECES, 4>
 {
   public:
     LIR_HEADER(SetElementCacheV);
 
     static const size_t Index = 1;
     static const size_t Value = 1 + BOX_PIECES;
 
     LSetElementCacheV(const LAllocation &object, const LDefinition &tempToUnboxIndex,
-                      const LDefinition &temp, const LDefinition &tempFloat)
+                      const LDefinition &temp, const LDefinition &tempDouble,
+                      const LDefinition &tempFloat32)
     {
         setOperand(0, object);
         setTemp(0, tempToUnboxIndex);
         setTemp(1, temp);
-        setTemp(2, tempFloat);
+        setTemp(2, tempDouble);
+        setTemp(3, tempFloat32);
     }
     const MSetElementCache *mir() const {
         return mir_->toSetElementCache();
     }
 
     const LAllocation *object() {
         return getOperand(0);
     }
     const LDefinition *tempToUnboxIndex() {
         return getTemp(0);
     }
     const LDefinition *temp() {
         return getTemp(1);
     }
-    const LDefinition *tempFloat() {
+    const LDefinition *tempDouble() {
         return getTemp(2);
     }
-};
-
-class LSetElementCacheT : public LInstructionHelper<0, 2 + BOX_PIECES, 3>
+    const LDefinition *tempFloat32() {
+        if (hasUnaliasedDouble())
+            return getTemp(3);
+        return getTemp(2);
+    }
+
+};
+
+class LSetElementCacheT : public LInstructionHelper<0, 2 + BOX_PIECES, 4>
 {
   public:
     LIR_HEADER(SetElementCacheT);
 
     static const size_t Index = 2;
 
     LSetElementCacheT(const LAllocation &object, const LAllocation &value,
                       const LDefinition &tempToUnboxIndex,
-                      const LDefinition &temp, const LDefinition &tempFloat) {
+                      const LDefinition &temp, const LDefinition &tempDouble,
+                      const LDefinition &tempFloat32) {
         setOperand(0, object);
         setOperand(1, value);
         setTemp(0, tempToUnboxIndex);
         setTemp(1, temp);
-        setTemp(2, tempFloat);
+        setTemp(2, tempDouble);
+        setTemp(3, tempFloat32);
     }
     const MSetElementCache *mir() const {
         return mir_->toSetElementCache();
     }
 
     const LAllocation *object() {
         return getOperand(0);
     }
@@ -5152,19 +5162,25 @@ class LSetElementCacheT : public LInstru
         return getOperand(1);
     }
     const LDefinition *tempToUnboxIndex() {
         return getTemp(0);
     }
     const LDefinition *temp() {
         return getTemp(1);
     }
-    const LDefinition *tempFloat() {
+    const LDefinition *tempDouble() {
         return getTemp(2);
     }
+    const LDefinition *tempFloat32() {
+        if (hasUnaliasedDouble())
+            return getTemp(3);
+        return getTemp(2);
+    }
+
 };
 
 class LCallIteratorStart : public LCallInstructionHelper<1, 1, 0>
 {
   public:
     LIR_HEADER(CallIteratorStart)
 
     explicit LCallIteratorStart(const LAllocation &object) {
@@ -6020,24 +6036,27 @@ class LAssertRangeD : public LInstructio
     MAssertRange *mir() {
         return mir_->toAssertRange();
     }
     const Range *range() {
         return mir()->assertedRange();
     }
 };
 
-class LAssertRangeF : public LInstructionHelper<0, 1, 1>
+class LAssertRangeF : public LInstructionHelper<0, 1, 2>
 {
   public:
     LIR_HEADER(AssertRangeF)
-
-    LAssertRangeF(const LAllocation &input, const LDefinition &temp) {
+    LAssertRangeF(const LAllocation &input, const LDefinition &temp, const LDefinition &armtemp) {
         setOperand(0, input);
         setTemp(0, temp);
+        setTemp(1, armtemp);
+    }
+    const LDefinition *armtemp() {
+        return getTemp(1);
     }
 
     const LAllocation *input() {
         return getOperand(0);
     }
 
     const LDefinition *temp() {
         return getTemp(0);
--- a/js/src/jit/LIR.cpp
+++ b/js/src/jit/LIR.cpp
@@ -329,16 +329,24 @@ LInstruction::printName(FILE *fp, Opcode
 }
 
 void
 LInstruction::printName(FILE *fp)
 {
     printName(fp, op());
 }
 
+bool
+LAllocation::aliases(const LAllocation &other) const
+{
+    if (isFloatReg() && other.isFloatReg())
+        return toFloatReg()->reg().aliases(other.toFloatReg()->reg());
+    return *this == other;
+}
+
 #ifdef DEBUG
 static const char * const TypeChars[] =
 {
     "g",            // GENERAL
     "i",            // INT32
     "o",            // OBJECT
     "s",            // SLOTS
     "f",            // FLOAT32
--- a/js/src/jit/LIR.h
+++ b/js/src/jit/LIR.h
@@ -190,18 +190,19 @@ class LAllocation : public TempObject
         return bits_;
     }
 
 #ifdef DEBUG
     const char *toString() const;
 #else
     const char *toString() const { return "???"; }
 #endif
+    bool aliases(const LAllocation &other) const;
+    void dump() const;
 
-    void dump() const;
 };
 
 class LUse : public LAllocation
 {
     static const uint32_t POLICY_BITS = 3;
     static const uint32_t POLICY_SHIFT = 0;
     static const uint32_t POLICY_MASK = (1 << POLICY_BITS) - 1;
     static const uint32_t REG_BITS = 5;
@@ -476,16 +477,38 @@ class LDefinition
     }
 
     Policy policy() const {
         return (Policy)((bits_ >> POLICY_SHIFT) & POLICY_MASK);
     }
     Type type() const {
         return (Type)((bits_ >> TYPE_SHIFT) & TYPE_MASK);
     }
+    bool isCompatibleReg(const AnyRegister &r) const {
+        if (isFloatReg() && r.isFloat()) {
+#if defined(JS_CODEGEN_ARM) && defined(EVERYONE_KNOWS_ABOUT_ALIASING)
+            if (type() == FLOAT32)
+                return r.fpu().isSingle();
+            return r.fpu().isDouble();
+#else
+            return true;
+#endif
+        }
+        return !isFloatReg() && !r.isFloat();
+    }
+    bool isCompatibleDef(const LDefinition &other) const {
+#ifdef JS_CODEGEN_ARM
+        if (isFloatReg() && other.isFloatReg())
+            return type() == other.type();
+        return !isFloatReg() && !other.isFloatReg();
+#else
+        return isFloatReg() == other.isFloatReg();
+#endif
+    }
+
     bool isFloatReg() const {
         return type() == FLOAT32 || type() == DOUBLE;
     }
     uint32_t virtualRegister() const {
         return (bits_ >> VREG_SHIFT) & VREG_MASK;
     }
     LAllocation *output() {
         return &output_;
--- a/js/src/jit/LinearScan.cpp
+++ b/js/src/jit/LinearScan.cpp
@@ -182,17 +182,17 @@ LinearScanAllocator::allocateRegisters()
         CodePosition bestNextUsed;
         bestCode = findBestBlockedRegister(&bestNextUsed);
         if (bestCode != AnyRegister::Invalid &&
             (req->kind() == Requirement::REGISTER || hint->pos() < bestNextUsed))
         {
             AnyRegister best = AnyRegister::FromCode(bestCode);
             IonSpew(IonSpew_RegAlloc, "  Decided best register was %s", best.name());
 
-            if (!splitBlockingIntervals(LAllocation(best)))
+            if (!splitBlockingIntervals(best))
                 return false;
             if (!assign(LAllocation(best)))
                 return false;
 
             continue;
         }
 
         IonSpew(IonSpew_RegAlloc, "  No registers available to spill");
@@ -681,65 +681,72 @@ LinearScanAllocator::splitInterval(LiveI
     // popped from the unhandled queue. Therefore the split will likely be
     // closer to the lower start positions in the queue.
     unhandled.enqueueBackward(newInterval);
 
     return true;
 }
 
 bool
-LinearScanAllocator::splitBlockingIntervals(LAllocation allocation)
+LinearScanAllocator::splitBlockingIntervals(AnyRegister allocatedReg)
 {
-    JS_ASSERT(allocation.isRegister());
 
     // Split current before the next fixed use.
-    LiveInterval *fixed = fixedIntervals[allocation.toRegister().code()];
+    LiveInterval *fixed = fixedIntervals[allocatedReg.code()];
     if (fixed->numRanges() > 0) {
         CodePosition fixedPos = current->intersect(fixed);
         if (fixedPos != CodePosition::MIN) {
             JS_ASSERT(fixedPos > current->start());
             JS_ASSERT(fixedPos < current->end());
             if (!splitInterval(current, fixedPos))
                 return false;
         }
     }
 
     // Split the blocking interval if it exists.
-    for (IntervalIterator i(active.begin()); i != active.end(); i++) {
-        if (i->getAllocation()->isRegister() && *i->getAllocation() == allocation) {
+
+    for (IntervalIterator i(active.begin()); i != active.end();) {
+        if (i->getAllocation()->isRegister() &&
+            i->getAllocation()->toRegister().aliases(allocatedReg))
+        {
             IonSpew(IonSpew_RegAlloc, " Splitting active interval %u = [%u, %u]",
                     vregs[i->vreg()].ins()->id(), i->start().bits(), i->end().bits());
 
             JS_ASSERT(i->start() != current->start());
             JS_ASSERT(i->covers(current->start()));
             JS_ASSERT(i->start() != current->start());
 
             if (!splitInterval(*i, current->start()))
                 return false;
 
             LiveInterval *it = *i;
-            active.removeAt(i);
+            i = active.removeAt(i);
             finishInterval(it);
-            break;
+            if (allocatedReg.numAliased() == 1)
+                break;
+        } else {
+            IonSpew(IonSpew_RegAlloc, " Not touching active interval %u = [%u, %u]",
+                    vregs[i->vreg()].ins()->id(), i->start().bits(), i->end().bits());
+            i++;
         }
     }
-
     // Split any inactive intervals at the next live point.
     for (IntervalIterator i(inactive.begin()); i != inactive.end(); ) {
-        if (i->getAllocation()->isRegister() && *i->getAllocation() == allocation) {
+        if (i->getAllocation()->isRegister() &&
+            i->getAllocation()->toRegister().aliases(allocatedReg))
+        {
             IonSpew(IonSpew_RegAlloc, " Splitting inactive interval %u = [%u, %u]",
                     vregs[i->vreg()].ins()->id(), i->start().bits(), i->end().bits());
 
             LiveInterval *it = *i;
             CodePosition nextActive = it->nextCoveredAfter(current->start());
             JS_ASSERT(nextActive != CodePosition::MIN);
 
             if (!splitInterval(it, nextActive))
                 return false;
-
             i = inactive.removeAt(i);
             finishInterval(it);
         } else {
             i++;
         }
     }
 
     return true;
@@ -972,86 +979,121 @@ AnyRegister::Code
 LinearScanAllocator::findBestFreeRegister(CodePosition *freeUntil)
 {
     IonSpew(IonSpew_RegAlloc, "  Computing freeUntilPos");
 
     // Compute free-until positions for all registers
     CodePosition freeUntilPos[AnyRegister::Total];
     bool needFloat = vregs[current->vreg()].isFloatReg();
     for (RegisterSet regs(allRegisters_); !regs.empty(needFloat); ) {
+        // If the requested register is a FP, we may need to look at
+        // all of the float32 and float64 registers.
         AnyRegister reg = regs.takeAny(needFloat);
         freeUntilPos[reg.code()] = CodePosition::MAX;
     }
     for (IntervalIterator i(active.begin()); i != active.end(); i++) {
         LAllocation *alloc = i->getAllocation();
         if (alloc->isRegister(needFloat)) {
             AnyRegister reg = alloc->toRegister();
-            IonSpew(IonSpew_RegAlloc, "   Register %s not free", reg.name());
-            freeUntilPos[reg.code()] = CodePosition::MIN;
+            for (int a = 0; a < reg.numAliased(); a++) {
+                IonSpew(IonSpew_RegAlloc, "   Register %s not free", reg.aliased(a).name());
+                freeUntilPos[reg.aliased(a).code()] = CodePosition::MIN;
+            }
         }
     }
     for (IntervalIterator i(inactive.begin()); i != inactive.end(); i++) {
         LAllocation *alloc = i->getAllocation();
         if (alloc->isRegister(needFloat)) {
             AnyRegister reg = alloc->toRegister();
             CodePosition pos = current->intersect(*i);
-            if (pos != CodePosition::MIN && pos < freeUntilPos[reg.code()]) {
-                freeUntilPos[reg.code()] = pos;
-                IonSpew(IonSpew_RegAlloc, "   Register %s free until %u", reg.name(), pos.bits());
+            for (int a = 0; a < reg.numAliased(); a++) {
+                if (pos != CodePosition::MIN && pos < freeUntilPos[reg.aliased(a).code()]) {
+                    freeUntilPos[reg.aliased(a).code()] = pos;
+                    IonSpew(IonSpew_RegAlloc, "   Register %s free until %u", reg.aliased(a).name(), pos.bits());
+                }
             }
         }
     }
 
     CodePosition fixedPos = fixedIntervalsUnion->intersect(current);
     if (fixedPos != CodePosition::MIN) {
         for (IntervalIterator i(fixed.begin()); i != fixed.end(); i++) {
             AnyRegister reg = i->getAllocation()->toRegister();
-            if (freeUntilPos[reg.code()] != CodePosition::MIN) {
-                CodePosition pos = current->intersect(*i);
-                if (pos != CodePosition::MIN && pos < freeUntilPos[reg.code()]) {
-                    freeUntilPos[reg.code()] = (pos == current->start()) ? CodePosition::MIN : pos;
-                    IonSpew(IonSpew_RegAlloc, "   Register %s free until %u", reg.name(), pos.bits());
+            for (int a = 0; a < reg.numAliased(); a++) {
+                AnyRegister areg = reg.aliased(a);
+                if (freeUntilPos[areg.code()] != CodePosition::MIN) {
+                    CodePosition pos = current->intersect(*i);
+                    if (pos != CodePosition::MIN && pos < freeUntilPos[areg.code()]) {
+                        freeUntilPos[areg.code()] = (pos == current->start()) ? CodePosition::MIN : pos;
+                        IonSpew(IonSpew_RegAlloc, "   Register %s free until %u", areg.name(), pos.bits());
+                    }
                 }
             }
         }
     }
 
     AnyRegister::Code bestCode = AnyRegister::Invalid;
     if (current->index()) {
         // As an optimization, use the allocation from the previous interval if
         // it is available.
         LiveInterval *previous = vregs[current->vreg()].getInterval(current->index() - 1);
         LAllocation *alloc = previous->getAllocation();
         if (alloc->isRegister(needFloat)) {
             AnyRegister prevReg = alloc->toRegister();
-            if (freeUntilPos[prevReg.code()] != CodePosition::MIN)
+            bool useit = true;
+            for (int a = 0; a < prevReg.numAliased(); a++) {
+                AnyRegister aprevReg = prevReg.aliased(a);
+                if (freeUntilPos[aprevReg.code()] == CodePosition::MIN) {
+                    useit = false;
+                    break;
+                }
+            }
+            if (useit)
                 bestCode = prevReg.code();
         }
     }
 
     // Assign the register suggested by the hint if it's free.
     const Requirement *hint = current->hint();
     if (hint->kind() == Requirement::FIXED && hint->allocation().isRegister()) {
         AnyRegister hintReg = hint->allocation().toRegister();
-        if (freeUntilPos[hintReg.code()] > hint->pos())
+        bool useit = true;
+        for (int a = 0; a < hintReg.numAliased(); a++) {
+            if (freeUntilPos[hintReg.aliased(a).code()] <= hint->pos()) {
+                useit = false;
+                break;
+            }
+        }
+        if (useit)
             bestCode = hintReg.code();
+
     } else if (hint->kind() == Requirement::MUST_REUSE_INPUT) {
         LiveInterval *other = vregs[hint->virtualRegister()].intervalFor(hint->pos());
         if (other && other->getAllocation()->isRegister()) {
             AnyRegister hintReg = other->getAllocation()->toRegister();
-            if (freeUntilPos[hintReg.code()] > hint->pos())
+            bool useit = true;
+            for (int a = 0; a < hintReg.numAliased(); a++) {
+                if (freeUntilPos[hintReg.aliased(a).code()] <= hint->pos()) {
+                    useit = false;
+                    break;
+                }
+            }
+            if (useit)
                 bestCode = hintReg.code();
         }
     }
 
     if (bestCode == AnyRegister::Invalid) {
         // If all else fails, search freeUntilPos for largest value
         for (uint32_t i = 0; i < AnyRegister::Total; i++) {
             if (freeUntilPos[i] == CodePosition::MIN)
                 continue;
+            AnyRegister cur = AnyRegister::FromCode(i);
+            if (!vregs[current->vreg()].isCompatibleReg(cur))
+                continue;
             if (bestCode == AnyRegister::Invalid || freeUntilPos[i] > freeUntilPos[bestCode])
                 bestCode = AnyRegister::Code(i);
         }
     }
 
     if (bestCode != AnyRegister::Invalid)
         *freeUntil = freeUntilPos[bestCode];
     return bestCode;
@@ -1073,58 +1115,70 @@ LinearScanAllocator::findBestBlockedRegi
     bool needFloat = vregs[current->vreg()].isFloatReg();
     for (RegisterSet regs(allRegisters_); !regs.empty(needFloat); ) {
         AnyRegister reg = regs.takeAny(needFloat);
         nextUsePos[reg.code()] = CodePosition::MAX;
     }
     for (IntervalIterator i(active.begin()); i != active.end(); i++) {
         LAllocation *alloc = i->getAllocation();
         if (alloc->isRegister(needFloat)) {
-            AnyRegister reg = alloc->toRegister();
-            if (i->start() == current->start()) {
-                nextUsePos[reg.code()] = CodePosition::MIN;
-                IonSpew(IonSpew_RegAlloc, "   Disqualifying %s due to recency", reg.name());
-            } else if (nextUsePos[reg.code()] != CodePosition::MIN) {
-                nextUsePos[reg.code()] = i->nextUsePosAfter(current->start());
-                IonSpew(IonSpew_RegAlloc, "   Register %s next used %u", reg.name(),
-                        nextUsePos[reg.code()].bits());
+            AnyRegister fullreg = alloc->toRegister();
+            for (int a = 0; a < fullreg.numAliased(); a++) {
+                AnyRegister reg = fullreg.aliased(a);
+                if (i->start() == current->start()) {
+                    nextUsePos[reg.code()] = CodePosition::MIN;
+                    IonSpew(IonSpew_RegAlloc, "   Disqualifying %s due to recency", reg.name());
+                } else if (nextUsePos[reg.code()] != CodePosition::MIN) {
+                    nextUsePos[reg.code()] = i->nextUsePosAfter(current->start());
+                    IonSpew(IonSpew_RegAlloc, "   Register %s next used %u", reg.name(),
+                            nextUsePos[reg.code()].bits());
+                }
             }
         }
     }
     for (IntervalIterator i(inactive.begin()); i != inactive.end(); i++) {
         LAllocation *alloc = i->getAllocation();
         if (alloc->isRegister(needFloat)) {
             AnyRegister reg = alloc->toRegister();
             CodePosition pos = i->nextUsePosAfter(current->start());
-            if (pos < nextUsePos[reg.code()]) {
-                nextUsePos[reg.code()] = pos;
-                IonSpew(IonSpew_RegAlloc, "   Register %s next used %u", reg.name(), pos.bits());
+            for (int a = 0; a < reg.numAliased(); a++) {
+                if (pos < nextUsePos[reg.aliased(a).code()]) {
+                    nextUsePos[reg.aliased(a).code()] = pos;
+                    IonSpew(IonSpew_RegAlloc, "   Register %s next used %u", reg.aliased(a).name(), pos.bits());
+                }
             }
         }
     }
 
     CodePosition fixedPos = fixedIntervalsUnion->intersect(current);
     if (fixedPos != CodePosition::MIN) {
         for (IntervalIterator i(fixed.begin()); i != fixed.end(); i++) {
-            AnyRegister reg = i->getAllocation()->toRegister();
-            if (nextUsePos[reg.code()] != CodePosition::MIN) {
-                CodePosition pos = i->intersect(current);
-                if (pos != CodePosition::MIN && pos < nextUsePos[reg.code()]) {
-                    nextUsePos[reg.code()] = (pos == current->start()) ? CodePosition::MIN : pos;
-                    IonSpew(IonSpew_RegAlloc, "   Register %s next used %u (fixed)", reg.name(), pos.bits());
+            AnyRegister fullreg = i->getAllocation()->toRegister();
+            for (int a = 0; a < fullreg.numAliased(); a++) {
+                AnyRegister reg = fullreg.aliased(a);
+                if (nextUsePos[reg.code()] != CodePosition::MIN) {
+                    CodePosition pos = i->intersect(current);
+                    if (pos != CodePosition::MIN && pos < nextUsePos[reg.code()]) {
+                        nextUsePos[reg.code()] = (pos == current->start()) ? CodePosition::MIN : pos;
+                        IonSpew(IonSpew_RegAlloc, "   Register %s next used %u (fixed)", reg.name(), pos.bits());
+                    }
                 }
             }
         }
     }
 
     // Search nextUsePos for largest value
     AnyRegister::Code bestCode = AnyRegister::Invalid;
     for (size_t i = 0; i < AnyRegister::Total; i++) {
         if (nextUsePos[i] == CodePosition::MIN)
             continue;
+        AnyRegister cur = AnyRegister::FromCode(i);
+        if (!vregs[current->vreg()].isCompatibleReg(cur))
+            continue;
+
         if (bestCode == AnyRegister::Invalid || nextUsePos[i] > nextUsePos[bestCode])
             bestCode = AnyRegister::Code(i);
     }
 
     if (bestCode != AnyRegister::Invalid)
         *nextUsed = nextUsePos[bestCode];
     return bestCode;
 }
@@ -1140,17 +1194,17 @@ LinearScanAllocator::findBestBlockedRegi
  * Intuitively, it is a bug if any allocated intervals exist which can not
  * coexist.
  */
 bool
 LinearScanAllocator::canCoexist(LiveInterval *a, LiveInterval *b)
 {
     LAllocation *aa = a->getAllocation();
     LAllocation *ba = b->getAllocation();
-    if (aa->isRegister() && ba->isRegister() && aa->toRegister() == ba->toRegister())
+    if (aa->isRegister() && ba->isRegister() && aa->toRegister().aliases(ba->toRegister()))
         return a->intersect(b) == CodePosition::MIN;
     return true;
 }
 
 #ifdef DEBUG
 /*
  * Ensure intervals appear in exactly the appropriate one of {active,inactive,
  * handled}, and that active and inactive intervals do not conflict. Handled
--- a/js/src/jit/LinearScan.h
+++ b/js/src/jit/LinearScan.h
@@ -98,17 +98,17 @@ class LinearScanAllocator
     bool reifyAllocations();
     bool populateSafepoints();
 
     // Optimization for the UnsortedQueue.
     void enqueueVirtualRegisterIntervals();
 
     uint32_t allocateSlotFor(const LiveInterval *interval);
     bool splitInterval(LiveInterval *interval, CodePosition pos);
-    bool splitBlockingIntervals(LAllocation allocation);
+    bool splitBlockingIntervals(AnyRegister allocatedReg);
     bool assign(LAllocation allocation);
     bool spill();
     void freeAllocation(LiveInterval *interval, LAllocation *alloc);
     void finishInterval(LiveInterval *interval);
     AnyRegister::Code findBestFreeRegister(CodePosition *freeUntil);
     AnyRegister::Code findBestBlockedRegister(CodePosition *nextUsed);
     bool canCoexist(LiveInterval *a, LiveInterval *b);
     bool moveInputAlloc(CodePosition pos, LAllocation *from, LAllocation *to, LDefinition::Type type);
--- a/js/src/jit/LiveRangeAllocator.cpp
+++ b/js/src/jit/LiveRangeAllocator.cpp
@@ -642,17 +642,17 @@ LiveRangeAllocator<VREG, forLSRA>::build
                 for (AnyRegisterIterator iter(allRegisters_); iter.more(); iter++) {
                     if (forLSRA) {
                         if (!addFixedRangeAtHead(*iter, inputOf(*ins), outputOf(*ins)))
                             return false;
                     } else {
                         bool found = false;
                         for (size_t i = 0; i < ins->numDefs(); i++) {
                             if (ins->getDef(i)->isFixed() &&
-                                *ins->getDef(i)->output() == LAllocation(*iter)) {
+                                ins->getDef(i)->output()->aliases(LAllocation(*iter))) {
                                 found = true;
                                 break;
                             }
                         }
                         if (!found && !addFixedRangeAtHead(*iter, outputOf(*ins), outputOf(*ins).next()))
                             return false;
                     }
                 }
--- a/js/src/jit/LiveRangeAllocator.h
+++ b/js/src/jit/LiveRangeAllocator.h
@@ -489,16 +489,22 @@ class VirtualRegister
         if (!found)
             found = intervals_.end();
         interval->setIndex(found - intervals_.begin());
         return intervals_.insert(found, interval);
     }
     bool isFloatReg() const {
         return def_->isFloatReg();
     }
+    bool isCompatibleReg(const AnyRegister &r) const {
+        return def_->isCompatibleReg(r);
+    }
+    bool isCompatibleVReg(const VirtualRegister &vr) const {
+        return def_->isCompatibleDef(*vr.def_);
+    }
 
     LiveInterval *intervalFor(CodePosition pos);
     LiveInterval *getFirstInterval();
 };
 
 // Index of the virtual registers in a graph. VREG is a subclass of
 // VirtualRegister extended with any allocator specific state for the vreg.
 template <typename VREG>
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -1211,17 +1211,17 @@ LIRGenerator::visitRound(MRound *ins)
 
     if (type == MIRType_Double) {
         LRound *lir = new (alloc()) LRound(useRegister(ins->num()), tempDouble());
         if (!assignSnapshot(lir, Bailout_Round))
             return false;
         return define(lir, ins);
     }
 
-    LRoundF *lir = new (alloc()) LRoundF(useRegister(ins->num()), tempDouble());
+    LRoundF *lir = new (alloc()) LRoundF(useRegister(ins->num()), tempFloat32());
     if (!assignSnapshot(lir, Bailout_Round))
         return false;
     return define(lir, ins);
 }
 
 bool
 LIRGenerator::visitMinMax(MMinMax *ins)
 {
@@ -3080,20 +3080,21 @@ LIRGenerator::visitAssertRange(MAssertRa
       case MIRType_Int32:
         lir = new(alloc()) LAssertRangeI(useRegisterAtStart(input));
         break;
 
       case MIRType_Double:
         lir = new(alloc()) LAssertRangeD(useRegister(input), tempDouble());
         break;
 
-      case MIRType_Float32:
-        lir = new(alloc()) LAssertRangeF(useRegister(input), tempFloat32());
+      case MIRType_Float32: {
+        LDefinition armtemp = hasMultiAlias() ? tempFloat32() : LDefinition::BogusTemp();
+        lir = new(alloc()) LAssertRangeF(useRegister(input), tempFloat32(), armtemp);
         break;
-
+      }
       case MIRType_Value:
         lir = new(alloc()) LAssertRangeV(tempToUnbox(), tempDouble(), tempDouble());
         if (!useBox(lir, LAssertRangeV::Input, input))
             return false;
         break;
 
       default:
         MOZ_ASSUME_UNREACHABLE("Unexpected Range for MIRType");
@@ -3190,27 +3191,30 @@ LIRGenerator::visitSetElementCache(MSetE
     JS_ASSERT(ins->index()->type() == MIRType_Value);
 
     // Due to lack of registers on x86, we reuse the object register as a
     // temporary. This register may be used in a 1-byte store, which on x86
     // again has constraints; thus the use of |useByteOpRegister| over
     // |useRegister| below.
     LInstruction *lir;
     if (ins->value()->type() == MIRType_Value) {
+        LDefinition tempF32 = hasUnaliasedDouble() ? tempFloat32() : LDefinition::BogusTemp();
         lir = new(alloc()) LSetElementCacheV(useByteOpRegister(ins->object()), tempToUnbox(),
-                                             temp(), tempDouble());
+                                             temp(), tempDouble(), tempF32);
 
         if (!useBox(lir, LSetElementCacheV::Index, ins->index()))
             return false;
         if (!useBox(lir, LSetElementCacheV::Value, ins->value()))
             return false;
     } else {
+        LDefinition tempF32 = hasUnaliasedDouble() ? tempFloat32() : LDefinition::BogusTemp();
         lir = new(alloc()) LSetElementCacheT(useByteOpRegister(ins->object()),
                                              useRegisterOrConstant(ins->value()),
-                                             tempToUnbox(), temp(), tempDouble());
+                                             tempToUnbox(), temp(), tempDouble(),
+                                             tempF32);
 
         if (!useBox(lir, LSetElementCacheT::Index, ins->index()))
             return false;
     }
 
     return add(lir, ins) && assignSafepoint(lir, ins);
 }
 
@@ -3474,18 +3478,20 @@ LIRGenerator::visitAsmJSParameter(MAsmJS
     return defineFixed(new(alloc()) LAsmJSParameter, ins, LArgument(abi.offsetFromArgBase()));
 }
 
 bool
 LIRGenerator::visitAsmJSReturn(MAsmJSReturn *ins)
 {
     MDefinition *rval = ins->getOperand(0);
     LAsmJSReturn *lir = new(alloc()) LAsmJSReturn;
-    if (IsFloatingPointType(rval->type()))
-        lir->setOperand(0, useFixed(rval, ReturnFloatReg));
+    if (rval->type() == MIRType_Float32)
+        lir->setOperand(0, useFixed(rval, ReturnFloat32Reg));
+    else if (rval->type() == MIRType_Double)
+        lir->setOperand(0, useFixed(rval, ReturnDoubleReg));
     else if (rval->type() == MIRType_Int32)
         lir->setOperand(0, useFixed(rval, ReturnReg));
     else
         MOZ_ASSUME_UNREACHABLE("Unexpected asm.js return type");
     return add(lir);
 }
 
 bool
--- a/js/src/jit/RegisterSets.h
+++ b/js/src/jit/RegisterSets.h
@@ -61,16 +61,51 @@ struct AnyRegister {
         return isFloat() ? fpu().name() : gpr().name();
     }
     Code code() const {
         return code_;
     }
     bool volatile_() const {
         return isFloat() ? fpu().volatile_() : gpr().volatile_();
     }
+    AnyRegister aliased(uint32_t aliasIdx) const {
+        AnyRegister ret;
+        if (isFloat()) {
+            FloatRegister fret;
+            fpu().aliased(aliasIdx, &fret);
+            ret = AnyRegister(fret);
+        } else {
+            Register gret;
+            gpr().aliased(aliasIdx, &gret);
+            ret = AnyRegister(gret);
+        }
+        JS_ASSERT_IF(aliasIdx == 0, ret == *this);
+        return ret;
+    }
+    uint32_t numAliased() const {
+        if (isFloat())
+            return fpu().numAliased();
+        return gpr().numAliased();
+    }
+    bool aliases(const AnyRegister &other) const {
+        if (isFloat() && other.isFloat())
+            return fpu().aliases(other.fpu());
+        if (!isFloat() && !other.isFloat())
+            return gpr().aliases(other.gpr());
+        return false;
+    }
+    // do the two registers hold the same type of data (e.g. both float32, both gpr)
+    bool isCompatibleReg (const AnyRegister other) const {
+        if (isFloat() && other.isFloat())
+            return fpu().equiv(other.fpu());
+        if (!isFloat() && !other.isFloat())
+            return true;
+        return false;
+    }
+
 };
 
 // Registers to hold a boxed value. Uses one register on 64 bit
 // platforms, two registers on 32 bit platforms.
 class ValueOperand
 {
 #if defined(JS_NUNBOX32)
     Register type_;
@@ -460,16 +495,25 @@ class TypedRegisterSet
         return bits_;
     }
     uint32_t size() const {
         return mozilla::CountPopulation32(bits_);
     }
     bool operator ==(const TypedRegisterSet<T> &other) const {
         return other.bits_ == bits_;
     }
+    TypedRegisterSet<T> reduceSetForPush() const {
+        return T::ReduceSetForPush(*this);
+    }
+    uint32_t getSizeInBytes() const {
+        return T::GetSizeInBytes(*this);
+    }
+    uint32_t getPushSizeInBytes() const {
+        return T::GetPushSizeInBytes(*this);
+    }
 };
 
 typedef TypedRegisterSet<Register> GeneralRegisterSet;
 typedef TypedRegisterSet<FloatRegister> FloatRegisterSet;
 
 class AnyRegisterIterator;
 
 class RegisterSet {
--- a/js/src/jit/Registers.h
+++ b/js/src/jit/Registers.h
@@ -51,49 +51,25 @@ struct Register {
         return code_ == other.code_;
     }
     bool operator !=(Register other) const {
         return code_ != other.code_;
     }
     bool volatile_() const {
         return !!((1 << code()) & Registers::VolatileMask);
     }
-};
-
-struct FloatRegister {
-    typedef FloatRegisters Codes;
-    typedef Codes::Code Code;
-
-    Code code_;
-
-    static FloatRegister FromCode(uint32_t i) {
-        JS_ASSERT(i < FloatRegisters::Total);
-        FloatRegister r = { (FloatRegisters::Code)i };
-        return r;
-    }
-    static FloatRegister FromName(const char *name) {
-        FloatRegisters::Code code = FloatRegisters::FromName(name);
-        FloatRegister r = { code };
-        return r;
-    }
-    Code code() const {
-        JS_ASSERT((uint32_t)code_ < FloatRegisters::Total);
-        return code_;
-    }
-    const char *name() const {
-        return FloatRegisters::GetName(code());
-    }
-    bool operator ==(FloatRegister other) const {
+    bool aliases(const Register &other) const {
         return code_ == other.code_;
     }
-    bool operator !=(FloatRegister other) const {
-        return code_ != other.code_;
+    uint32_t numAliased() const {
+        return 1;
     }
-    bool volatile_() const {
-        return !!((1 << code()) & FloatRegisters::VolatileMask);
+    void aliased(uint32_t aliasIdx, Register *ret) const {
+        JS_ASSERT(aliasIdx == 0);
+        *ret = *this;
     }
 };
 
 class RegisterDump
 {
   protected: // Silence Clang warning.
     mozilla::Array<uintptr_t, Registers::Total> regs_;
     mozilla::Array<double, FloatRegisters::Total> fpregs_;
--- a/js/src/jit/Safepoints.cpp
+++ b/js/src/jit/Safepoints.cpp
@@ -52,16 +52,43 @@ WriteRegisterMask(CompactBufferWriter &s
 static int32_t
 ReadRegisterMask(CompactBufferReader &stream)
 {
     if (sizeof(PackedRegisterMask) == 1)
         return stream.readByte();
     return stream.readUnsigned();
 }
 
+static void
+WriteFloatRegisterMask(CompactBufferWriter &stream, uint64_t bits)
+{
+    if (sizeof(FloatRegisters::SetType) == 1) {
+        stream.writeByte(bits);
+    } else if (sizeof(FloatRegisters::SetType) == 4) {
+        stream.writeUnsigned(bits);
+    } else {
+        JS_ASSERT(sizeof(FloatRegisters::SetType) == 8);
+        stream.writeUnsigned(bits & 0xffffffff);
+        stream.writeUnsigned(bits >> 32);
+    }
+}
+
+static int64_t
+ReadFloatRegisterMask(CompactBufferReader &stream)
+{
+    if (sizeof(FloatRegisters::SetType) == 1)
+        return stream.readByte();
+    if (sizeof(FloatRegisters::SetType) <= 4)
+        return stream.readUnsigned();
+    JS_ASSERT(sizeof(FloatRegisters::SetType) == 8);
+    uint64_t ret = stream.readUnsigned();
+    ret |= uint64_t(stream.readUnsigned()) << 32;
+    return ret;
+}
+
 void
 SafepointWriter::writeGcRegs(LSafepoint *safepoint)
 {
     GeneralRegisterSet gc = safepoint->gcRegs();
     GeneralRegisterSet spilledGpr = safepoint->liveRegs().gprs();
     FloatRegisterSet spilledFloat = safepoint->liveRegs().fpus();
     GeneralRegisterSet slots = safepoint->slotsOrElementsRegs();
     GeneralRegisterSet valueRegs;
@@ -76,17 +103,17 @@ SafepointWriter::writeGcRegs(LSafepoint 
         WriteRegisterMask(stream_, valueRegs.bits());
 #endif
     }
 
     // GC registers are a subset of the spilled registers.
     JS_ASSERT((valueRegs.bits() & ~spilledGpr.bits()) == 0);
     JS_ASSERT((gc.bits() & ~spilledGpr.bits()) == 0);
 
-    WriteRegisterMask(stream_, spilledFloat.bits());
+    WriteFloatRegisterMask(stream_, spilledFloat.bits());
 
 #ifdef DEBUG
     if (IonSpewEnabled(IonSpew_Safepoints)) {
         for (GeneralRegisterForwardIterator iter(spilledGpr); iter.more(); iter++) {
             const char *type = gc.has(*iter)
                                ? "gc"
                                : slots.has(*iter)
                                  ? "slots"
@@ -357,18 +384,17 @@ SafepointReader::SafepointReader(IonScri
         slotsOrElementsSpills_ = allGprSpills_;
     } else {
         gcSpills_ = GeneralRegisterSet(ReadRegisterMask(stream_));
         slotsOrElementsSpills_ = GeneralRegisterSet(ReadRegisterMask(stream_));
 #ifdef JS_PUNBOX64
         valueSpills_ = GeneralRegisterSet(ReadRegisterMask(stream_));
 #endif
     }
-
-    allFloatSpills_ = FloatRegisterSet(ReadRegisterMask(stream_));
+    allFloatSpills_ = FloatRegisterSet(ReadFloatRegisterMask(stream_));
 
     advanceFromGcRegs();
 }
 
 uint32_t
 SafepointReader::osiReturnPointOffset() const
 {
     return osiCallPointOffset_ + Assembler::patchWrite_NearCallSize();
--- a/js/src/jit/Snapshots.cpp
+++ b/js/src/jit/Snapshots.cpp
@@ -281,17 +281,17 @@ RValueAllocation::readPayload(CompactBuf
         break;
       case PAYLOAD_STACK_OFFSET:
         p->stackOffset = reader.readSigned();
         break;
       case PAYLOAD_GPR:
         p->gpr = Register::FromCode(reader.readByte());
         break;
       case PAYLOAD_FPU:
-        p->fpu = FloatRegister::FromCode(reader.readByte());
+        p->fpu.data = reader.readByte();
         break;
       case PAYLOAD_PACKED_TAG:
         p->type = JSValueType(*mode & PACKED_TAG_MASK);
         *mode = *mode & ~PACKED_TAG_MASK;
         break;
     }
 }
 
--- a/js/src/jit/Snapshots.h
+++ b/js/src/jit/Snapshots.h
@@ -86,21 +86,35 @@ class RValueAllocation
         PayloadType type2;
         const char *name;
     };
 
   private:
     Mode mode_;
 
     // Additional information to recover the content of the allocation.
+    struct FloatRegisterBits {
+        uint32_t data;
+        bool operator == (const FloatRegisterBits &other) const {
+            return data == other.data;
+        }
+        uint32_t code() const {
+            return data;
+        }
+        const char *name() const {
+            FloatRegister tmp = FloatRegister::FromCode(data);
+            return tmp.name();
+        }
+    };
+
     union Payload {
         uint32_t index;
         int32_t stackOffset;
         Register gpr;
-        FloatRegister fpu;
+        FloatRegisterBits fpu;
         JSValueType type;
     };
 
     Payload arg1_;
     Payload arg2_;
 
     static Payload payloadOfIndex(uint32_t index) {
         Payload p;
@@ -114,17 +128,19 @@ class RValueAllocation
     }
     static Payload payloadOfRegister(Register reg) {
         Payload p;
         p.gpr = reg;
         return p;
     }
     static Payload payloadOfFloatRegister(FloatRegister reg) {
         Payload p;
-        p.fpu = reg;
+        FloatRegisterBits b;
+        b.data = reg.code();
+        p.fpu = b;
         return p;
     }
     static Payload payloadOfValueType(JSValueType type) {
         Payload p;
         p.type = type;
         return p;
     }
 
@@ -264,17 +280,18 @@ class RValueAllocation
         return arg1_.stackOffset;
     }
     Register reg() const {
         JS_ASSERT(layoutFromMode(mode()).type1 == PAYLOAD_GPR);
         return arg1_.gpr;
     }
     FloatRegister fpuReg() const {
         JS_ASSERT(layoutFromMode(mode()).type1 == PAYLOAD_FPU);
-        return arg1_.fpu;
+        FloatRegisterBits b = arg1_.fpu;
+        return FloatRegister::FromCode(b.data);
     }
     JSValueType knownType() const {
         JS_ASSERT(layoutFromMode(mode()).type1 == PAYLOAD_PACKED_TAG);
         return arg1_.type;
     }
 
     int32_t stackOffset2() const {
         JS_ASSERT(layoutFromMode(mode()).type2 == PAYLOAD_STACK_OFFSET);
--- a/js/src/jit/arm/Architecture-arm.cpp
+++ b/js/src/jit/arm/Architecture-arm.cpp
@@ -303,10 +303,57 @@ FloatRegisters::FromName(const char *nam
     for (size_t i = 0; i < Total; i++) {
         if (strcmp(GetName(i), name) == 0)
             return Code(i);
     }
 
     return Invalid;
 }
 
+FloatRegisterSet
+VFPRegister::ReduceSetForPush(const FloatRegisterSet &s)
+{
+    FloatRegisterSet mod;
+    for (TypedRegisterIterator<FloatRegister> iter(s); iter.more(); iter++) {
+        if ((*iter).isSingle()) {
+            // add in just this float
+            mod.addUnchecked(*iter);
+        } else if ((*iter).id() < 16) {
+            // a double with an overlay, add in both floats
+            mod.addUnchecked((*iter).singleOverlay(0));
+            mod.addUnchecked((*iter).singleOverlay(1));
+        } else {
+            // add in the lone double in the range 16-31
+            mod.addUnchecked(*iter);
+        }
+    }
+    return mod;
+}
+
+uint32_t
+VFPRegister::GetSizeInBytes(const FloatRegisterSet &s)
+{
+    uint64_t bits = s.bits();
+    uint32_t ret = mozilla::CountPopulation32(bits&0xffffffff) * sizeof(float);
+    ret +=  mozilla::CountPopulation32(bits >> 32) * sizeof(double);
+    return ret;
+}
+uint32_t
+VFPRegister::GetPushSizeInBytes(const FloatRegisterSet &s)
+{
+    FloatRegisterSet ss = s.reduceSetForPush();
+    uint64_t bits = ss.bits();
+    uint32_t ret = mozilla::CountPopulation32(bits&0xffffffff) * sizeof(float);
+    ret +=  mozilla::CountPopulation32(bits >> 32) * sizeof(double);
+    return ret;
+}
+uint32_t
+VFPRegister::getRegisterDumpOffsetInBytes()
+{
+    if (isSingle())
+        return id() * sizeof(float);
+    if (isDouble())
+        return id() * sizeof(double);
+    MOZ_ASSUME_UNREACHABLE();
+}
+
 } // namespace jit
 } // namespace js
--- a/js/src/jit/arm/Architecture-arm.h
+++ b/js/src/jit/arm/Architecture-arm.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 jit_arm_Architecture_arm_h
 #define jit_arm_Architecture_arm_h
 
+#include "mozilla/MathAlgorithms.h"
+
 #include <limits.h>
 #include <stdint.h>
 
 #include "js/Utility.h"
 
 // gcc appears to use __ARM_PCS_VFP to denote that the target is a hard-float target.
 #if defined(__ARM_PCS_VFP)
 #define JS_CODEGEN_ARM_HARDFP
@@ -135,16 +137,21 @@ class Registers
         (1 << Registers::r3);
 
     // Registers returned from a JS -> C call.
     static const uint32_t CallMask =
         (1 << Registers::r0) |
         (1 << Registers::r1);  // used for double-size returns
 
     static const uint32_t AllocatableMask = AllMask & ~NonAllocatableMask;
+    typedef uint32_t SetType;
+    static uint32_t SetSize(SetType x) {
+        static_assert(sizeof(SetType) == 4, "SetType must be 32 bits");
+        return mozilla::CountPopulation32(x);
+    }
 };
 
 // Smallest integer type that can hold a register bitmask.
 typedef uint16_t PackedRegisterMask;
 
 class FloatRegisters
 {
   public:
@@ -219,25 +226,232 @@ class FloatRegisters
 
     // d15 is the ARM scratch float register.
     static const uint32_t NonAllocatableMask = (1 << d15) | (1 << invalid_freg);
 
     // Registers that can be allocated without being saved, generally.
     static const uint32_t TempMask = VolatileMask & ~NonAllocatableMask;
 
     static const uint32_t AllocatableMask = AllMask & ~NonAllocatableMask;
+    typedef uint32_t SetType;
 };
 
+template <typename T>
+class TypedRegisterSet;
+
+class VFPRegister
+{
+  public:
+    // What type of data is being stored in this register?
+    // UInt / Int are specifically for vcvt, where we need
+    // to know how the data is supposed to be converted.
+    enum RegType {
+        Single = 0x0,
+        Double = 0x1,
+        UInt   = 0x2,
+        Int    = 0x3
+    };
+
+    typedef FloatRegisters Codes;
+    typedef Codes::Code Code;
+
+  protected:
+    RegType kind : 2;
+    // ARM doesn't have more than 32 registers...
+    // don't take more bits than we'll need.
+    // Presently, I don't have plans to address the upper
+    // and lower halves of the double registers seprately, so
+    // 5 bits should suffice.  If I do decide to address them seprately
+    // (vmov, I'm looking at you), I will likely specify it as a separate
+    // field.
+  public:
+    Code code_ : 5;
+  protected:
+    bool _isInvalid : 1;
+    bool _isMissing : 1;
+
+  public:
+    MOZ_CONSTEXPR VFPRegister(uint32_t r, RegType k)
+      : kind(k), code_ (Code(r)), _isInvalid(false), _isMissing(false)
+    { }
+    MOZ_CONSTEXPR VFPRegister()
+      : kind(Double), code_(Code(0)), _isInvalid(true), _isMissing(false)
+    { }
+
+    MOZ_CONSTEXPR VFPRegister(RegType k, uint32_t id, bool invalid, bool missing) :
+        kind(k), code_(Code(id)), _isInvalid(invalid), _isMissing(missing) {
+    }
+
+    explicit MOZ_CONSTEXPR VFPRegister(Code id)
+      : kind(Double), code_(id), _isInvalid(false), _isMissing(false)
+    { }
+    bool operator==(const VFPRegister &other) const {
+        JS_ASSERT(!isInvalid());
+        JS_ASSERT(!other.isInvalid());
+        return kind == other.kind && code_ == other.code_;
+    }
+    bool isDouble() const { return kind == Double; }
+    bool isSingle() const { return kind == Single; }
+    bool isFloat() const { return (kind == Double) || (kind == Single); }
+    bool isInt() const { return (kind == UInt) || (kind == Int); }
+    bool isSInt() const { return kind == Int; }
+    bool isUInt() const { return kind == UInt; }
+    bool equiv(VFPRegister other) const { return other.kind == kind; }
+    size_t size() const { return (kind == Double) ? 8 : 4; }
+    bool isInvalid() const;
+    bool isMissing() const;
+
+    VFPRegister doubleOverlay(unsigned int which = 0) const;
+    VFPRegister singleOverlay(unsigned int which = 0) const;
+    VFPRegister sintOverlay(unsigned int which = 0) const;
+    VFPRegister uintOverlay(unsigned int which = 0) const;
+
+    struct VFPRegIndexSplit;
+    VFPRegIndexSplit encode();
+
+    // for serializing values
+    struct VFPRegIndexSplit {
+        const uint32_t block : 4;
+        const uint32_t bit : 1;
+
+      private:
+        friend VFPRegIndexSplit js::jit::VFPRegister::encode();
+
+        VFPRegIndexSplit(uint32_t block_, uint32_t bit_)
+          : block(block_), bit(bit_)
+        {
+            JS_ASSERT(block == block_);
+            JS_ASSERT(bit == bit_);
+        }
+    };
+
+    Code code() const {
+        JS_ASSERT(!_isInvalid && !_isMissing);
+        // this should only be used in areas where we only have doubles
+        // and singles.
+        JS_ASSERT(isFloat());
+        return Code(code_);
+    }
+    uint32_t id() const {
+        return code_;
+    }
+    static VFPRegister FromCode(uint32_t i) {
+        uint32_t code = i & 31;
+        uint32_t kind = i >> 5;
+        return VFPRegister(code, Double);
+    }
+    bool volatile_() const {
+        if (isDouble())
+            return !!((1 << (code_ >> 1)) & FloatRegisters::VolatileMask);
+        return !!((1 << code_) & FloatRegisters::VolatileMask);
+    }
+    const char *name() const {
+        return FloatRegisters::GetName(code_);
+    }
+    bool operator != (const VFPRegister &other) const {
+        return other.kind != kind || code_ != other.code_;
+    }
+    bool aliases(const VFPRegister &other) {
+        if (kind == other.kind)
+            return code_ == other.code_;
+        return doubleOverlay() == other.doubleOverlay();
+    }
+    static const int NumAliasedDoubles = 16;
+    uint32_t numAliased() const {
+        return 1;
+#ifdef EVERYONE_KNOWS_ABOUT_ALIASING
+        if (isDouble()) {
+            if (code_ < NumAliasedDoubles)
+                return 3;
+            return 1;
+        }
+        return 2;
+#endif
+    }
+    void aliased(uint32_t aliasIdx, VFPRegister *ret) {
+        if (aliasIdx == 0) {
+            *ret = *this;
+            return;
+        }
+        if (isDouble()) {
+            JS_ASSERT(code_ < NumAliasedDoubles);
+            JS_ASSERT(aliasIdx <= 2);
+            *ret = singleOverlay(aliasIdx - 1);
+            return;
+        }
+        JS_ASSERT(aliasIdx == 1);
+        *ret = doubleOverlay(aliasIdx - 1);
+    }
+    uint32_t numAlignedAliased() const {
+        if (isDouble()) {
+            if (code_ < NumAliasedDoubles)
+                return 2;
+            return 1;
+        }
+        // s1 has 0 other aligned aliases, 1 total.
+        // s0 has 1 other aligned aliase, 2 total.
+        return 2 - (code_ & 1);
+    }
+    // |   d0    |
+    // | s0 | s1 |
+    // if we've stored s0 and s1 in memory, we also want to say that d0
+    // is stored there, but it is only stored at the location where it is aligned
+    // e.g. at s0, not s1.
+    void alignedAliased(uint32_t aliasIdx, VFPRegister *ret) {
+        if (aliasIdx == 0) {
+            *ret = *this;
+            return;
+        }
+        JS_ASSERT(aliasIdx == 1);
+        if (isDouble()) {
+            JS_ASSERT(code_ < NumAliasedDoubles);
+            *ret = singleOverlay(aliasIdx - 1);
+            return;
+        }
+        JS_ASSERT((code_ & 1) == 0);
+        *ret = doubleOverlay(aliasIdx - 1);
+        return;
+    }
+    typedef FloatRegisters::SetType SetType;
+    static uint32_t SetSize(SetType x) {
+        static_assert(sizeof(SetType) == 4, "SetType must be 32 bits");
+        return mozilla::CountPopulation32(x);
+    }
+    static Code FromName(const char *name) {
+        return FloatRegisters::FromName(name);
+    }
+    static TypedRegisterSet<VFPRegister> ReduceSetForPush(const TypedRegisterSet<VFPRegister> &s);
+    static uint32_t GetSizeInBytes(const TypedRegisterSet<VFPRegister> &s);
+    static uint32_t GetPushSizeInBytes(const TypedRegisterSet<VFPRegister> &s);
+    uint32_t getRegisterDumpOffsetInBytes();
+
+};
+
+// The only floating point register set that we work with
+// are the VFP Registers
+typedef VFPRegister FloatRegister;
+
 uint32_t GetARMFlags();
 bool HasMOVWT();
 bool HasVFPv3();
 bool HasVFP();
-bool Has16DP();
+bool Has32DP();
 bool HasIDIV();
 
+// Arm/D32 has double registers that can NOT be treated as float32
+// and this requires some dances in lowering.
+static bool hasUnaliasedDouble() {
+    return Has32DP();
+}
+// On ARM, Dn aliases both S2n and S2n+1, so if you need to convert a float32
+// to a double as a temporary, you need a temporary double register.
+static bool hasMultiAlias() {
+    return true;
+}
+
 bool ParseARMHwCapFlags(const char *armHwCap);
 
 // If the simulator is used then the ABI choice is dynamic.  Otherwise the ABI is static
 // and useHardFpABI is inlined so that unused branches can be optimized away.
 #if defined(JS_ARM_SIMULATOR)
 bool UseHardFpABI();
 #else
 static inline bool UseHardFpABI()
--- a/js/src/jit/arm/Assembler-arm.cpp
+++ b/js/src/jit/arm/Assembler-arm.cpp
@@ -185,27 +185,25 @@ js::jit::VM(VFPRegister vr)
 
 VFPRegister::VFPRegIndexSplit
 jit::VFPRegister::encode()
 {
     JS_ASSERT(!_isInvalid);
 
     switch (kind) {
       case Double:
-        return VFPRegIndexSplit(_code &0xf , _code >> 4);
+        return VFPRegIndexSplit(code_ & 0xf , code_ >> 4);
       case Single:
-        return VFPRegIndexSplit(_code >> 1, _code & 1);
+        return VFPRegIndexSplit(code_ >> 1, code_ & 1);
       default:
         // vfp register treated as an integer, NOT a gpr
-        return VFPRegIndexSplit(_code >> 1, _code & 1);
+        return VFPRegIndexSplit(code_ >> 1, code_ & 1);
     }
 }
 
-VFPRegister js::jit::NoVFPRegister(true);
-
 bool
 InstDTR::isTHIS(const Instruction &i)
 {
     return (i.encode() & IsDTRMask) == (uint32_t)IsDTR;
 }
 
 InstDTR *
 InstDTR::asTHIS(const Instruction &i)
@@ -1180,74 +1178,72 @@ BOffImm::getDest(Instruction *src)
     // TODO: It is probably worthwhile to verify that src is actually a branch
     // NOTE: This does not explicitly shift the offset of the destination left by 2,
     // since it is indexing into an array of instruction sized objects.
     return &src[(((int32_t)data<<8)>>8) + 2];
 }
 
 //VFPRegister implementation
 VFPRegister
-VFPRegister::doubleOverlay() const
+VFPRegister::doubleOverlay(unsigned int which) const
 {
     JS_ASSERT(!_isInvalid);
-    if (kind != Double) {
-        JS_ASSERT(_code % 2 == 0);
-        return VFPRegister(_code >> 1, Double);
-    }
+    if (kind != Double)
+        return VFPRegister(code_ >> 1, Double);
     return *this;
 }
 VFPRegister
-VFPRegister::singleOverlay() const
+VFPRegister::singleOverlay(unsigned int which) const
 {
     JS_ASSERT(!_isInvalid);
     if (kind == Double) {
         // There are no corresponding float registers for d16-d31
-        JS_ASSERT(_code < 16);
-        return VFPRegister(_code << 1, Single);
+        JS_ASSERT(code_ < 16);
+        JS_ASSERT(which < 2);
+        return VFPRegister((code_ << 1) + which, Single);
     }
-
-    JS_ASSERT(_code % 2 == 0);
-    return VFPRegister(_code, Single);
+    JS_ASSERT(which == 0);
+    return VFPRegister(code_, Single);
 }
 
 VFPRegister
-VFPRegister::sintOverlay() const
+VFPRegister::sintOverlay(unsigned int which) const
 {
     JS_ASSERT(!_isInvalid);
     if (kind == Double) {
         // There are no corresponding float registers for d16-d31
-        JS_ASSERT(_code < 16);
-        return VFPRegister(_code << 1, Int);
+        JS_ASSERT(code_ < 16);
+        JS_ASSERT(which < 2);
+        return VFPRegister((code_ << 1) + which, Int);
     }
-
-    JS_ASSERT(_code % 2 == 0);
-    return VFPRegister(_code, Int);
+    JS_ASSERT(which == 0);
+    return VFPRegister(code_, Int);
 }
 VFPRegister
-VFPRegister::uintOverlay() const
+VFPRegister::uintOverlay(unsigned int which) const
 {
     JS_ASSERT(!_isInvalid);
     if (kind == Double) {
         // There are no corresponding float registers for d16-d31
-        JS_ASSERT(_code < 16);
-        return VFPRegister(_code << 1, UInt);
+        JS_ASSERT(code_ < 16);
+        JS_ASSERT(which < 2);
+        return VFPRegister((code_ << 1) + which, UInt);
     }
-
-    JS_ASSERT(_code % 2 == 0);
-    return VFPRegister(_code, UInt);
+    JS_ASSERT(which == 0);
+    return VFPRegister(code_, UInt);
 }
 
 bool
-VFPRegister::isInvalid()
+VFPRegister::isInvalid() const
 {
     return _isInvalid;
 }
 
 bool
-VFPRegister::isMissing()
+VFPRegister::isMissing() const
 {
     JS_ASSERT(!_isInvalid);
     return _isMissing;
 }
 
 
 bool
 Assembler::oom() const
--- a/js/src/jit/arm/Assembler-arm.h
+++ b/js/src/jit/arm/Assembler-arm.h
@@ -80,27 +80,29 @@ class ABIArgGenerator
     uint32_t stackBytesConsumedSoFar() const { return stackOffset_; }
     static const Register NonArgReturnVolatileReg0;
     static const Register NonArgReturnVolatileReg1;
 };
 
 static MOZ_CONSTEXPR_VAR Register PreBarrierReg = r1;
 
 static MOZ_CONSTEXPR_VAR Register InvalidReg = { Registers::invalid_reg };
-static MOZ_CONSTEXPR_VAR FloatRegister InvalidFloatReg = { FloatRegisters::invalid_freg };
+static MOZ_CONSTEXPR_VAR FloatRegister InvalidFloatReg(FloatRegisters::invalid_freg);
 
 static MOZ_CONSTEXPR_VAR Register JSReturnReg_Type = r3;
 static MOZ_CONSTEXPR_VAR Register JSReturnReg_Data = r2;
 static MOZ_CONSTEXPR_VAR Register StackPointer = sp;
 static MOZ_CONSTEXPR_VAR Register FramePointer = InvalidReg;
 static MOZ_CONSTEXPR_VAR Register ReturnReg = r0;
-static MOZ_CONSTEXPR_VAR FloatRegister ReturnFloatReg = { FloatRegisters::d0 };
-static MOZ_CONSTEXPR_VAR FloatRegister ScratchFloatReg = { FloatRegisters::d15 };
+static MOZ_CONSTEXPR_VAR FloatRegister ReturnFloat32Reg(FloatRegisters::d0);
+static MOZ_CONSTEXPR_VAR FloatRegister ReturnDoubleReg(FloatRegisters::d0);
+static MOZ_CONSTEXPR_VAR FloatRegister ScratchFloat32Reg(FloatRegisters::d15);
+static MOZ_CONSTEXPR_VAR FloatRegister ScratchDoubleReg(FloatRegisters::d15);
 
-static MOZ_CONSTEXPR_VAR FloatRegister NANReg = { FloatRegisters::d14 };
+static MOZ_CONSTEXPR_VAR FloatRegister NANReg(FloatRegisters::d14);
 
 // Registers used in the GenerateFFIIonExit Enable Activation block.
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegCallee = r4;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE0 = r0;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE1 = r1;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE2 = r2;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE3 = r3;
 
@@ -108,32 +110,32 @@ static MOZ_CONSTEXPR_VAR Register AsmJSI
 // None of these may be the second scratch register (lr).
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegReturnData = r2;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegReturnType = r3;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD0 = r0;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD1 = r1;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD2 = r4;
 
 
-static MOZ_CONSTEXPR_VAR FloatRegister d0  = {FloatRegisters::d0};
-static MOZ_CONSTEXPR_VAR FloatRegister d1  = {FloatRegisters::d1};
-static MOZ_CONSTEXPR_VAR FloatRegister d2  = {FloatRegisters::d2};
-static MOZ_CONSTEXPR_VAR FloatRegister d3  = {FloatRegisters::d3};
-static MOZ_CONSTEXPR_VAR FloatRegister d4  = {FloatRegisters::d4};
-static MOZ_CONSTEXPR_VAR FloatRegister d5  = {FloatRegisters::d5};
-static MOZ_CONSTEXPR_VAR FloatRegister d6  = {FloatRegisters::d6};
-static MOZ_CONSTEXPR_VAR FloatRegister d7  = {FloatRegisters::d7};
-static MOZ_CONSTEXPR_VAR FloatRegister d8  = {FloatRegisters::d8};
-static MOZ_CONSTEXPR_VAR FloatRegister d9  = {FloatRegisters::d9};
-static MOZ_CONSTEXPR_VAR FloatRegister d10 = {FloatRegisters::d10};
-static MOZ_CONSTEXPR_VAR FloatRegister d11 = {FloatRegisters::d11};
-static MOZ_CONSTEXPR_VAR FloatRegister d12 = {FloatRegisters::d12};
-static MOZ_CONSTEXPR_VAR FloatRegister d13 = {FloatRegisters::d13};
-static MOZ_CONSTEXPR_VAR FloatRegister d14 = {FloatRegisters::d14};
-static MOZ_CONSTEXPR_VAR FloatRegister d15 = {FloatRegisters::d15};
+static MOZ_CONSTEXPR_VAR FloatRegister d0(FloatRegisters::d0);
+static MOZ_CONSTEXPR_VAR FloatRegister d1(FloatRegisters::d1);
+static MOZ_CONSTEXPR_VAR FloatRegister d2(FloatRegisters::d2);
+static MOZ_CONSTEXPR_VAR FloatRegister d3(FloatRegisters::d3);
+static MOZ_CONSTEXPR_VAR FloatRegister d4(FloatRegisters::d4);
+static MOZ_CONSTEXPR_VAR FloatRegister d5(FloatRegisters::d5);
+static MOZ_CONSTEXPR_VAR FloatRegister d6(FloatRegisters::d6);
+static MOZ_CONSTEXPR_VAR FloatRegister d7(FloatRegisters::d7);
+static MOZ_CONSTEXPR_VAR FloatRegister d8(FloatRegisters::d8);
+static MOZ_CONSTEXPR_VAR FloatRegister d9(FloatRegisters::d9);
+static MOZ_CONSTEXPR_VAR FloatRegister d10(FloatRegisters::d10);
+static MOZ_CONSTEXPR_VAR FloatRegister d11(FloatRegisters::d11);
+static MOZ_CONSTEXPR_VAR FloatRegister d12(FloatRegisters::d12);
+static MOZ_CONSTEXPR_VAR FloatRegister d13(FloatRegisters::d13);
+static MOZ_CONSTEXPR_VAR FloatRegister d14(FloatRegisters::d14);
+static MOZ_CONSTEXPR_VAR FloatRegister d15(FloatRegisters::d15);
 
 // For maximal awesomeness, 8 should be sufficent.
 // ldrd/strd (dual-register load/store) operate in a single cycle
 // when the address they are dealing with is 8 byte aligned.
 // Also, the ARM abi wants the stack to be 8 byte aligned at
 // function boundaries.  I'm trying to make sure this is always true.
 static const uint32_t StackAlignment = 8;
 static const uint32_t CodeAlignment = 8;
@@ -164,109 +166,19 @@ Register toRM (Instruction &i);
 Register toRD (Instruction &i);
 Register toR (Instruction &i);
 
 class VFPRegister;
 uint32_t VD(VFPRegister vr);
 uint32_t VN(VFPRegister vr);
 uint32_t VM(VFPRegister vr);
 
-class VFPRegister
-{
-  public:
-    // What type of data is being stored in this register?
-    // UInt / Int are specifically for vcvt, where we need
-    // to know how the data is supposed to be converted.
-    enum RegType {
-        Double = 0x0,
-        Single = 0x1,
-        UInt   = 0x2,
-        Int    = 0x3
-    };
-
-  protected:
-    RegType kind : 2;
-    // ARM doesn't have more than 32 registers...
-    // don't take more bits than we'll need.
-    // Presently, I don't have plans to address the upper
-    // and lower halves of the double registers seprately, so
-    // 5 bits should suffice.  If I do decide to address them seprately
-    // (vmov, I'm looking at you), I will likely specify it as a separate
-    // field.
-    uint32_t _code : 5;
-    bool _isInvalid : 1;
-    bool _isMissing : 1;
-
-    VFPRegister(int  r, RegType k)
-      : kind(k), _code (r), _isInvalid(false), _isMissing(false)
-    { }
-
-  public:
-    VFPRegister()
-      : _isInvalid(true), _isMissing(false)
-    { }
-
-    VFPRegister(bool b)
-      : _isInvalid(false), _isMissing(b)
-    { }
-
-    VFPRegister(FloatRegister fr)
-      : kind(Double), _code(fr.code()), _isInvalid(false), _isMissing(false)
-    {
-        JS_ASSERT(_code == (unsigned)fr.code());
-    }
-
-    VFPRegister(FloatRegister fr, RegType k)
-      : kind(k), _code (fr.code()), _isInvalid(false), _isMissing(false)
-    {
-        JS_ASSERT(_code == (unsigned)fr.code());
-    }
-    bool isDouble() const { return kind == Double; }
-    bool isSingle() const { return kind == Single; }
-    bool isFloat() const { return (kind == Double) || (kind == Single); }
-    bool isInt() const { return (kind == UInt) || (kind == Int); }
-    bool isSInt() const { return kind == Int; }
-    bool isUInt() const { return kind == UInt; }
-    bool equiv(VFPRegister other) const { return other.kind == kind; }
-    size_t size() const { return (kind == Double) ? 8 : 4; }
-    bool isInvalid();
-    bool isMissing();
-
-    VFPRegister doubleOverlay() const;
-    VFPRegister singleOverlay() const;
-    VFPRegister sintOverlay() const;
-    VFPRegister uintOverlay() const;
-
-    struct VFPRegIndexSplit;
-    VFPRegIndexSplit encode();
-
-    // for serializing values
-    struct VFPRegIndexSplit {
-        const uint32_t block : 4;
-        const uint32_t bit : 1;
-
-      private:
-        friend VFPRegIndexSplit js::jit::VFPRegister::encode();
-
-        VFPRegIndexSplit (uint32_t block_, uint32_t bit_)
-          : block(block_), bit(bit_)
-        {
-            JS_ASSERT (block == block_);
-            JS_ASSERT(bit == bit_);
-        }
-    };
-
-    uint32_t code() const {
-        return _code;
-    }
-};
-
 // For being passed into the generic vfp instruction generator when
 // there is an instruction that only takes two registers
-extern VFPRegister NoVFPRegister;
+static MOZ_CONSTEXPR_VAR VFPRegister NoVFPRegister(VFPRegister::Double, 0, false, true);
 
 struct ImmTag : public Imm32
 {
     ImmTag(JSValueTag mask)
       : Imm32(int32_t(mask))
     { }
 };
 
--- a/js/src/jit/arm/BaselineIC-arm.cpp
+++ b/js/src/jit/arm/BaselineIC-arm.cpp
@@ -182,18 +182,18 @@ ICBinaryArith_Int32::Compiler::generateS
             Label toUint;
             masm.j(Assembler::LessThan, &toUint);
 
             // Move result and box for return.
             masm.mov(scratchReg, R0.payloadReg());
             EmitReturnFromIC(masm);
 
             masm.bind(&toUint);
-            masm.convertUInt32ToDouble(scratchReg, ScratchFloatReg);
-            masm.boxDouble(ScratchFloatReg, R0);
+            masm.convertUInt32ToDouble(scratchReg, ScratchDoubleReg);
+            masm.boxDouble(ScratchDoubleReg, R0);
         } else {
             masm.j(Assembler::LessThan, &failure);
             // Move result for return.
             masm.mov(scratchReg, R0.payloadReg());
         }
         break;
       default:
         MOZ_ASSUME_UNREACHABLE("Unhandled op for BinaryArith_Int32.");
--- a/js/src/jit/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -983,24 +983,24 @@ bool
 CodeGeneratorARM::visitPowHalfD(LPowHalfD *ins)
 {
     FloatRegister input = ToFloatRegister(ins->input());
     FloatRegister output = ToFloatRegister(ins->output());
 
     Label done;
 
     // Masm.pow(-Infinity, 0.5) == Infinity.
-    masm.ma_vimm(NegativeInfinity<double>(), ScratchFloatReg);
-    masm.compareDouble(input, ScratchFloatReg);
-    masm.ma_vneg(ScratchFloatReg, output, Assembler::Equal);
+    masm.ma_vimm(NegativeInfinity<double>(), ScratchDoubleReg);
+    masm.compareDouble(input, ScratchDoubleReg);
+    masm.ma_vneg(ScratchDoubleReg, output, Assembler::Equal);
     masm.ma_b(&done, Assembler::Equal);
 
     // Math.pow(-0, 0.5) == 0 == Math.pow(0, 0.5). Adding 0 converts any -0 to 0.
-    masm.ma_vimm(0.0, ScratchFloatReg);
-    masm.ma_vadd(ScratchFloatReg, input, output);
+    masm.ma_vimm(0.0, ScratchDoubleReg);
+    masm.ma_vadd(ScratchDoubleReg, input, output);
     masm.ma_vsqrt(output, output);
 
     masm.bind(&done);
     return true;
 }
 
 MoveOperand
 CodeGeneratorARM::toMoveOperand(const LAllocation *a) const
@@ -1246,18 +1246,18 @@ CodeGeneratorARM::visitRoundF(LRoundF *l
     if (!bailoutFrom(&bail, lir->snapshot()))
         return false;
     return true;
 }
 
 void
 CodeGeneratorARM::emitRoundDouble(FloatRegister src, Register dest, Label *fail)
 {
-    masm.ma_vcvt_F64_I32(src, ScratchFloatReg);
-    masm.ma_vxfer(ScratchFloatReg, dest);
+    masm.ma_vcvt_F64_I32(src, ScratchDoubleReg);
+    masm.ma_vxfer(ScratchDoubleReg, dest);
     masm.ma_cmp(dest, Imm32(0x7fffffff));
     masm.ma_cmp(dest, Imm32(0x80000000), Assembler::NotEqual);
     masm.ma_b(fail, Assembler::Equal);
 }
 
 bool
 CodeGeneratorARM::visitTruncateDToInt32(LTruncateDToInt32 *ins)
 {
@@ -1349,18 +1349,18 @@ bool
 CodeGeneratorARM::visitBoxFloatingPoint(LBoxFloatingPoint *box)
 {
     const LDefinition *payload = box->getDef(PAYLOAD_INDEX);
     const LDefinition *type = box->getDef(TYPE_INDEX);
     const LAllocation *in = box->getOperand(0);
 
     FloatRegister reg = ToFloatRegister(in);
     if (box->type() == MIRType_Float32) {
-        masm.convertFloat32ToDouble(reg, ScratchFloatReg);
-        reg = ScratchFloatReg;
+        masm.convertFloat32ToDouble(reg, ScratchFloat32Reg);
+        reg = ScratchFloat32Reg;
     }
 
     //masm.as_vxfer(ToRegister(payload), ToRegister(type),
     //              VFPRegister(ToFloatRegister(in)), Assembler::FloatToCore);
     masm.ma_vxfer(VFPRegister(reg), ToRegister(payload), ToRegister(type));
     return true;
 }
 
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -51,18 +51,18 @@ MacroAssemblerARM::convertInt32ToDouble(
     as_vxfer(src, InvalidReg, dest.sintOverlay(),
              CoreToFloat);
     as_vcvt(dest, dest.sintOverlay());
 }
 
 void
 MacroAssemblerARM::convertInt32ToDouble(const Address &src, FloatRegister dest)
 {
-    ma_vldr(Operand(src), ScratchFloatReg);
-    as_vcvt(dest, VFPRegister(ScratchFloatReg).sintOverlay());
+    ma_vldr(Operand(src), ScratchDoubleReg);
+    as_vcvt(dest, VFPRegister(ScratchDoubleReg).sintOverlay());
 }
 
 void
 MacroAssemblerARM::convertUInt32ToDouble(Register src, FloatRegister dest_)
 {
     // direct conversions aren't possible.
     VFPRegister dest = VFPRegister(dest_);
     as_vxfer(src, InvalidReg, dest.uintOverlay(), CoreToFloat);
@@ -89,38 +89,40 @@ void MacroAssemblerARM::convertDoubleToF
 //        then it was clamped to INT_MIN/INT_MAX, and we can test it.
 //        NOTE: if the value really was supposed to be INT_MAX / INT_MIN
 //        then it will be wrong.
 // 2) convert the floating point value to an integer, if it did not fit,
 //        then it set one or two bits in the fpcsr.  Check those.
 void
 MacroAssemblerARM::branchTruncateDouble(FloatRegister src, Register dest, Label *fail)
 {
-    ma_vcvt_F64_I32(src, ScratchFloatReg);
-    ma_vxfer(ScratchFloatReg, dest);
+    FloatRegister ScratchSIntReg = ScratchDoubleReg.sintOverlay();
+    ma_vcvt_F64_I32(src, ScratchSIntReg);
+    ma_vxfer(ScratchSIntReg, dest);
     ma_cmp(dest, Imm32(0x7fffffff));
     ma_cmp(dest, Imm32(0x80000000), Assembler::NotEqual);
     ma_b(fail, Assembler::Equal);
 }
 
 // Checks whether a double is representable as a 32-bit integer. If so, the
 // integer is written to the output register. Otherwise, a bailout is taken to
 // the given snapshot. This function overwrites the scratch float register.
 void
 MacroAssemblerARM::convertDoubleToInt32(FloatRegister src, Register dest,
                                         Label *fail, bool negativeZeroCheck)
 {
     // convert the floating point value to an integer, if it did not fit,
     //     then when we convert it *back* to  a float, it will have a
     //     different value, which we can test.
-    ma_vcvt_F64_I32(src, ScratchFloatReg);
+    FloatRegister ScratchSIntReg = ScratchDoubleReg.sintOverlay();
+    ma_vcvt_F64_I32(src, ScratchSIntReg);
     // move the value into the dest register.
-    ma_vxfer(ScratchFloatReg, dest);
-    ma_vcvt_I32_F64(ScratchFloatReg, ScratchFloatReg);
-    ma_vcmp(src, ScratchFloatReg);
+    ma_vxfer(ScratchSIntReg, dest);
+    ma_vcvt_I32_F64(ScratchSIntReg, ScratchDoubleReg);
+    ma_vcmp(src, ScratchDoubleReg);
     as_vmrs(pc);
     ma_b(fail, Assembler::VFP_NotEqualOrUnordered);
 
     if (negativeZeroCheck) {
         ma_cmp(dest, Imm32(0));
         // Test and bail for -0.0, when integer result is 0
         // Move the top word of the double into the output reg, if it is non-zero,
         // then the original value was -0.0
@@ -135,21 +137,21 @@ MacroAssemblerARM::convertDoubleToInt32(
 // the given snapshot. This function overwrites the scratch float register.
 void
 MacroAssemblerARM::convertFloat32ToInt32(FloatRegister src, Register dest,
                                          Label *fail, bool negativeZeroCheck)
 {
     // convert the floating point value to an integer, if it did not fit,
     //     then when we convert it *back* to  a float, it will have a
     //     different value, which we can test.
-    ma_vcvt_F32_I32(src, ScratchFloatReg);
+    ma_vcvt_F32_I32(src, ScratchFloat32Reg.sintOverlay());
     // move the value into the dest register.
-    ma_vxfer(ScratchFloatReg, dest);
-    ma_vcvt_I32_F32(ScratchFloatReg, ScratchFloatReg);
-    ma_vcmp_f32(src, ScratchFloatReg);
+    ma_vxfer(ScratchFloat32Reg, dest);
+    ma_vcvt_I32_F32(ScratchFloat32Reg.sintOverlay(), ScratchFloat32Reg);
+    ma_vcmp_f32(src, ScratchFloat32Reg);
     as_vmrs(pc);
     ma_b(fail, Assembler::VFP_NotEqualOrUnordered);
 
     if (negativeZeroCheck) {
         ma_cmp(dest, Imm32(0));
         // Test and bail for -0.0, when integer result is 0
         // Move the float into the output reg, and if it is non-zero then
         // the original value was -0.0
@@ -161,36 +163,35 @@ MacroAssemblerARM::convertFloat32ToInt32
 
 void
 MacroAssemblerARM::convertFloat32ToDouble(FloatRegister src, FloatRegister dest) {
     as_vcvt(VFPRegister(dest), VFPRegister(src).singleOverlay());
 }
 
 void
 MacroAssemblerARM::branchTruncateFloat32(FloatRegister src, Register dest, Label *fail) {
-    ma_vcvt_F32_I32(src, ScratchFloatReg);
-    ma_vxfer(ScratchFloatReg, dest);
+    ma_vcvt_F32_I32(src, ScratchFloat32Reg.sintOverlay());
+    ma_vxfer(ScratchFloat32Reg, dest);
     ma_cmp(dest, Imm32(0x7fffffff));
     ma_cmp(dest, Imm32(0x80000000), Assembler::NotEqual);
     ma_b(fail, Assembler::Equal);
 }
 
 void
-MacroAssemblerARM::convertInt32ToFloat32(Register src, FloatRegister dest_) {
+MacroAssemblerARM::convertInt32ToFloat32(Register src, FloatRegister dest) {
     // direct conversions aren't possible.
-    VFPRegister dest = VFPRegister(dest_).singleOverlay();
     as_vxfer(src, InvalidReg, dest.sintOverlay(),
              CoreToFloat);
-    as_vcvt(dest, dest.sintOverlay());
+    as_vcvt(dest.singleOverlay(), dest.sintOverlay());
 }
 
 void
 MacroAssemblerARM::convertInt32ToFloat32(const Address &src, FloatRegister dest) {
-    ma_vldr(Operand(src), ScratchFloatReg);
-    as_vcvt(dest, VFPRegister(ScratchFloatReg).sintOverlay());
+    ma_vldr(Operand(src), ScratchFloat32Reg);
+    as_vcvt(dest, VFPRegister(ScratchFloat32Reg).sintOverlay());
 }
 
 void
 MacroAssemblerARM::addDouble(FloatRegister src, FloatRegister dest)
 {
     ma_vadd(dest, src, dest);
 }
 
@@ -1552,51 +1553,59 @@ void
 MacroAssemblerARM::ma_vcmpz_f32(FloatRegister src1, Condition cc)
 {
     as_vcmpz(VFPRegister(src1).singleOverlay(), cc);
 }
 
 void
 MacroAssemblerARM::ma_vcvt_F64_I32(FloatRegister src, FloatRegister dest, Condition cc)
 {
-    as_vcvt(VFPRegister(dest).sintOverlay(), VFPRegister(src), false, cc);
+    JS_ASSERT(src.isDouble());
+    JS_ASSERT(dest.isSInt());
+    as_vcvt(dest, src, false, cc);
 }
 void
 MacroAssemblerARM::ma_vcvt_F64_U32(FloatRegister src, FloatRegister dest, Condition cc)
 {
-    as_vcvt(VFPRegister(dest).uintOverlay(), VFPRegister(src), false, cc);
-}
-void
-MacroAssemblerARM::ma_vcvt_I32_F64(FloatRegister dest, FloatRegister src, Condition cc)
-{
-    as_vcvt(VFPRegister(dest), VFPRegister(src).sintOverlay(), false, cc);
-}
-void
-MacroAssemblerARM::ma_vcvt_U32_F64(FloatRegister dest, FloatRegister src, Condition cc)
-{
-    as_vcvt(VFPRegister(dest), VFPRegister(src).uintOverlay(), false, cc);
+    JS_ASSERT(src.isDouble());
+    JS_ASSERT(dest.isUInt());
+    as_vcvt(dest, src, false, cc);
+}
+void
+MacroAssemblerARM::ma_vcvt_I32_F64(FloatRegister src, FloatRegister dest, Condition cc)
+{
+    JS_ASSERT(src.isSInt());
+    JS_ASSERT(dest.isDouble());
+    as_vcvt(dest, src, false, cc);
+}
+void
+MacroAssemblerARM::ma_vcvt_U32_F64(FloatRegister src, FloatRegister dest, Condition cc)
+{
+    JS_ASSERT(src.isUInt());
+    JS_ASSERT(dest.isDouble());
+    as_vcvt(dest, src, false, cc);
 }
 
 void
 MacroAssemblerARM::ma_vcvt_F32_I32(FloatRegister src, FloatRegister dest, Condition cc)
 {
     as_vcvt(VFPRegister(dest).sintOverlay(), VFPRegister(src).singleOverlay(), false, cc);
 }
 void
 MacroAssemblerARM::ma_vcvt_F32_U32(FloatRegister src, FloatRegister dest, Condition cc)
 {
     as_vcvt(VFPRegister(dest).uintOverlay(), VFPRegister(src).singleOverlay(), false, cc);
 }
 void
-MacroAssemblerARM::ma_vcvt_I32_F32(FloatRegister dest, FloatRegister src, Condition cc)
+MacroAssemblerARM::ma_vcvt_I32_F32(FloatRegister src, FloatRegister dest, Condition cc)
 {
     as_vcvt(VFPRegister(dest).singleOverlay(), VFPRegister(src).sintOverlay(), false, cc);
 }
 void
-MacroAssemblerARM::ma_vcvt_U32_F32(FloatRegister dest, FloatRegister src, Condition cc)
+MacroAssemblerARM::ma_vcvt_U32_F32(FloatRegister src, FloatRegister dest, Condition cc)
 {
     as_vcvt(VFPRegister(dest).singleOverlay(), VFPRegister(src).uintOverlay(), false, cc);
 }
 
 void
 MacroAssemblerARM::ma_vxfer(FloatRegister src, Register dest, Condition cc)
 {
     as_vxfer(dest, InvalidReg, VFPRegister(src).singleOverlay(), FloatToCore, cc);
@@ -1609,28 +1618,16 @@ MacroAssemblerARM::ma_vxfer(FloatRegiste
 }
 
 void
 MacroAssemblerARM::ma_vxfer(Register src1, Register src2, FloatRegister dest, Condition cc)
 {
     as_vxfer(src1, src2, VFPRegister(dest), CoreToFloat, cc);
 }
 
-void
-MacroAssemblerARM::ma_vxfer(VFPRegister src, Register dest, Condition cc)
-{
-    as_vxfer(dest, InvalidReg, src, FloatToCore, cc);
-}
-
-void
-MacroAssemblerARM::ma_vxfer(VFPRegister src, Register dest1, Register dest2, Condition cc)
-{
-    as_vxfer(dest1, dest2, src, FloatToCore, cc);
-}
-
 BufferOffset
 MacroAssemblerARM::ma_vdtr(LoadStore ls, const Operand &addr, VFPRegister rt, Condition cc)
 {
     int off = addr.disp();
     JS_ASSERT((off & 3) == 0);
     Register base = Register::FromCode(addr.base());
     if (off > -1024 && off < 1024)
         return as_vdtr(ls, rt, addr.toVFPAddr(), cc);
@@ -2434,26 +2431,26 @@ MacroAssemblerARMCompat::storePtr(Regist
     movePtr(ImmWord(uintptr_t(dest.addr)), ScratchRegister);
     storePtr(src, Address(ScratchRegister, 0x0));
 }
 
 // Note: this function clobbers the input register.
 void
 MacroAssembler::clampDoubleToUint8(FloatRegister input, Register output)
 {
-    JS_ASSERT(input != ScratchFloatReg);
-    ma_vimm(0.5, ScratchFloatReg);
+    JS_ASSERT(input != ScratchDoubleReg);
+    ma_vimm(0.5, ScratchDoubleReg);
     if (HasVFPv3()) {
         Label notSplit;
-        ma_vadd(input, ScratchFloatReg, ScratchFloatReg);
+        ma_vadd(input, ScratchDoubleReg, ScratchDoubleReg);
         // Convert the double into an unsigned fixed point value with 24 bits of
         // precision. The resulting number will look like 0xII.DDDDDD
-        as_vcvtFixed(ScratchFloatReg, false, 24, true);
+        as_vcvtFixed(ScratchDoubleReg, false, 24, true);
         // Move the fixed point value into an integer register
-        as_vxfer(output, InvalidReg, ScratchFloatReg, FloatToCore);
+        as_vxfer(output, InvalidReg, ScratchFloat32Reg.uintOverlay(), FloatToCore);
         // see if this value *might* have been an exact integer after adding 0.5
         // This tests the 1/2 through 1/16,777,216th places, but 0.5 needs to be tested out to
         // the 1/140,737,488,355,328th place.
         ma_tst(output, Imm32(0x00ffffff));
         // convert to a uint8 by shifting out all of the fraction bits
         ma_lsr(Imm32(24), output, output);
         // If any of the bottom 24 bits were non-zero, then we're good, since this number
         // can't be exactly XX.0
@@ -2463,31 +2460,31 @@ MacroAssembler::clampDoubleToUint8(Float
         // If the lower 32 bits of the double were 0, then this was an exact number,
         // and it should be even.
         ma_bic(Imm32(1), output, NoSetCond, Zero);
         bind(&notSplit);
     } else {
         Label outOfRange;
         ma_vcmpz(input);
         // do the add, in place so we can reference it later
-        ma_vadd(input, ScratchFloatReg, input);
+        ma_vadd(input, ScratchDoubleReg, input);
         // do the conversion to an integer.
-        as_vcvt(VFPRegister(ScratchFloatReg).uintOverlay(), VFPRegister(input));
+        as_vcvt(VFPRegister(ScratchDoubleReg).uintOverlay(), VFPRegister(input));
         // copy the converted value out
-        as_vxfer(output, InvalidReg, ScratchFloatReg, FloatToCore);
+        as_vxfer(output, InvalidReg, ScratchDoubleReg, FloatToCore);
         as_vmrs(pc);
         ma_mov(Imm32(0), output, NoSetCond, Overflow);  // NaN => 0
         ma_b(&outOfRange, Overflow);  // NaN
         ma_cmp(output, Imm32(0xff));
         ma_mov(Imm32(0xff), output, NoSetCond, Above);
         ma_b(&outOfRange, Above);
         // convert it back to see if we got the same value back
-        as_vcvt(ScratchFloatReg, VFPRegister(ScratchFloatReg).uintOverlay());
+        as_vcvt(ScratchDoubleReg, VFPRegister(ScratchDoubleReg).uintOverlay());
         // do the check
-        as_vcmp(ScratchFloatReg, input);
+        as_vcmp(ScratchDoubleReg, input);
         as_vmrs(pc);
         ma_bic(Imm32(1), output, NoSetCond, Zero);
         bind(&outOfRange);
     }
 }
 
 void
 MacroAssemblerARMCompat::cmp32(Register lhs, Imm32 rhs)
@@ -3095,17 +3092,16 @@ void
 MacroAssemblerARMCompat::unboxNonDouble(const Address &src, Register dest)
 {
     ma_ldr(payloadOf(src), dest);
 }
 
 void
 MacroAssemblerARMCompat::unboxDouble(const ValueOperand &operand, FloatRegister dest)
 {
-    JS_ASSERT(dest != ScratchFloatReg);
     as_vxfer(operand.payloadReg(), operand.typeReg(),
              VFPRegister(dest), CoreToFloat);
 }
 
 void
 MacroAssemblerARMCompat::unboxDouble(const Address &src, FloatRegister dest)
 {
     ma_vldr(Operand(src), dest);
@@ -3157,19 +3153,19 @@ MacroAssemblerARMCompat::boolValueToDoub
 }
 
 void
 MacroAssemblerARMCompat::int32ValueToDouble(const ValueOperand &operand, FloatRegister dest)
 {
     // transfer the integral value to a floating point register
     VFPRegister vfpdest = VFPRegister(dest);
     as_vxfer(operand.payloadReg(), InvalidReg,
-             vfpdest.sintOverlay(), CoreToFloat);
+             ScratchFloat32Reg.sintOverlay(), CoreToFloat);
     // convert the value to a double.
-    as_vcvt(vfpdest, vfpdest.sintOverlay());
+    as_vcvt(vfpdest, ScratchFloat32Reg.sintOverlay());
 }
 
 void
 MacroAssemblerARMCompat::boolValueToFloat32(const ValueOperand &operand, FloatRegister dest)
 {
     VFPRegister d = VFPRegister(dest).singleOverlay();
     ma_vimm_f32(1.0, dest);
     ma_cmp(operand.payloadReg(), Imm32(0));
@@ -3934,23 +3930,23 @@ MacroAssemblerARMCompat::callWithABIPost
 {
     if (secondScratchReg_ != lr)
         ma_mov(secondScratchReg_, lr);
 
     switch (result) {
       case MoveOp::DOUBLE:
         if (!UseHardFpABI()) {
             // Move double from r0/r1 to ReturnFloatReg.
-            as_vxfer(r0, r1, ReturnFloatReg, CoreToFloat);
+            as_vxfer(r0, r1, ReturnDoubleReg, CoreToFloat);
             break;
         }
       case MoveOp::FLOAT32:
         if (!UseHardFpABI()) {
             // Move float32 from r0 to ReturnFloatReg.
-            as_vxfer(r0, InvalidReg, VFPRegister(d0).singleOverlay(), CoreToFloat);
+            as_vxfer(r0, InvalidReg, ReturnFloat32Reg.singleOverlay(), CoreToFloat);
             break;
         }
       case MoveOp::GENERAL:
         break;
 
       default:
         MOZ_ASSUME_UNREACHABLE("unexpected callWithABI result");
     }
@@ -4150,37 +4146,37 @@ MacroAssemblerARMCompat::floor(FloatRegi
     ma_b(&handleNeg, Assembler::Signed);
     // NaN is always a bail condition, just bail directly.
     ma_b(bail, Assembler::Overflow);
 
     // The argument is a positive number, truncation is the path to glory;
     // Since it is known to be > 0.0, explicitly convert to a larger range,
     // then a value that rounds to INT_MAX is explicitly different from an
     // argument that clamps to INT_MAX
-    ma_vcvt_F64_U32(input, ScratchFloatReg);
-    ma_vxfer(VFPRegister(ScratchFloatReg).uintOverlay(), output);
+    ma_vcvt_F64_U32(input, ScratchDoubleReg.uintOverlay());
+    ma_vxfer(ScratchDoubleReg.uintOverlay(), output);
     ma_mov(output, output, SetCond);
     ma_b(bail, Signed);
     ma_b(&fin);
 
     bind(&handleZero);
     // Move the top word of the double into the output reg, if it is non-zero,
     // then the original value was -0.0
     as_vxfer(output, InvalidReg, input, FloatToCore, Always, 1);
     ma_cmp(output, Imm32(0));
     ma_b(bail, NonZero);
     ma_b(&fin);
 
     bind(&handleNeg);
     // Negative case, negate, then start dancing
     ma_vneg(input, input);
-    ma_vcvt_F64_U32(input, ScratchFloatReg);
-    ma_vxfer(VFPRegister(ScratchFloatReg).uintOverlay(), output);
-    ma_vcvt_U32_F64(ScratchFloatReg, ScratchFloatReg);
-    compareDouble(ScratchFloatReg, input);
+    ma_vcvt_F64_U32(input, ScratchDoubleReg.uintOverlay());
+    ma_vxfer(ScratchDoubleReg.uintOverlay(), output);
+    ma_vcvt_U32_F64(ScratchDoubleReg.uintOverlay(), ScratchDoubleReg);
+    compareDouble(ScratchDoubleReg, input);
     ma_add(output, Imm32(1), output, NoSetCond, NotEqual);
     // Negate the output.  Since INT_MIN < -INT_MAX, even after adding 1,
     // the result will still be a negative number
     ma_rsb(output, Imm32(0), output, SetCond);
     // Flip the negated input back to its original value.
     ma_vneg(input, input);
     // If the result looks non-negative, then this value didn't actually fit into
     // the int range, and special handling is required.
@@ -4202,37 +4198,37 @@ MacroAssemblerARMCompat::floorf(FloatReg
     ma_b(&handleNeg, Assembler::Signed);
     // NaN is always a bail condition, just bail directly.
     ma_b(bail, Assembler::Overflow);
 
     // The argument is a positive number, truncation is the path to glory;
     // Since it is known to be > 0.0, explicitly convert to a larger range,
     // then a value that rounds to INT_MAX is explicitly different from an
     // argument that clamps to INT_MAX
-    ma_vcvt_F32_U32(input, ScratchFloatReg);
-    ma_vxfer(VFPRegister(ScratchFloatReg).uintOverlay(), output);
+    ma_vcvt_F32_U32(input, ScratchFloat32Reg.uintOverlay());
+    ma_vxfer(VFPRegister(ScratchFloat32Reg).uintOverlay(), output);
     ma_mov(output, output, SetCond);
     ma_b(bail, Signed);
     ma_b(&fin);
 
     bind(&handleZero);
     // Move the top word of the double into the output reg, if it is non-zero,
     // then the original value was -0.0
     as_vxfer(output, InvalidReg, VFPRegister(input).singleOverlay(), FloatToCore, Always, 0);
     ma_cmp(output, Imm32(0));
     ma_b(bail, NonZero);
     ma_b(&fin);
 
     bind(&handleNeg);
     // Negative case, negate, then start dancing
     ma_vneg_f32(input, input);
-    ma_vcvt_F32_U32(input, ScratchFloatReg);
-    ma_vxfer(VFPRegister(ScratchFloatReg).uintOverlay(), output);
-    ma_vcvt_U32_F32(ScratchFloatReg, ScratchFloatReg);
-    compareFloat(ScratchFloatReg, input);
+    ma_vcvt_F32_U32(input, ScratchFloat32Reg.uintOverlay());
+    ma_vxfer(VFPRegister(ScratchFloat32Reg).uintOverlay(), output);
+    ma_vcvt_U32_F32(ScratchFloat32Reg.uintOverlay(), ScratchFloat32Reg);
+    compareFloat(ScratchFloat32Reg, input);
     ma_add(output, Imm32(1), output, NoSetCond, NotEqual);
     // Negate the output.  Since INT_MIN < -INT_MAX, even after adding 1,
     // the result will still be a negative number
     ma_rsb(output, Imm32(0), output, SetCond);
     // Flip the negated input back to its original value.
     ma_vneg_f32(input, input);
     // If the result looks non-negative, then this value didn't actually fit into
     // the int range, and special handling is required.
@@ -4253,44 +4249,45 @@ MacroAssemblerARMCompat::ceil(FloatRegis
     compareDouble(input, InvalidFloatReg);
     // NaN is always a bail condition, just bail directly.
     ma_b(bail, Assembler::Overflow);
     ma_b(&handleZero, Assembler::Equal);
     ma_b(&handlePos, Assembler::NotSigned);
 
     // We are in the ]-Inf; 0[ range
     // If we are in the ]-1; 0[ range => bailout
-    ma_vimm(-1.0, ScratchFloatReg);
-    compareDouble(input, ScratchFloatReg);
+    ma_vimm(-1.0, ScratchDoubleReg);
+    compareDouble(input, ScratchDoubleReg);
     ma_b(bail, Assembler::GreaterThan);
 
     // We are in the ]-Inf; -1] range: ceil(x) == -floor(-x) and floor can
     // be computed with direct truncation here (x > 0).
-    ma_vneg(input, ScratchFloatReg);
-    ma_vcvt_F64_U32(ScratchFloatReg, ScratchFloatReg);
-    ma_vxfer(VFPRegister(ScratchFloatReg).uintOverlay(), output);
+    ma_vneg(input, ScratchDoubleReg);
+    FloatRegister ScratchUIntReg = ScratchDoubleReg.uintOverlay();
+    ma_vcvt_F64_U32(ScratchDoubleReg, ScratchUIntReg);
+    ma_vxfer(ScratchUIntReg, output);
     ma_neg(output, output, SetCond);
     ma_b(bail, NotSigned);
     ma_b(&fin);
 
     // Test for 0.0 / -0.0: if the top word of the input double is not zero,
     // then it was -0 and we need to bail out.
     bind(&handleZero);
     as_vxfer(output, InvalidReg, input, FloatToCore, Always, 1);
     ma_cmp(output, Imm32(0));
     ma_b(bail, NonZero);
     ma_b(&fin);
 
     // We are in the ]0; +inf] range: truncate integer values, maybe add 1 for
     // non integer values, maybe bail if overflow.
     bind(&handlePos);
-    ma_vcvt_F64_U32(input, ScratchFloatReg);
-    ma_vxfer(VFPRegister(ScratchFloatReg).uintOverlay(), output);
-    ma_vcvt_U32_F64(ScratchFloatReg, ScratchFloatReg);
-    compareDouble(ScratchFloatReg, input);
+    ma_vcvt_F64_U32(input, ScratchUIntReg);
+    ma_vxfer(ScratchUIntReg, output);
+    ma_vcvt_U32_F64(ScratchUIntReg, ScratchDoubleReg);
+    compareDouble(ScratchDoubleReg, input);
     ma_add(output, Imm32(1), output, NoSetCond, NotEqual);
     // Bail out if the add overflowed or the result is non positive
     ma_mov(output, output, SetCond);
     ma_b(bail, Signed);
     ma_b(bail, Zero);
 
     bind(&fin);
 }
@@ -4305,44 +4302,45 @@ MacroAssemblerARMCompat::ceilf(FloatRegi
     compareFloat(input, InvalidFloatReg);
     // NaN is always a bail condition, just bail directly.
     ma_b(bail, Assembler::Overflow);
     ma_b(&handleZero, Assembler::Equal);
     ma_b(&handlePos, Assembler::NotSigned);
 
     // We are in the ]-Inf; 0[ range
     // If we are in the ]-1; 0[ range => bailout
-    ma_vimm_f32(-1.f, ScratchFloatReg);
-    compareFloat(input, ScratchFloatReg);
+    ma_vimm_f32(-1.f, ScratchFloat32Reg);
+    compareFloat(input, ScratchFloat32Reg);
     ma_b(bail, Assembler::GreaterThan);
 
     // We are in the ]-Inf; -1] range: ceil(x) == -floor(-x) and floor can
     // be computed with direct truncation here (x > 0).
-    ma_vneg_f32(input, ScratchFloatReg);
-    ma_vcvt_F32_U32(ScratchFloatReg, ScratchFloatReg);
-    ma_vxfer(VFPRegister(ScratchFloatReg).uintOverlay(), output);
+    ma_vneg_f32(input, ScratchFloat32Reg);
+    FloatRegister ScratchUIntReg = ScratchDoubleReg.uintOverlay();
+    ma_vcvt_F32_U32(ScratchFloat32Reg, ScratchUIntReg);
+    ma_vxfer(ScratchUIntReg, output);
     ma_neg(output, output, SetCond);
     ma_b(bail, NotSigned);
     ma_b(&fin);
 
     // Test for 0.0 / -0.0: if the top word of the input double is not zero,
     // then it was -0 and we need to bail out.
     bind(&handleZero);
     as_vxfer(output, InvalidReg, VFPRegister(input).singleOverlay(), FloatToCore, Always, 0);
     ma_cmp(output, Imm32(0));
     ma_b(bail, NonZero);
     ma_b(&fin);
 
     // We are in the ]0; +inf] range: truncate integer values, maybe add 1 for
     // non integer values, maybe bail if overflow.
     bind(&handlePos);
-    ma_vcvt_F32_U32(input, ScratchFloatReg);
-    ma_vxfer(VFPRegister(ScratchFloatReg).uintOverlay(), output);
-    ma_vcvt_U32_F32(ScratchFloatReg, ScratchFloatReg);
-    compareFloat(ScratchFloatReg, input);
+    ma_vcvt_F32_U32(input, ScratchUIntReg);
+    ma_vxfer(ScratchUIntReg, output);
+    ma_vcvt_U32_F32(ScratchUIntReg, ScratchFloat32Reg);
+    compareFloat(ScratchFloat32Reg, input);
     ma_add(output, Imm32(1), output, NoSetCond, NotEqual);
     // Bail out if the add overflowed or the result is non positive
     ma_mov(output, output, SetCond);
     ma_b(bail, Signed);
     ma_b(bail, Zero);
 
     bind(&fin);
 }
@@ -4378,55 +4376,55 @@ MacroAssemblerARMCompat::round(FloatRegi
     Label handleZero;
     Label handleNeg;
     Label fin;
     // Do a compare based on the original value, then do most other things based on the
     // shifted value.
     ma_vcmpz(input);
     // Adding 0.5 is technically incorrect!
     // We want to add 0.5 to negative numbers, and 0.49999999999999999 to positive numbers.
-    ma_vimm(0.5, ScratchFloatReg);
+    ma_vimm(0.5, ScratchDoubleReg);
     // Since we already know the sign bit, flip all numbers to be positive, stored in tmp.
     ma_vabs(input, tmp);
     // Add 0.5, storing the result into tmp.
-    ma_vadd(ScratchFloatReg, tmp, tmp);
+    ma_vadd(ScratchDoubleReg, tmp, tmp);
     as_vmrs(pc);
     ma_b(&handleZero, Assembler::Equal);
     ma_b(&handleNeg, Assembler::Signed);
     // NaN is always a bail condition, just bail directly.
     ma_b(bail, Assembler::Overflow);
 
     // The argument is a positive number, truncation is the path to glory;
     // Since it is known to be > 0.0, explicitly convert to a larger range,
     // then a value that rounds to INT_MAX is explicitly different from an
     // argument that clamps to INT_MAX
-    ma_vcvt_F64_U32(tmp, ScratchFloatReg);
-    ma_vxfer(VFPRegister(ScratchFloatReg).uintOverlay(), output);
+    ma_vcvt_F64_U32(tmp, ScratchDoubleReg.uintOverlay());
+    ma_vxfer(VFPRegister(ScratchDoubleReg).uintOverlay(), output);
     ma_mov(output, output, SetCond);
     ma_b(bail, Signed);
     ma_b(&fin);
 
     bind(&handleZero);
     // Move the top word of the double into the output reg, if it is non-zero,
     // then the original value was -0.0
     as_vxfer(output, InvalidReg, input, FloatToCore, Always, 1);
     ma_cmp(output, Imm32(0));
     ma_b(bail, NonZero);
     ma_b(&fin);
 
     bind(&handleNeg);
     // Negative case, negate, then start dancing.  This number may be positive, since we added 0.5
-    ma_vcvt_F64_U32(tmp, ScratchFloatReg);
-    ma_vxfer(VFPRegister(ScratchFloatReg).uintOverlay(), output);
+    ma_vcvt_F64_U32(tmp, ScratchDoubleReg.uintOverlay());
+    ma_vxfer(VFPRegister(ScratchDoubleReg).uintOverlay(), output);
 
     // -output is now a correctly rounded value, unless the original value was exactly
     // halfway between two integers, at which point, it has been rounded away from zero, when
     // it should be rounded towards \infty.
-    ma_vcvt_U32_F64(ScratchFloatReg, ScratchFloatReg);
-    compareDouble(ScratchFloatReg, tmp);
+    ma_vcvt_U32_F64(ScratchDoubleReg.uintOverlay(), ScratchDoubleReg);
+    compareDouble(ScratchDoubleReg, tmp);
     ma_sub(output, Imm32(1), output, NoSetCond, Equal);
     // Negate the output.  Since INT_MIN < -INT_MAX, even after adding 1,
     // the result will still be a negative number
     ma_rsb(output, Imm32(0), output, SetCond);
 
     // If the result looks non-negative, then this value didn't actually fit into
     // the int range, and special handling is required, or it was zero, which means
     // the result is actually -0.0 which also requires special handling.
@@ -4441,55 +4439,55 @@ MacroAssemblerARMCompat::roundf(FloatReg
     Label handleZero;
     Label handleNeg;
     Label fin;
     // Do a compare based on the original value, then do most other things based on the
     // shifted value.
     ma_vcmpz_f32(input);
     // Adding 0.5 is technically incorrect!
     // We want to add 0.5 to negative numbers, and 0.49999999999999999 to positive numbers.
-    ma_vimm_f32(0.5f, ScratchFloatReg);
+    ma_vimm_f32(0.5f, ScratchFloat32Reg);
     // Since we already know the sign bit, flip all numbers to be positive, stored in tmp.
     ma_vabs_f32(input, tmp);
     // Add 0.5, storing the result into tmp.
-    ma_vadd_f32(ScratchFloatReg, tmp, tmp);
+    ma_vadd_f32(ScratchFloat32Reg, tmp, tmp);
     as_vmrs(pc);
     ma_b(&handleZero, Assembler::Equal);
     ma_b(&handleNeg, Assembler::Signed);
     // NaN is always a bail condition, just bail directly.
     ma_b(bail, Assembler::Overflow);
 
     // The argument is a positive number, truncation is the path to glory;
     // Since it is known to be > 0.0, explicitly convert to a larger range,
     // then a value that rounds to INT_MAX is explicitly different from an
     // argument that clamps to INT_MAX
-    ma_vcvt_F32_U32(tmp, ScratchFloatReg);
-    ma_vxfer(VFPRegister(ScratchFloatReg).uintOverlay(), output);
+    ma_vcvt_F32_U32(tmp, ScratchFloat32Reg.uintOverlay());
+    ma_vxfer(VFPRegister(ScratchFloat32Reg).uintOverlay(), output);
     ma_mov(output, output, SetCond);
     ma_b(bail, Signed);
     ma_b(&fin);
 
     bind(&handleZero);
-    // Move the top word of the double into the output reg, if it is non-zero,
+    // Move the whole float32 into the output reg, if it is non-zero,
     // then the original value was -0.0
-    as_vxfer(output, InvalidReg, input, FloatToCore, Always, 1);
+    as_vxfer(output, InvalidReg, input, FloatToCore, Always, 0);
     ma_cmp(output, Imm32(0));
     ma_b(bail, NonZero);
     ma_b(&fin);
 
     bind(&handleNeg);
     // Negative case, negate, then start dancing.  This number may be positive, since we added 0.5
-    ma_vcvt_F32_U32(tmp, ScratchFloatReg);
-    ma_vxfer(VFPRegister(ScratchFloatReg).uintOverlay(), output);
+    ma_vcvt_F32_U32(tmp, ScratchFloat32Reg.uintOverlay());
+    ma_vxfer(VFPRegister(ScratchFloat32Reg).uintOverlay(), output);
 
     // -output is now a correctly rounded value, unless the original value was exactly
     // halfway between two integers, at which point, it has been rounded away from zero, when
     // it should be rounded towards \infty.
-    ma_vcvt_U32_F32(ScratchFloatReg, ScratchFloatReg);
-    compareFloat(ScratchFloatReg, tmp);
+    ma_vcvt_U32_F32(ScratchFloat32Reg.uintOverlay(), ScratchFloat32Reg);
+    compareFloat(ScratchFloat32Reg, tmp);
     ma_sub(output, Imm32(1), output, NoSetCond, Equal);
     // Negate the output.  Since INT_MIN < -INT_MAX, even after adding 1,
     // the result will still be a negative number
     ma_rsb(output, Imm32(0), output, SetCond);
 
     // If the result looks non-negative, then this value didn't actually fit into
     // the int range, and special handling is required, or it was zero, which means
     // the result is actually -0.0 which also requires special handling.
--- a/js/src/jit/arm/MacroAssembler-arm.h
+++ b/js/src/jit/arm/MacroAssembler-arm.h
@@ -366,18 +366,16 @@ class MacroAssemblerARM : public Assembl
     // source is F32, dest is I32
     void ma_vcvt_F32_I32(FloatRegister src, FloatRegister dest, Condition cc = Always);
     void ma_vcvt_F32_U32(FloatRegister src, FloatRegister dest, Condition cc = Always);
 
     // source is I32, dest is F32
     void ma_vcvt_I32_F32(FloatRegister src, FloatRegister dest, Condition cc = Always);
     void ma_vcvt_U32_F32(FloatRegister src, FloatRegister dest, Condition cc = Always);
 
-    void ma_vxfer(FloatRegister src, Register dest, Condition cc = Always);
-    void ma_vxfer(FloatRegister src, Register dest1, Register dest2, Condition cc = Always);
 
     void ma_vxfer(VFPRegister src, Register dest, Condition cc = Always);
     void ma_vxfer(VFPRegister src, Register dest1, Register dest2, Condition cc = Always);
 
     void ma_vxfer(Register src1, Register src2, FloatRegister dest, Condition cc = Always);
 
     BufferOffset ma_vdtr(LoadStore ls, const Operand &addr, VFPRegister dest, Condition cc = Always);
 
--- a/js/src/jit/arm/MoveEmitter-arm.cpp
+++ b/js/src/jit/arm/MoveEmitter-arm.cpp
@@ -107,17 +107,17 @@ MoveEmitterARM::breakCycle(const MoveOpe
     //   (B -> A)
     //
     // This case handles (A -> B), which we reach first. We save B, then allow
     // the original move to continue.
     switch (type) {
       case MoveOp::FLOAT32:
       case MoveOp::DOUBLE:
         if (to.isMemory()) {
-            FloatRegister temp = ScratchFloatReg;
+            FloatRegister temp = ScratchDoubleReg;
             masm.ma_vldr(toOperand(to, true), temp);
             masm.ma_vstr(temp, cycleSlot());
         } else {
             masm.ma_vstr(to.floatReg(), cycleSlot());
         }
         break;
       case MoveOp::INT32:
       case MoveOp::GENERAL:
@@ -148,17 +148,17 @@ MoveEmitterARM::completeCycle(const Move
     //   (B -> A)
     //
     // This case handles (B -> A), which we reach last. We emit a move from the
     // saved value of B, to A.
     switch (type) {
       case MoveOp::FLOAT32:
       case MoveOp::DOUBLE:
         if (to.isMemory()) {
-            FloatRegister temp = ScratchFloatReg;
+            FloatRegister temp = ScratchDoubleReg;
             masm.ma_vldr(cycleSlot(), temp);
             masm.ma_vstr(temp, toOperand(to, true));
         } else {
             masm.ma_vldr(cycleSlot(), to.floatReg());
         }
         break;
       case MoveOp::INT32:
       case MoveOp::GENERAL:
@@ -236,17 +236,17 @@ MoveEmitterARM::emitFloat32Move(const Mo
             masm.ma_vstr(VFPRegister(from.floatReg()).singleOverlay(),
                          toOperand(to, true));
     } else if (to.isFloatReg()) {
         masm.ma_vldr(toOperand(from, true),
                      VFPRegister(to.floatReg()).singleOverlay());
     } else {
         // Memory to memory move.
         JS_ASSERT(from.isMemory());
-        FloatRegister reg = ScratchFloatReg;
+        FloatRegister reg = ScratchFloat32Reg;
         masm.ma_vldr(toOperand(from, true),
                      VFPRegister(reg).singleOverlay());
         masm.ma_vstr(VFPRegister(reg).singleOverlay(),
                      toOperand(to, true));
     }
 }
 
 void
@@ -257,17 +257,17 @@ MoveEmitterARM::emitDoubleMove(const Mov
             masm.ma_vmov(from.floatReg(), to.floatReg());
         else
             masm.ma_vstr(from.floatReg(), toOperand(to, true));
     } else if (to.isFloatReg()) {
         masm.ma_vldr(toOperand(from, true), to.floatReg());
     } else {
         // Memory to memory move.
         JS_ASSERT(from.isMemory());
-        FloatRegister reg = ScratchFloatReg;
+        FloatRegister reg = ScratchDoubleReg;
         masm.ma_vldr(toOperand(from, true), reg);
         masm.ma_vstr(reg, toOperand(to, true));
     }
 }
 
 void
 MoveEmitterARM::emit(const MoveOp &move)
 {
--- a/js/src/jit/arm/Simulator-arm.cpp
+++ b/js/src/jit/arm/Simulator-arm.cpp
@@ -560,17 +560,17 @@ ArmDebugger::getValue(const char *desc, 
     if (strncmp(desc, "0x", 2) == 0)
         return sscanf(desc + 2, "%x", reinterpret_cast<uint32_t*>(value)) == 1;
     return sscanf(desc, "%u", reinterpret_cast<uint32_t*>(value)) == 1;
 }
 
 bool
 ArmDebugger::getVFPDoubleValue(const char *desc, double *value)
 {
-    FloatRegister reg = FloatRegister::FromName(desc);
+    FloatRegister reg(FloatRegister::FromName(desc));
     if (reg != InvalidFloatReg) {
         *value = sim_->get_double_from_d_register(reg.code());
         return true;
     }
     return false;
 }
 
 bool
--- a/js/src/jit/arm/Trampoline-arm.cpp
+++ b/js/src/jit/arm/Trampoline-arm.cpp
@@ -836,17 +836,17 @@ JitRuntime::generateVMWrapper(JSContext 
 
       case Type_Bool:
         masm.load8ZeroExtend(Address(sp, 0), ReturnReg);
         masm.freeStack(sizeof(int32_t));
         break;
 
       case Type_Double:
         if (cx->runtime()->jitSupportsFloatingPoint)
-            masm.loadDouble(Address(sp, 0), ReturnFloatReg);
+            masm.loadDouble(Address(sp, 0), ReturnDoubleReg);
         else
             masm.assumeUnreachable("Unable to load into float reg, with no FP support.");
         masm.freeStack(sizeof(double));
         break;
 
       default:
         JS_ASSERT(f.outParam == Type_Void);
         break;
--- a/js/src/jit/shared/CodeGenerator-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-shared.cpp
@@ -517,18 +517,27 @@ class VerifyOp
     VerifyOp(MacroAssembler &masm, Label *failure)
       : masm(masm), failure_(failure)
     {}
 
     void operator()(Register reg, Address dump) {
         masm.branchPtr(Assembler::NotEqual, dump, reg, failure_);
     }
     void operator()(FloatRegister reg, Address dump) {
-        masm.loadDouble(dump, ScratchFloatReg);
-        masm.branchDouble(Assembler::DoubleNotEqual, ScratchFloatReg, reg, failure_);
+        FloatRegister scratch;
+#ifdef JS_CODEGEN_ARM
+        if (reg.isDouble())
+            scratch = ScratchDoubleReg;
+        else
+            scratch = ScratchFloat32Reg;
+#else
+        scratch = ScratchFloat32Reg;
+#endif
+        masm.loadDouble(dump, scratch);
+        masm.branchDouble(Assembler::DoubleNotEqual, scratch, reg, failure_);
     }
 };
 
 void
 CodeGeneratorShared::verifyOsiPointRegs(LSafepoint *safepoint)
 {
     // Ensure the live registers stored by callVM did not change between
     // the call and this OsiPoint. Try-catch relies on this invariant.
--- a/js/src/jit/shared/CodeGenerator-x86-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-x86-shared.cpp
@@ -149,29 +149,29 @@ CodeGeneratorX86Shared::visitTestDAndBra
     //            ---------
     //      NaN    1  1  1
     //        >    0  0  0
     //        <    0  0  1
     //        =    1  0  0
     //
     // NaN is falsey, so comparing against 0 and then using the Z flag is
     // enough to determine which branch to take.
-    masm.xorpd(ScratchFloatReg, ScratchFloatReg);
-    masm.ucomisd(ToFloatRegister(opd), ScratchFloatReg);
+    masm.xorpd(ScratchDoubleReg, ScratchDoubleReg);
+    masm.ucomisd(ToFloatRegister(opd), ScratchDoubleReg);
     emitBranch(Assembler::NotEqual, test->ifTrue(), test->ifFalse());
     return true;
 }
 
 bool
 CodeGeneratorX86Shared::visitTestFAndBranch(LTestFAndBranch *test)
 {
     const LAllocation *opd = test->input();
     // ucomiss flags are the same as doubles; see comment above
-    masm.xorps(ScratchFloatReg, ScratchFloatReg);
-    masm.ucomiss(ToFloatRegister(opd), ScratchFloatReg);
+    masm.xorps(ScratchFloat32Reg, ScratchFloat32Reg);
+    masm.ucomiss(ToFloatRegister(opd), ScratchFloat32Reg);
     emitBranch(Assembler::NotEqual, test->ifTrue(), test->ifFalse());
     return true;
 }
 
 bool
 CodeGeneratorX86Shared::visitBitAndAndBranch(LBitAndAndBranch *baab)
 {
     if (baab->right()->isConstant())
@@ -265,35 +265,35 @@ CodeGeneratorX86Shared::visitNotD(LNotD 
     FloatRegister opd = ToFloatRegister(ins->input());
 
     // Not returns true if the input is a NaN. We don't have to worry about
     // it if we know the input is never NaN though.
     Assembler::NaNCond nanCond = Assembler::NaN_IsTrue;
     if (ins->mir()->operandIsNeverNaN())
         nanCond = Assembler::NaN_HandledByCond;
 
-    masm.xorpd(ScratchFloatReg, ScratchFloatReg);
-    masm.compareDouble(Assembler::DoubleEqualOrUnordered, opd, ScratchFloatReg);
+    masm.xorpd(ScratchDoubleReg, ScratchDoubleReg);
+    masm.compareDouble(Assembler::DoubleEqualOrUnordered, opd, ScratchDoubleReg);
     masm.emitSet(Assembler::Equal, ToRegister(ins->output()), nanCond);
     return true;
 }
 
 bool
 CodeGeneratorX86Shared::visitNotF(LNotF *ins)
 {
     FloatRegister opd = ToFloatRegister(ins->input());
 
     // Not returns true if the input is a NaN. We don't have to worry about
     // it if we know the input is never NaN though.
     Assembler::NaNCond nanCond = Assembler::NaN_IsTrue;
     if (ins->mir()->operandIsNeverNaN())
         nanCond = Assembler::NaN_HandledByCond;
 
-    masm.xorps(ScratchFloatReg, ScratchFloatReg);
-    masm.compareFloat(Assembler::DoubleEqualOrUnordered, opd, ScratchFloatReg);
+    masm.xorps(ScratchFloat32Reg, ScratchFloat32Reg);
+    masm.compareFloat(Assembler::DoubleEqualOrUnordered, opd, ScratchFloat32Reg);
     masm.emitSet(Assembler::Equal, ToRegister(ins->output()), nanCond);
     return true;
 }
 
 bool
 CodeGeneratorX86Shared::visitCompareDAndBranch(LCompareDAndBranch *comp)
 {
     FloatRegister lhs = ToFloatRegister(comp->left());
@@ -518,30 +518,30 @@ CodeGeneratorX86Shared::visitMinMaxD(LMi
 
 bool
 CodeGeneratorX86Shared::visitAbsD(LAbsD *ins)
 {
     FloatRegister input = ToFloatRegister(ins->input());
     JS_ASSERT(input == ToFloatRegister(ins->output()));
     // Load a value which is all ones except for the sign bit.
     masm.loadConstantDouble(SpecificNaN<double>(0, FloatingPoint<double>::kSignificandBits),
-                            ScratchFloatReg);
-    masm.andpd(ScratchFloatReg, input);
+                            ScratchDoubleReg);
+    masm.andpd(ScratchDoubleReg, input);
     return true;
 }
 
 bool
 CodeGeneratorX86Shared::visitAbsF(LAbsF *ins)
 {
     FloatRegister input = ToFloatRegister(ins->input());
     JS_ASSERT(input == ToFloatRegister(ins->output()));
     // Same trick as visitAbsD above.
     masm.loadConstantFloat32(SpecificNaN<float>(0, FloatingPoint<float>::kSignificandBits),
-                             ScratchFloatReg);
-    masm.andps(ScratchFloatReg, input);
+                             ScratchFloat32Reg);
+    masm.andps(ScratchFloat32Reg, input);
     return true;
 }
 
 bool
 CodeGeneratorX86Shared::visitSqrtD(LSqrtD *ins)
 {
     FloatRegister input = ToFloatRegister(ins->input());
     FloatRegister output = ToFloatRegister(ins->output());
@@ -563,35 +563,35 @@ CodeGeneratorX86Shared::visitPowHalfD(LP
 {
     FloatRegister input = ToFloatRegister(ins->input());
     JS_ASSERT(input == ToFloatRegister(ins->output()));
 
     Label done, sqrt;
 
     if (!ins->mir()->operandIsNeverNegativeInfinity()) {
         // Branch if not -Infinity.
-        masm.loadConstantDouble(NegativeInfinity<double>(), ScratchFloatReg);
+        masm.loadConstantDouble(NegativeInfinity<double>(), ScratchDoubleReg);
 
         Assembler::DoubleCondition cond = Assembler::DoubleNotEqualOrUnordered;
         if (ins->mir()->operandIsNeverNaN())
             cond = Assembler::DoubleNotEqual;
-        masm.branchDouble(cond, input, ScratchFloatReg, &sqrt);
+        masm.branchDouble(cond, input, ScratchDoubleReg, &sqrt);
 
         // Math.pow(-Infinity, 0.5) == Infinity.
         masm.xorpd(input, input);
-        masm.subsd(ScratchFloatReg, input);
+        masm.subsd(ScratchDoubleReg, input);
         masm.jump(&done);
 
         masm.bind(&sqrt);
     }
 
     if (!ins->mir()->operandIsNeverNegativeZero()) {
         // Math.pow(-0, 0.5) == 0 == Math.pow(0, 0.5). Adding 0 converts any -0 to 0.
-        masm.xorpd(ScratchFloatReg, ScratchFloatReg);
-        masm.addsd(ScratchFloatReg, input);
+        masm.xorpd(ScratchDoubleReg, ScratchDoubleReg);
+        masm.addsd(ScratchDoubleReg, input);
     }
 
     masm.sqrtsd(input, input);
 
     masm.bind(&done);
     return true;
 }
 
@@ -1555,17 +1555,17 @@ CodeGeneratorX86Shared::visitMathF(LMath
     }
     return true;
 }
 
 bool
 CodeGeneratorX86Shared::visitFloor(LFloor *lir)
 {
     FloatRegister input = ToFloatRegister(lir->input());
-    FloatRegister scratch = ScratchFloatReg;
+    FloatRegister scratch = ScratchDoubleReg;
     Register output = ToRegister(lir->output());
 
     Label bailout;
 
     if (AssemblerX86Shared::HasSSE41()) {
         // Bail on negative-zero.
         masm.branchNegativeZero(input, output, &bailout);
         if (!bailoutFrom(&bailout, lir->snapshot()))
@@ -1618,17 +1618,17 @@ CodeGeneratorX86Shared::visitFloor(LFloo
     }
     return true;
 }
 
 bool
 CodeGeneratorX86Shared::visitFloorF(LFloorF *lir)
 {
     FloatRegister input = ToFloatRegister(lir->input());
-    FloatRegister scratch = ScratchFloatReg;
+    FloatRegister scratch = ScratchFloat32Reg;
     Register output = ToRegister(lir->output());
 
     Label bailout;
 
     if (AssemblerX86Shared::HasSSE41()) {
         // Bail on negative-zero.
         masm.branchNegativeZeroFloat32(input, output, &bailout);
         if (!bailoutFrom(&bailout, lir->snapshot()))
@@ -1681,17 +1681,17 @@ CodeGeneratorX86Shared::visitFloorF(LFlo
     }
     return true;
 }
 
 bool
 CodeGeneratorX86Shared::visitCeil(LCeil *lir)
 {
     FloatRegister input = ToFloatRegister(lir->input());
-    FloatRegister scratch = ScratchFloatReg;
+    FloatRegister scratch = ScratchDoubleReg;
     Register output = ToRegister(lir->output());
 
     Label bailout, lessThanMinusOne;
 
     // Bail on ]-1; -0] range
     masm.loadConstantDouble(-1, scratch);
     masm.branchDouble(Assembler::DoubleLessThanOrEqualOrUnordered, input,
                       scratch, &lessThanMinusOne);
@@ -1737,17 +1737,17 @@ CodeGeneratorX86Shared::visitCeil(LCeil 
     masm.bind(&end);
     return true;
 }
 
 bool
 CodeGeneratorX86Shared::visitCeilF(LCeilF *lir)
 {
     FloatRegister input = ToFloatRegister(lir->input());
-    FloatRegister scratch = ScratchFloatReg;
+    FloatRegister scratch = ScratchFloat32Reg;
     Register output = ToRegister(lir->output());
 
     Label bailout, lessThanMinusOne;
 
     // Bail on ]-1; -0] range
     masm.loadConstantFloat32(-1.f, scratch);
     masm.branchFloat(Assembler::DoubleLessThanOrEqualOrUnordered, input,
                      scratch, &lessThanMinusOne);
@@ -1794,17 +1794,17 @@ CodeGeneratorX86Shared::visitCeilF(LCeil
     return true;
 }
 
 bool
 CodeGeneratorX86Shared::visitRound(LRound *lir)
 {
     FloatRegister input = ToFloatRegister(lir->input());
     FloatRegister temp = ToFloatRegister(lir->temp());
-    FloatRegister scratch = ScratchFloatReg;
+    FloatRegister scratch = ScratchDoubleReg;
     Register output = ToRegister(lir->output());
 
     Label negative, end, bailout;
 
     // Load 0.5 in the temp register.
     masm.loadConstantDouble(0.5, temp);
 
     // Branch to a slow path for negative inputs. Doesn't catch NaN or -0.
@@ -1875,17 +1875,17 @@ CodeGeneratorX86Shared::visitRound(LRoun
     return true;
 }
 
 bool
 CodeGeneratorX86Shared::visitRoundF(LRoundF *lir)
 {
     FloatRegister input = ToFloatRegister(lir->input());
     FloatRegister temp = ToFloatRegister(lir->temp());
-    FloatRegister scratch = ScratchFloatReg;
+    FloatRegister scratch = ScratchFloat32Reg;
     Register output = ToRegister(lir->output());
 
     Label negative, end, bailout;
 
     // Load 0.5 in the temp register.
     masm.loadConstantFloat32(0.5f, temp);
 
     // Branch to a slow path for negative inputs. Doesn't catch NaN or -0.
--- a/js/src/jit/shared/Lowering-shared-inl.h
+++ b/js/src/jit/shared/Lowering-shared-inl.h
@@ -144,20 +144,20 @@ LIRGeneratorShared::defineReturn(LInstru
 
         if (getVirtualRegister() >= MAX_VIRTUAL_REGISTERS)
             return false;
 #elif defined(JS_PUNBOX64)
         lir->setDef(0, LDefinition(vreg, LDefinition::BOX, LGeneralReg(JSReturnReg)));
 #endif
         break;
       case MIRType_Float32:
-        lir->setDef(0, LDefinition(vreg, LDefinition::FLOAT32, LFloatReg(ReturnFloatReg)));
+        lir->setDef(0, LDefinition(vreg, LDefinition::FLOAT32, LFloatReg(ReturnFloat32Reg)));
         break;
       case MIRType_Double:
-        lir->setDef(0, LDefinition(vreg, LDefinition::DOUBLE, LFloatReg(ReturnFloatReg)));
+        lir->setDef(0, LDefinition(vreg, LDefinition::DOUBLE, LFloatReg(ReturnDoubleReg)));
         break;
       default:
         LDefinition::Type type = LDefinition::TypeFrom(mir->type());
         JS_ASSERT(type != LDefinition::DOUBLE && type != LDefinition::FLOAT32);
         lir->setDef(0, LDefinition(vreg, type, LGeneralReg(ReturnReg)));
         break;
     }
 
--- a/js/src/jit/shared/Lowering-shared.h
+++ b/js/src/jit/shared/Lowering-shared.h
@@ -210,14 +210,15 @@ class LIRGeneratorShared : public MInstr
     static bool allowFloat32Optimizations() {
        return false;
     }
 
     // Whether we can inline ForkJoinGetSlice.
     static bool allowInlineForkJoinGetSlice() {
         return false;
     }
+
 };
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_shared_Lowering_shared_h */
--- a/js/src/jit/shared/MacroAssembler-x86-shared.cpp
+++ b/js/src/jit/shared/MacroAssembler-x86-shared.cpp
@@ -68,44 +68,44 @@ MacroAssembler::PopRegsInMaskIgnore(Regi
     }
     JS_ASSERT(diffG == 0);
 }
 
 // Note: this function clobbers the input register.
 void
 MacroAssembler::clampDoubleToUint8(FloatRegister input, Register output)
 {
-    JS_ASSERT(input != ScratchFloatReg);
+    JS_ASSERT(input != ScratchDoubleReg);
     Label positive, done;
 
     // <= 0 or NaN --> 0
-    zeroDouble(ScratchFloatReg);
-    branchDouble(DoubleGreaterThan, input, ScratchFloatReg, &positive);
+    zeroDouble(ScratchDoubleReg);
+    branchDouble(DoubleGreaterThan, input, ScratchDoubleReg, &positive);
     {
         move32(Imm32(0), output);
         jump(&done);
     }
 
     bind(&positive);
 
     // Add 0.5 and truncate.
-    loadConstantDouble(0.5, ScratchFloatReg);
-    addDouble(ScratchFloatReg, input);
+    loadConstantDouble(0.5, ScratchDoubleReg);
+    addDouble(ScratchDoubleReg, input);
 
     Label outOfRange;
 
     // Truncate to int32 and ensure the result <= 255. This relies on the
     // processor setting output to a value > 255 for doubles outside the int32
     // range (for instance 0x80000000).
     cvttsd2si(input, output);
     branch32(Assembler::Above, output, Imm32(255), &outOfRange);
     {
         // Check if we had a tie.
-        convertInt32ToDouble(output, ScratchFloatReg);
-        branchDouble(DoubleNotEqual, input, ScratchFloatReg, &done);
+        convertInt32ToDouble(output, ScratchDoubleReg);
+        branchDouble(DoubleNotEqual, input, ScratchDoubleReg, &done);
 
         // It was a tie. Mask out the ones bit to get an even value.
         // See also js_TypedArray_uint8_clamp_double.
         and32(Imm32(~1), output);
         jump(&done);
     }
 
     // > 255 --> 255
@@ -162,20 +162,20 @@ MacroAssemblerX86Shared::branchNegativeZ
 {
     // Determines whether the low double contained in the XMM register reg
     // is equal to -0.0.
 
 #if defined(JS_CODEGEN_X86)
     Label nonZero;
 
     // Compare to zero. Lets through {0, -0}.
-    xorpd(ScratchFloatReg, ScratchFloatReg);
+    xorpd(ScratchDoubleReg, ScratchDoubleReg);
 
     // If reg is non-zero, jump to nonZero.
-    branchDouble(DoubleNotEqual, reg, ScratchFloatReg, &nonZero);
+    branchDouble(DoubleNotEqual, reg, ScratchDoubleReg, &nonZero);
 
     // Input register is either zero or negative zero. Retrieve sign of input.
     movmskpd(reg, scratch);
 
     // If reg is 1 or 3, input is negative zero.
     // If reg is 0 or 2, input is a normal zero.
     branchTest32(NonZero, scratch, Imm32(1), label);
 
--- a/js/src/jit/shared/MacroAssembler-x86-shared.h
+++ b/js/src/jit/shared/MacroAssembler-x86-shared.h
@@ -331,18 +331,18 @@ class MacroAssemblerX86Shared : public A
         convertInt32ToFloat32(Operand(src), dest);
     }
     void convertInt32ToFloat32(const Operand &src, FloatRegister dest) {
         // Clear the output register first to break dependencies; see above;
         zeroFloat32(dest);
         cvtsi2ss(src, dest);
     }
     Condition testDoubleTruthy(bool truthy, FloatRegister reg) {
-        zeroDouble(ScratchFloatReg);
-        ucomisd(ScratchFloatReg, reg);
+        zeroDouble(ScratchDoubleReg);
+        ucomisd(ScratchDoubleReg, reg);
         return truthy ? NonZero : Zero;
     }
     void branchTestDoubleTruthy(bool truthy, FloatRegister reg, Label *label) {
         Condition cond = testDoubleTruthy(truthy, reg);
         j(cond, label);
     }
     void load8ZeroExtend(const Address &src, Register dest) {
         movzbl(Operand(src), dest);
@@ -432,28 +432,28 @@ class MacroAssemblerX86Shared : public A
     void zeroDouble(FloatRegister reg) {
         xorpd(reg, reg);
     }
     void zeroFloat32(FloatRegister reg) {
         xorps(reg, reg);
     }
     void negateDouble(FloatRegister reg) {
         // From MacroAssemblerX86Shared::maybeInlineDouble
-        pcmpeqw(ScratchFloatReg, ScratchFloatReg);
-        psllq(Imm32(63), ScratchFloatReg);
+        pcmpeqw(ScratchDoubleReg, ScratchDoubleReg);
+        psllq(Imm32(63), ScratchDoubleReg);
 
         // XOR the float in a float register with -0.0.
-        xorpd(ScratchFloatReg, reg); // s ^ 0x80000000000000
+        xorpd(ScratchDoubleReg, reg); // s ^ 0x80000000000000
     }
     void negateFloat(FloatRegister reg) {
-        pcmpeqw(ScratchFloatReg, ScratchFloatReg);
-        psllq(Imm32(31), ScratchFloatReg);
+        pcmpeqw(ScratchFloat32Reg, ScratchFloat32Reg);
+        psllq(Imm32(31), ScratchFloat32Reg);
 
         // XOR the float in a float register with -0.0.
-        xorps(ScratchFloatReg, reg); // s ^ 0x80000000
+        xorps(ScratchFloat32Reg, reg); // s ^ 0x80000000
     }
     void addDouble(FloatRegister src, FloatRegister dest) {
         addsd(src, dest);
     }
     void subDouble(FloatRegister src, FloatRegister dest) {
         subsd(src, dest);
     }
     void mulDouble(FloatRegister src, FloatRegister dest) {
@@ -531,36 +531,36 @@ class MacroAssemblerX86Shared : public A
     void convertDoubleToInt32(FloatRegister src, Register dest, Label *fail,
                               bool negativeZeroCheck = true)
     {
         // Check for -0.0
         if (negativeZeroCheck)
             branchNegativeZero(src, dest, fail);
 
         cvttsd2si(src, dest);
-        cvtsi2sd(dest, ScratchFloatReg);
-        ucomisd(src, ScratchFloatReg);
+        cvtsi2sd(dest, ScratchDoubleReg);
+        ucomisd(src, ScratchDoubleReg);
         j(Assembler::Parity, fail);
         j(Assembler::NotEqual, fail);
 
     }
 
     // Checks whether a float32 is representable as a 32-bit integer. If so, the
     // integer is written to the output register. Otherwise, a bailout is taken to
     // the given snapshot. This function overwrites the scratch float register.
     void convertFloat32ToInt32(FloatRegister src, Register dest, Label *fail,
                                bool negativeZeroCheck = true)
     {
         // Check for -0.0
         if (negativeZeroCheck)
             branchNegativeZeroFloat32(src, dest, fail);
 
         cvttss2si(src, dest);
-        convertInt32ToFloat32(dest, ScratchFloatReg);
-        ucomiss(src, ScratchFloatReg);
+        convertInt32ToFloat32(dest, ScratchFloat32Reg);
+        ucomiss(src, ScratchFloat32Reg);
         j(Assembler::Parity, fail);
         j(Assembler::NotEqual, fail);
     }
 
     void clampIntToUint8(Register reg) {
         Label inRange;
         branchTest32(Assembler::Zero, reg, Imm32(0xffffff00), &inRange);
         {
--- a/js/src/jit/shared/MoveEmitter-x86-shared.cpp
+++ b/js/src/jit/shared/MoveEmitter-x86-shared.cpp
@@ -222,26 +222,26 @@ MoveEmitterX86::breakCycle(const MoveOpe
     //   (A -> B)
     //   (B -> A)
     //
     // This case handles (A -> B), which we reach first. We save B, then allow
     // the original move to continue.
     switch (type) {
       case MoveOp::FLOAT32:
         if (to.isMemory()) {
-            masm.loadFloat32(toAddress(to), ScratchFloatReg);
-            masm.storeFloat32(ScratchFloatReg, cycleSlot());
+            masm.loadFloat32(toAddress(to), ScratchFloat32Reg);
+            masm.storeFloat32(ScratchFloat32Reg, cycleSlot());
         } else {
             masm.storeFloat32(to.floatReg(), cycleSlot());
         }
         break;
       case MoveOp::DOUBLE:
         if (to.isMemory()) {
-            masm.loadDouble(toAddress(to), ScratchFloatReg);
-            masm.storeDouble(ScratchFloatReg, cycleSlot());
+            masm.loadDouble(toAddress(to), ScratchDoubleReg);
+            masm.storeDouble(ScratchDoubleReg, cycleSlot());
         } else {
             masm.storeDouble(to.floatReg(), cycleSlot());
         }
         break;
       case MoveOp::INT32:
 #ifdef JS_CODEGEN_X64
         // x64 can't pop to a 32-bit destination, so don't push.
         if (to.isMemory()) {
@@ -269,28 +269,28 @@ MoveEmitterX86::completeCycle(const Move
     //
     // This case handles (B -> A), which we reach last. We emit a move from the
     // saved value of B, to A.
     switch (type) {
       case MoveOp::FLOAT32:
         JS_ASSERT(pushedAtCycle_ != -1);
         JS_ASSERT(pushedAtCycle_ - pushedAtStart_ >= sizeof(float));
         if (to.isMemory()) {
-            masm.loadFloat32(cycleSlot(), ScratchFloatReg);
-            masm.storeFloat32(ScratchFloatReg, toAddress(to));
+            masm.loadFloat32(cycleSlot(), ScratchFloat32Reg);
+            masm.storeFloat32(ScratchFloat32Reg, toAddress(to));
         } else {
             masm.loadFloat32(cycleSlot(), to.floatReg());
         }
         break;
       case MoveOp::DOUBLE:
         JS_ASSERT(pushedAtCycle_ != -1);
         JS_ASSERT(pushedAtCycle_ - pushedAtStart_ >= sizeof(double));
         if (to.isMemory()) {
-            masm.loadDouble(cycleSlot(), ScratchFloatReg);
-            masm.storeDouble(ScratchFloatReg, toAddress(to));
+            masm.loadDouble(cycleSlot(), ScratchDoubleReg);
+            masm.storeDouble(ScratchDoubleReg, toAddress(to));
         } else {
             masm.loadDouble(cycleSlot(), to.floatReg());
         }
         break;
       case MoveOp::INT32:
 #ifdef JS_CODEGEN_X64
         JS_ASSERT(pushedAtCycle_ != -1);
         JS_ASSERT(pushedAtCycle_ - pushedAtStart_ >= sizeof(int32_t));
@@ -383,36 +383,36 @@ MoveEmitterX86::emitFloat32Move(const Mo
             masm.moveFloat32(from.floatReg(), to.floatReg());
         else
             masm.storeFloat32(from.floatReg(), toAddress(to));
     } else if (to.isFloatReg()) {
         masm.loadFloat32(toAddress(from), to.floatReg());
     } else {
         // Memory to memory move.
         JS_ASSERT(from.isMemory());
-        masm.loadFloat32(toAddress(from), ScratchFloatReg);
-        masm.storeFloat32(ScratchFloatReg, toAddress(to));
+        masm.loadFloat32(toAddress(from), ScratchFloat32Reg);
+        masm.storeFloat32(ScratchFloat32Reg, toAddress(to));
     }
 }
 
 void
 MoveEmitterX86::emitDoubleMove(const MoveOperand &from, const MoveOperand &to)
 {
     if (from.isFloatReg()) {
         if (to.isFloatReg())
             masm.moveDouble(from.floatReg(), to.floatReg());
         else
             masm.storeDouble(from.floatReg(), toAddress(to));
     } else if (to.isFloatReg()) {
         masm.loadDouble(toAddress(from), to.floatReg());
     } else {
         // Memory to memory move.
         JS_ASSERT(from.isMemory());
-        masm.loadDouble(toAddress(from), ScratchFloatReg);
-        masm.storeDouble(ScratchFloatReg, toAddress(to));
+        masm.loadDouble(toAddress(from), ScratchDoubleReg);
+        masm.storeDouble(ScratchDoubleReg, toAddress(to));
     }
 }
 
 void
 MoveEmitterX86::assertDone()
 {
     JS_ASSERT(!inCycle_);
 }
--- a/js/src/jit/x64/Architecture-x64.h
+++ b/js/src/jit/x64/Architecture-x64.h
@@ -22,17 +22,21 @@ static const uint32_t ION_FRAME_SLACK_SI
 static const uint32_t ShadowStackSpace = 32;
 #else
 static const uint32_t ShadowStackSpace = 0;
 #endif
 
 class Registers {
   public:
     typedef JSC::X86Registers::RegisterID Code;
-
+    typedef uint32_t SetType;
+    static uint32_t SetSize(SetType x) {
+        static_assert(sizeof(SetType) == 4, "SetType must be 32 bits");
+        return mozilla::CountPopulation32(x);
+    }
     static const char *GetName(Code code) {
         static const char * const Names[] = { "rax", "rcx", "rdx", "rbx",
                                               "rsp", "rbp", "rsi", "rdi",
                                               "r8",  "r9",  "r10", "r11",
                                               "r12", "r13", "r14", "r15" };
         return Names[code];
     }
 
@@ -43,16 +47,17 @@ class Registers {
         }
         return Invalid;
     }
 
     static const Code StackPointer = JSC::X86Registers::esp;
     static const Code Invalid = JSC::X86Registers::invalid_reg;
 
     static const uint32_t Total = 16;
+    static const uint32_t TotalPhys = 16;
     static const uint32_t Allocatable = 14;
 
     static const uint32_t AllMask = (1 << Total) - 1;
 
     static const uint32_t ArgRegMask =
 # if !defined(_WIN64)
         (1 << JSC::X86Registers::edi) |
         (1 << JSC::X86Registers::esi) |
@@ -110,17 +115,17 @@ class Registers {
 };
 
 // Smallest integer type that can hold a register bitmask.
 typedef uint16_t PackedRegisterMask;
 
 class FloatRegisters {
   public:
     typedef JSC::X86Registers::XMMRegisterID Code;
-
+    typedef uint32_t SetType;
     static const char *GetName(Code code) {
         static const char * const Names[] = { "xmm0",  "xmm1",  "xmm2",  "xmm3",
                                               "xmm4",  "xmm5",  "xmm6",  "xmm7",
                                               "xmm8",  "xmm9",  "xmm10", "xmm11",
                                               "xmm12", "xmm13", "xmm14", "xmm15" };
         return Names[code];
     }
 
@@ -130,20 +135,22 @@ class FloatRegisters {
                 return Code(i);
         }
         return Invalid;
     }
 
     static const Code Invalid = JSC::X86Registers::invalid_xmm;
 
     static const uint32_t Total = 16;
+    static const uint32_t TotalPhys = 16;
+
     static const uint32_t Allocatable = 15;
 
     static const uint32_t AllMask = (1 << Total) - 1;
-
+    static const uint32_t AllDoubleMask = AllMask;
     static const uint32_t VolatileMask =
 #if defined(_WIN64)
         (1 << JSC::X86Registers::xmm0) |
         (1 << JSC::X86Registers::xmm1) |
         (1 << JSC::X86Registers::xmm2) |
         (1 << JSC::X86Registers::xmm3) |
         (1 << JSC::X86Registers::xmm4) |
         (1 << JSC::X86Registers::xmm5);
@@ -154,14 +161,96 @@ class FloatRegisters {
     static const uint32_t NonVolatileMask = AllMask & ~VolatileMask;
 
     static const uint32_t WrapperMask = VolatileMask;
 
     static const uint32_t NonAllocatableMask =
         (1 << JSC::X86Registers::xmm15);    // This is ScratchFloatReg.
 
     static const uint32_t AllocatableMask = AllMask & ~NonAllocatableMask;
+
 };
 
+template <typename T>
+class TypedRegisterSet;
+
+struct FloatRegister {
+    typedef FloatRegisters Codes;
+    typedef Codes::Code Code;
+    typedef Codes::SetType SetType;
+    static uint32_t SetSize(SetType x) {
+        static_assert(sizeof(SetType) == 4, "SetType must be 32 bits");
+        return mozilla::CountPopulation32(x);
+    }
+
+    Code code_;
+
+    static FloatRegister FromCode(uint32_t i) {
+        JS_ASSERT(i < FloatRegisters::Total);
+        FloatRegister r = { (FloatRegisters::Code)i };
+        return r;
+    }
+    Code code() const {
+        JS_ASSERT((uint32_t)code_ < FloatRegisters::Total);
+        return code_;
+    }
+    const char *name() const {
+        return FloatRegisters::GetName(code());
+    }
+    bool volatile_() const {
+        return !!((1 << code()) & FloatRegisters::VolatileMask);
+    }
+    bool operator !=(FloatRegister other) const {
+        return other.code_ != code_;
+    }
+    bool operator ==(FloatRegister other) const {
+        return other.code_ == code_;
+    }
+    bool aliases(FloatRegister other) const {
+        return other.code_ == code_;
+    }
+    uint32_t numAliased() const {
+        return 1;
+    }
+    void aliased(uint32_t aliasIdx, FloatRegister *ret) {
+        JS_ASSERT(aliasIdx == 0);
+        *ret = *this;
+    }
+    // This function mostly exists for the ARM backend.  It is to ensure that two
+    // floating point registers' types are equivalent.  e.g. S0 is not equivalent
+    // to D16, since S0 holds a float32, and D16 holds a Double.
+    // Since all floating point registers on x86 and x64 are equivalent, it is
+    // reasonable for this function to do the same.
+    bool equiv(FloatRegister other) const {
+        return true;
+    }
+    uint32_t size() const {
+        return sizeof(double);
+    }
+    uint32_t numAlignedAliased() {
+        return 1;
+    }
+    void alignedAliased(uint32_t aliasIdx, FloatRegister *ret) {
+        JS_ASSERT(aliasIdx == 0);
+        *ret = *this;
+    }
+    static TypedRegisterSet<FloatRegister> ReduceSetForPush(const TypedRegisterSet<FloatRegister> &s);
+    static uint32_t GetSizeInBytes(const TypedRegisterSet<FloatRegister> &s);
+    static uint32_t GetPushSizeInBytes(const TypedRegisterSet<FloatRegister> &s);
+    uint32_t getRegisterDumpOffsetInBytes();
+
+};
+
+// Arm/D32 has double registers that can NOT be treated as float32
+// and this requires some dances in lowering.
+static bool hasUnaliasedDouble() {
+    return false;
+}
+// On ARM, Dn aliases both S2n and S2n+1, so if you need to convert a float32
+// to a double as a temporary, you need a temporary double register.
+static bool hasMultiAlias() {
+    return false;
+}
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_x64_Architecture_x64_h */
--- a/js/src/jit/x64/Assembler-x64.cpp
+++ b/js/src/jit/x64/Assembler-x64.cpp
@@ -258,8 +258,29 @@ Assembler::TraceJumpRelocations(JSTracer
     RelocationIterator iter(reader);
     while (iter.read()) {
         JitCode *child = CodeFromJump(code, code->raw() + iter.offset());
         MarkJitCodeUnbarriered(trc, &child, "rel32");
         JS_ASSERT(child == CodeFromJump(code, code->raw() + iter.offset()));
     }
 }
 
+FloatRegisterSet
+FloatRegister::ReduceSetForPush(const FloatRegisterSet &s)
+{
+    return s;
+}
+uint32_t
+FloatRegister::GetSizeInBytes(const FloatRegisterSet &s)
+{
+    uint32_t ret = s.size() * sizeof(double);
+    return ret;
+}
+uint32_t
+FloatRegister::GetPushSizeInBytes(const FloatRegisterSet &s)
+{
+    return s.size() * sizeof(double);
+}
+uint32_t
+FloatRegister::getRegisterDumpOffsetInBytes()
+{
+    return code() * sizeof(double);
+}
--- a/js/src/jit/x64/Assembler-x64.h
+++ b/js/src/jit/x64/Assembler-x64.h
@@ -67,18 +67,20 @@ static MOZ_CONSTEXPR_VAR Register FrameP
 static MOZ_CONSTEXPR_VAR Register JSReturnReg = rcx;
 // Avoid, except for assertions.
 static MOZ_CONSTEXPR_VAR Register JSReturnReg_Type = JSReturnReg;
 static MOZ_CONSTEXPR_VAR Register JSReturnReg_Data = JSReturnReg;
 
 static MOZ_CONSTEXPR_VAR Register ReturnReg = rax;
 static MOZ_CONSTEXPR_VAR Register ScratchReg = r11;
 static MOZ_CONSTEXPR_VAR Register HeapReg = r15;
-static MOZ_CONSTEXPR_VAR FloatRegister ReturnFloatReg = xmm0;
-static MOZ_CONSTEXPR_VAR FloatRegister ScratchFloatReg = xmm15;
+static MOZ_CONSTEXPR_VAR FloatRegister ReturnFloat32Reg = xmm0;
+static MOZ_CONSTEXPR_VAR FloatRegister ScratchFloat32Reg = xmm15;
+static MOZ_CONSTEXPR_VAR FloatRegister ReturnDoubleReg = xmm0;
+static MOZ_CONSTEXPR_VAR FloatRegister ScratchDoubleReg = xmm15;
 
 // Avoid rbp, which is the FramePointer, which is unavailable in some modes.
 static MOZ_CONSTEXPR_VAR Register ArgumentsRectifierReg = r8;
 static MOZ_CONSTEXPR_VAR Register CallTempReg0 = rax;
 static MOZ_CONSTEXPR_VAR Register CallTempReg1 = rdi;
 static MOZ_CONSTEXPR_VAR Register CallTempReg2 = rbx;
 static MOZ_CONSTEXPR_VAR Register CallTempReg3 = rcx;
 static MOZ_CONSTEXPR_VAR Register CallTempReg4 = rsi;
--- a/js/src/jit/x64/BaselineIC-x64.cpp
+++ b/js/src/jit/x64/BaselineIC-x64.cpp
@@ -171,18 +171,18 @@ ICBinaryArith_Int32::Compiler::generateS
             Label toUint;
             masm.j(Assembler::Signed, &toUint);
 
             // Box and return.
             masm.boxValue(JSVAL_TYPE_INT32, ExtractTemp0, R0.valueReg());
             EmitReturnFromIC(masm);
 
             masm.bind(&toUint);
-            masm.convertUInt32ToDouble(ExtractTemp0, ScratchFloatReg);
-            masm.boxDouble(ScratchFloatReg, R0);
+            masm.convertUInt32ToDouble(ExtractTemp0, ScratchDoubleReg);
+            masm.boxDouble(ScratchDoubleReg, R0);
         } else {
             masm.j(Assembler::Signed, &revertRegister);
             masm.boxValue(JSVAL_TYPE_INT32, ExtractTemp0, R0.valueReg());
         }
         break;
       default:
         MOZ_ASSUME_UNREACHABLE("Unhandled op in BinaryArith_Int32");
     }
--- a/js/src/jit/x64/CodeGenerator-x64.cpp
+++ b/js/src/jit/x64/CodeGenerator-x64.cpp
@@ -69,18 +69,18 @@ bool
 CodeGeneratorX64::visitBox(LBox *box)
 {
     const LAllocation *in = box->getOperand(0);
     const LDefinition *result = box->getDef(0);
 
     if (IsFloatingPointType(box->type())) {
         FloatRegister reg = ToFloatRegister(in);
         if (box->type() == MIRType_Float32) {
-            masm.convertFloat32ToDouble(reg, ScratchFloatReg);
-            reg = ScratchFloatReg;
+            masm.convertFloat32ToDouble(reg, ScratchDoubleReg);
+            reg = ScratchDoubleReg;
         }
         masm.movq(reg, ToRegister(result));
     } else {
         masm.boxValue(ValueTypeFromMIRType(box->type()), ToRegister(in), ToRegister(result));
     }
     return true;
 }
 
--- a/js/src/jit/x64/Trampoline-x64.cpp
+++ b/js/src/jit/x64/Trampoline-x64.cpp
@@ -692,17 +692,17 @@ JitRuntime::generateVMWrapper(JSContext 
 
       case Type_Bool:
         masm.load8ZeroExtend(Address(esp, 0), ReturnReg);
         masm.freeStack(sizeof(int32_t));
         break;
 
       case Type_Double:
         JS_ASSERT(cx->runtime()->jitSupportsFloatingPoint);
-        masm.loadDouble(Address(esp, 0), ReturnFloatReg);
+        masm.loadDouble(Address(esp, 0), ReturnDoubleReg);
         masm.freeStack(sizeof(double));
         break;
 
       case Type_Pointer:
         masm.loadPtr(Address(esp, 0), ReturnReg);
         masm.freeStack(sizeof(uintptr_t));
         break;
 
--- a/js/src/jit/x86/Architecture-x86.h
+++ b/js/src/jit/x86/Architecture-x86.h
@@ -31,17 +31,21 @@ static const int32_t NUNBOX32_PAYLOAD_OF
 ////
 
 // Size of each bailout table entry. On x86 this is a 5-byte relative call.
 static const uint32_t BAILOUT_TABLE_ENTRY_SIZE    = 5;
 
 class Registers {
   public:
     typedef JSC::X86Registers::RegisterID Code;
-
+    typedef uint8_t SetType;
+    static uint32_t SetSize(SetType x) {
+        static_assert(sizeof(SetType) == 1, "SetType must be 8 bits");
+        return mozilla::CountPopulation32(x);
+    }
     static const char *GetName(Code code) {
         static const char * const Names[] = { "eax", "ecx", "edx", "ebx",
                                               "esp", "ebp", "esi", "edi" };
         return Names[code];
     }
 
     static Code FromName(const char *name) {
         for (size_t i = 0; i < Total; i++) {
@@ -50,16 +54,17 @@ class Registers {
         }
         return Invalid;
     }
 
     static const Code StackPointer = JSC::X86Registers::esp;
     static const Code Invalid = JSC::X86Registers::invalid_reg;
 
     static const uint32_t Total = 8;
+    static const uint32_t TotalPhys = 8;
     static const uint32_t Allocatable = 7;
 
     static const uint32_t AllMask = (1 << Total) - 1;
 
     static const uint32_t ArgRegMask = 0;
 
     static const uint32_t VolatileMask =
         (1 << JSC::X86Registers::eax) |
@@ -101,17 +106,17 @@ class Registers {
 };
 
 // Smallest integer type that can hold a register bitmask.
 typedef uint8_t PackedRegisterMask;
 
 class FloatRegisters {
   public:
     typedef JSC::X86Registers::XMMRegisterID Code;
-
+    typedef uint32_t SetType;
     static const char *GetName(Code code) {
         static const char * const Names[] = { "xmm0", "xmm1", "xmm2", "xmm3",
                                               "xmm4", "xmm5", "xmm6", "xmm7" };
         return Names[code];
     }
 
     static Code FromName(const char *name) {
         for (size_t i = 0; i < Total; i++) {
@@ -119,27 +124,111 @@ class FloatRegisters {
                 return Code(i);
         }
         return Invalid;
     }
 
     static const Code Invalid = JSC::X86Registers::invalid_xmm;
 
     static const uint32_t Total = 8;
+    static const uint32_t TotalPhys = 8;
     static const uint32_t Allocatable = 7;
 
     static const uint32_t AllMask = (1 << Total) - 1;
-
+    static const uint32_t AllDoubleMask = AllMask;
     static const uint32_t VolatileMask = AllMask;
     static const uint32_t NonVolatileMask = 0;
 
     static const uint32_t WrapperMask = VolatileMask;
 
     static const uint32_t NonAllocatableMask =
         (1 << JSC::X86Registers::xmm7);
 
     static const uint32_t AllocatableMask = AllMask & ~NonAllocatableMask;
 };
 
+template <typename T>
+class TypedRegisterSet;
+
+struct FloatRegister {
+    typedef FloatRegisters Codes;
+    typedef Codes::Code Code;
+    typedef Codes::SetType SetType;
+    static uint32_t SetSize(SetType x) {
+        static_assert(sizeof(SetType) == 4, "SetType must be 32 bits");
+        return mozilla::CountPopulation32(x);
+    }
+
+    Code code_;
+
+    static FloatRegister FromCode(uint32_t i) {
+        JS_ASSERT(i < FloatRegisters::Total);
+        FloatRegister r = { (FloatRegisters::Code)i };
+        return r;
+    }
+    Code code() const {
+        JS_ASSERT((uint32_t)code_ < FloatRegisters::Total);
+        return code_;
+    }
+    const char *name() const {
+        return FloatRegisters::GetName(code());
+    }
+    bool volatile_() const {
+        return !!((1 << code()) & FloatRegisters::VolatileMask);
+    }
+    bool operator != (FloatRegister other) const {
+        return other.code_ != code_;
+    }
+    bool operator == (FloatRegister other) const {
+        return other.code_ == code_;
+    }
+    bool aliases(FloatRegister other) const {
+        return other.code_ == code_;
+    }
+    uint32_t numAliased() const {
+        return 1;
+    }
+    void aliased(uint32_t aliasIdx, FloatRegister *ret) {
+        JS_ASSERT(aliasIdx == 0);
+        *ret = *this;
+    }
+    // This function mostly exists for the ARM backend.  It is to ensure that two
+    // floating point registers' types are equivalent.  e.g. S0 is not equivalent
+    // to D16, since S0 holds a float32, and D16 holds a Double.
+    // Since all floating point registers on x86 and x64 are equivalent, it is
+    // reasonable for this function to do the same.
+    bool equiv(FloatRegister other) const {
+        return true;
+    }
+    uint32_t size() const {
+        return sizeof(double);
+    }
+    uint32_t numAlignedAliased() const {
+        return 1;
+    }
+    void alignedAliased(uint32_t aliasIdx, FloatRegister *ret) {
+        JS_ASSERT(aliasIdx == 0);
+        *ret = *this;
+    }
+    static TypedRegisterSet<FloatRegister> ReduceSetForPush(const TypedRegisterSet<FloatRegister> &s);
+    static uint32_t GetSizeInBytes(const TypedRegisterSet<FloatRegister> &s);
+    static uint32_t GetPushSizeInBytes(const TypedRegisterSet<FloatRegister> &s);
+    uint32_t getRegisterDumpOffsetInBytes();
+
+
+};
+
+// Arm/D32 has double registers that can NOT be treated as float32
+// and this requires some dances in lowering.
+static bool hasUnaliasedDouble() {
+    return false;
+}
+
+// On ARM, Dn aliases both S2n and S2n+1, so if you need to convert a float32
+// to a double as a temporary, you need a temporary double register.
+static bool hasMultiAlias() {
+    return false;
+}
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_x86_Architecture_x86_h */
--- a/js/src/jit/x86/Assembler-x86.cpp
+++ b/js/src/jit/x86/Assembler-x86.cpp
@@ -85,8 +85,30 @@ Assembler::TraceJumpRelocations(JSTracer
     RelocationIterator iter(reader);
     while (iter.read()) {
         JitCode *child = CodeFromJump(code->raw() + iter.offset());
         MarkJitCodeUnbarriered(trc, &child, "rel32");
         JS_ASSERT(child == CodeFromJump(code->raw() + iter.offset()));
     }
 }
 
+uint32_t
+FloatRegister::GetSizeInBytes(const FloatRegisterSet &s)
+{
+    uint32_t ret = s.size() * sizeof(double);
+    return ret;
+}
+
+FloatRegisterSet
+FloatRegister::ReduceSetForPush(const FloatRegisterSet &s)
+{
+    return s;
+}
+uint32_t
+FloatRegister::GetPushSizeInBytes(const FloatRegisterSet &s)
+{
+    return s.size() * sizeof(double);
+}
+uint32_t
+FloatRegister::getRegisterDumpOffsetInBytes()
+{
+    return code() * sizeof(double);
+}
--- a/js/src/jit/x86/Assembler-x86.h
+++ b/js/src/jit/x86/Assembler-x86.h
@@ -38,18 +38,20 @@ static MOZ_CONSTEXPR_VAR FloatRegister x
 static MOZ_CONSTEXPR_VAR Register InvalidReg = { JSC::X86Registers::invalid_reg };
 static MOZ_CONSTEXPR_VAR FloatRegister InvalidFloatReg = { JSC::X86Registers::invalid_xmm };
 
 static MOZ_CONSTEXPR_VAR Register JSReturnReg_Type = ecx;
 static MOZ_CONSTEXPR_VAR Register JSReturnReg_Data = edx;
 static MOZ_CONSTEXPR_VAR Register StackPointer = esp;
 static MOZ_CONSTEXPR_VAR Register FramePointer = ebp;
 static MOZ_CONSTEXPR_VAR Register ReturnReg = eax;
-static MOZ_CONSTEXPR_VAR FloatRegister ReturnFloatReg = xmm0;
-static MOZ_CONSTEXPR_VAR FloatRegister ScratchFloatReg = xmm7;
+static MOZ_CONSTEXPR_VAR FloatRegister ReturnFloat32Reg = xmm0;
+static MOZ_CONSTEXPR_VAR FloatRegister ScratchFloat32Reg = xmm7;
+static MOZ_CONSTEXPR_VAR FloatRegister ReturnDoubleReg = xmm0;
+static MOZ_CONSTEXPR_VAR FloatRegister ScratchDoubleReg = xmm7;
 
 // Avoid ebp, which is the FramePointer, which is unavailable in some modes.
 static MOZ_CONSTEXPR_VAR Register ArgumentsRectifierReg = esi;
 static MOZ_CONSTEXPR_VAR Register CallTempReg0 = edi;
 static MOZ_CONSTEXPR_VAR Register CallTempReg1 = eax;
 static MOZ_CONSTEXPR_VAR Register CallTempReg2 = ebx;
 static MOZ_CONSTEXPR_VAR Register CallTempReg3 = ecx;
 static MOZ_CONSTEXPR_VAR Register CallTempReg4 = esi;
--- a/js/src/jit/x86/BaselineIC-x86.cpp
+++ b/js/src/jit/x86/BaselineIC-x86.cpp
@@ -178,18 +178,18 @@ ICBinaryArith_Int32::Compiler::generateS
             Label toUint;
             masm.j(Assembler::Signed, &toUint);
 
             // Box and return.
             masm.tagValue(JSVAL_TYPE_INT32, R0.payloadReg(), R0);
             EmitReturnFromIC(masm);
 
             masm.bind(&toUint);
-            masm.convertUInt32ToDouble(R0.payloadReg(), ScratchFloatReg);
-            masm.boxDouble(ScratchFloatReg, R0);
+            masm.convertUInt32ToDouble(R0.payloadReg(), ScratchDoubleReg);
+            masm.boxDouble(ScratchDoubleReg, R0);
         } else {
             masm.j(Assembler::Signed, &revertRegister);
             masm.tagValue(JSVAL_TYPE_INT32, R0.payloadReg(), R0);
         }
         break;
       default:
        MOZ_ASSUME_UNREACHABLE("Unhandled op for BinaryArith_Int32.  ");
     }
--- a/js/src/jit/x86/CodeGenerator-x86.cpp
+++ b/js/src/jit/x86/CodeGenerator-x86.cpp
@@ -110,18 +110,18 @@ CodeGeneratorX86::visitBox(LBox *box)
 bool
 CodeGeneratorX86::visitBoxFloatingPoint(LBoxFloatingPoint *box)
 {
     const LAllocation *in = box->getOperand(0);
     const ValueOperand out = ToOutValue(box);
 
     FloatRegister reg = ToFloatRegister(in);
     if (box->type() == MIRType_Float32) {
-        masm.convertFloat32ToDouble(reg, ScratchFloatReg);
-        reg = ScratchFloatReg;
+        masm.convertFloat32ToDouble(reg, ScratchFloat32Reg);
+        reg = ScratchFloat32Reg;
     }
     masm.boxDouble(reg, out);
     return true;
 }
 
 bool
 CodeGeneratorX86::visitUnbox(LUnbox *unbox)
 {
@@ -549,23 +549,23 @@ CodeGeneratorX86::postAsmJSCall(LAsmJSCa
     MAsmJSCall *mir = lir->mir();
     if (!IsFloatingPointType(mir->type()) || mir->callee().which() != MAsmJSCall::Callee::Builtin)
         return;
 
     if (mir->type() == MIRType_Float32) {
         masm.reserveStack(sizeof(float));
         Operand op(esp, 0);
         masm.fstp32(op);
-        masm.loadFloat32(op, ReturnFloatReg);
+        masm.loadFloat32(op, ReturnFloat32Reg);
         masm.freeStack(sizeof(float));
     } else {
         masm.reserveStack(sizeof(double));
         Operand op(esp, 0);
         masm.fstp(op);
-        masm.loadDouble(op, ReturnFloatReg);
+        masm.loadDouble(op, ReturnDoubleReg);
         masm.freeStack(sizeof(double));
     }
 }
 
 void
 DispatchIonCache::initializeAddCacheState(LInstruction *ins, AddCacheState *addState)
 {
     // On x86, where there is no general purpose scratch register available,
@@ -729,18 +729,18 @@ CodeGeneratorX86::visitOutOfLineTruncate
         masm.jump(&fail);
     } else {
         FloatRegister temp = ToFloatRegister(ins->tempFloat());
 
         // Try to convert doubles representing integers within 2^32 of a signed
         // integer, by adding/subtracting 2^32 and then trying to convert to int32.
         // This has to be an exact conversion, as otherwise the truncation works
         // incorrectly on the modified value.
-        masm.xorpd(ScratchFloatReg, ScratchFloatReg);
-        masm.ucomisd(input, ScratchFloatReg);
+        masm.xorpd(ScratchDoubleReg, ScratchDoubleReg);
+        masm.ucomisd(input, ScratchDoubleReg);
         masm.j(Assembler::Parity, &fail);
 
         {
             Label positive;
             masm.j(Assembler::Above, &positive);
 
             masm.loadConstantDouble(4294967296.0, temp);
             Label skip;
@@ -748,19 +748,19 @@ CodeGeneratorX86::visitOutOfLineTruncate
 
             masm.bind(&positive);
             masm.loadConstantDouble(-4294967296.0, temp);
             masm.bind(&skip);
         }
 
         masm.addsd(input, temp);
         masm.cvttsd2si(temp, output);
-        masm.cvtsi2sd(output, ScratchFloatReg);
+        masm.cvtsi2sd(output, ScratchDoubleReg);
 
-        masm.ucomisd(temp, ScratchFloatReg);
+        masm.ucomisd(temp, ScratchDoubleReg);
         masm.j(Assembler::Parity, &fail);
         masm.j(Assembler::Equal, ool->rejoin());
     }
 
     masm.bind(&fail);
     {
         saveVolatile(output);
 
@@ -819,18 +819,18 @@ CodeGeneratorX86::visitOutOfLineTruncate
         masm.jump(&fail);
     } else {
         FloatRegister temp = ToFloatRegister(ins->tempFloat());
 
         // Try to convert float32 representing integers within 2^32 of a signed
         // integer, by adding/subtracting 2^32 and then trying to convert to int32.
         // This has to be an exact conversion, as otherwise the truncation works
         // incorrectly on the modified value.
-        masm.xorps(ScratchFloatReg, ScratchFloatReg);
-        masm.ucomiss(input, ScratchFloatReg);
+        masm.xorps(ScratchFloat32Reg, ScratchFloat32Reg);
+        masm.ucomiss(input, ScratchFloat32Reg);
         masm.j(Assembler::Parity, &fail);
 
         {
             Label positive;
             masm.j(Assembler::Above, &positive);
 
             masm.loadConstantFloat32(4294967296.f, temp);
             Label skip;
@@ -838,19 +838,19 @@ CodeGeneratorX86::visitOutOfLineTruncate
 
             masm.bind(&positive);
             masm.loadConstantFloat32(-4294967296.f, temp);
             masm.bind(&skip);
         }
 
         masm.addss(input, temp);
         masm.cvttss2si(temp, output);
-        masm.cvtsi2ss(output, ScratchFloatReg);
+        masm.cvtsi2ss(output, ScratchFloat32Reg);
 
-        masm.ucomiss(temp, ScratchFloatReg);
+        masm.ucomiss(temp, ScratchFloat32Reg);
         masm.j(Assembler::Parity, &fail);
         masm.j(Assembler::Equal, ool->rejoin());
     }
 
     masm.bind(&fail);
     {
         saveVolatile(output);
 
--- a/js/src/jit/x86/Lowering-x86.h
+++ b/js/src/jit/x86/Lowering-x86.h
@@ -59,21 +59,19 @@ class LIRGeneratorX86 : public LIRGenera
 
     static bool allowTypedElementHoleCheck() {
         return true;
     }
 
     static bool allowStaticTypedArrayAccesses() {
         return true;
     }
-
     static bool allowFloat32Optimizations() {
         return true;
     }
-
     static bool allowInlineForkJoinGetSlice() {
         return true;
     }
 };
 
 typedef LIRGeneratorX86 LIRGeneratorSpecific;
 
 } // namespace js
--- a/js/src/jit/x86/MacroAssembler-x86.cpp
+++ b/js/src/jit/x86/MacroAssembler-x86.cpp
@@ -233,22 +233,22 @@ MacroAssemblerX86::callWithABIPre(uint32
 
 void
 MacroAssemblerX86::callWithABIPost(uint32_t stackAdjust, MoveOp::Type result)
 {
     freeStack(stackAdjust);
     if (result == MoveOp::DOUBLE) {
         reserveStack(sizeof(double));
         fstp(Operand(esp, 0));
-        loadDouble(Operand(esp, 0), ReturnFloatReg);
+        loadDouble(Operand(esp, 0), ReturnDoubleReg);
         freeStack(sizeof(double));
     } else if (result == MoveOp::FLOAT32) {
         reserveStack(sizeof(float));
         fstp32(Operand(esp, 0));
-        loadFloat32(Operand(esp, 0), ReturnFloatReg);
+        loadFloat32(Operand(esp, 0), ReturnFloat32Reg);
         freeStack(sizeof(float));
     }
     if (dynamicAlignment_)
         pop(esp);
 
     JS_ASSERT(inCall_);
     inCall_ = false;
 }
--- a/js/src/jit/x86/MacroAssembler-x86.h
+++ b/js/src/jit/x86/MacroAssembler-x86.h
@@ -828,40 +828,40 @@ class MacroAssemblerX86 : public MacroAs
     void unboxSymbol(const ValueOperand &src, Register dest) { unboxNonDouble(src, dest); }
     void unboxSymbol(const Address &src, Register dest) { unboxNonDouble(src, dest); }
     void unboxObject(const ValueOperand &src, Register dest) { unboxNonDouble(src, dest); }
     void unboxObject(const Address &src, Register dest) { unboxNonDouble(src, dest); }
     void unboxDouble(const Address &src, FloatRegister dest) {
         loadDouble(Operand(src), dest);
     }
     void unboxDouble(const ValueOperand &src, FloatRegister dest) {
-        JS_ASSERT(dest != ScratchFloatReg);
+        JS_ASSERT(dest != ScratchDoubleReg);
         if (Assembler::HasSSE41()) {
             movd(src.payloadReg(), dest);
             pinsrd(src.typeReg(), dest);
         } else {
             movd(src.payloadReg(), dest);
-            movd(src.typeReg(), ScratchFloatReg);
-            unpcklps(ScratchFloatReg, dest);
+            movd(src.typeReg(), ScratchDoubleReg);
+            unpcklps(ScratchDoubleReg, dest);
         }
     }
     void unboxDouble(const Operand &payload, const Operand &type,
                      Register scratch, FloatRegister dest) {
-        JS_ASSERT(dest != ScratchFloatReg);
+        JS_ASSERT(dest != ScratchDoubleReg);
         if (Assembler::HasSSE41()) {
             movl(payload, scratch);
             movd(scratch, dest);
             movl(type, scratch);
             pinsrd(scratch, dest);
         } else {
             movl(payload, scratch);
             movd(scratch, dest);
             movl(type, scratch);
-            movd(scratch, ScratchFloatReg);
-            unpcklps(ScratchFloatReg, dest);
+            movd(scratch, ScratchDoubleReg);
+            unpcklps(ScratchDoubleReg, dest);
         }
     }
     void unboxValue(const ValueOperand &src, AnyRegister dest) {
         if (dest.isFloat()) {
             Label notInt32, end;
             branchTestInt32(Assembler::NotEqual, src, &notInt32);
             convertInt32ToDouble(src.payloadReg(), dest.fpu());
             jump(&end);
--- a/js/src/jit/x86/Trampoline-x86.cpp
+++ b/js/src/jit/x86/Trampoline-x86.cpp
@@ -731,17 +731,17 @@ JitRuntime::generateVMWrapper(JSContext 
 
       case Type_Bool:
         masm.Pop(ReturnReg);
         masm.movzbl(ReturnReg, ReturnReg);
         break;
 
       case Type_Double:
         if (cx->runtime()->jitSupportsFloatingPoint)
-            masm.Pop(ReturnFloatReg);
+            masm.Pop(ReturnDoubleReg);
         else
             masm.assumeUnreachable("Unable to pop to float reg, with no FP support.");
         break;
 
       default:
         JS_ASSERT(f.outParam == Type_Void);
         break;
     }
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -28,16 +28,17 @@
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIWeakReferenceUtils.h"
 #include "nsAutoPtr.h"
 #include "nsThreadUtils.h"
 #include "nsFrameManager.h"
 #include "nsLayoutUtils.h"
 #include "nsViewManager.h"
 #include "RestyleManager.h"
+#include "SurfaceCache.h"
 #include "nsCSSRuleProcessor.h"
 #include "nsRuleNode.h"
 #include "gfxPlatform.h"
 #include "nsCSSRules.h"
 #include "nsFontFaceLoader.h"
 #include "mozilla/EventListenerManager.h"
 #include "prenv.h"
 #include "nsObjectFrame.h"
@@ -1705,20 +1706,25 @@ nsPresContext::ThemeChangedInternal()
 
   // Tell the theme that it changed, so it can flush any handles to stale theme
   // data.
   if (mTheme && sThemeChanged) {
     mTheme->ThemeChanged();
     sThemeChanged = false;
   }
 
-  // Clear all cached LookAndFeel colors.
   if (sLookAndFeelChanged) {
+    // Clear all cached LookAndFeel colors.
     LookAndFeel::Refresh();
     sLookAndFeelChanged = false;
+
+    // Vector images (SVG) may be using theme colors so we discard all cached
+    // surfaces. (We could add a vector image only version of DiscardAll, but
+    // in bug 940625 we decided theme changes are rare enough not to bother.)
+    mozilla::image::SurfaceCache::DiscardAll();
   }
 
   // This will force the system metrics to be generated the next time they're used
   nsCSSRuleProcessor::FreeSystemMetrics();
 
   // Changes to system metrics can change media queries on them.
   // Changes in theme can change system colors (whose changes are
   // properly reflected in computed style data), system fonts (whose
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -148,16 +148,23 @@ public:
                          bool aIsSVGMode);
 
   void ParseMediaList(const nsSubstring& aBuffer,
                       nsIURI* aURL, // for error reporting
                       uint32_t aLineNumber, // for error reporting
                       nsMediaList* aMediaList,
                       bool aHTMLMode);
 
+  bool ParseSourceSizeList(const nsAString& aBuffer,
+                           nsIURI* aURI, // for error reporting
+                           uint32_t aLineNumber, // for error reporting
+                           InfallibleTArray< nsAutoPtr<nsMediaQuery> >& aQueries,
+                           InfallibleTArray<nsCSSValue>& aValues,
+                           bool aHTMLMode);
+
   nsresult ParseVariable(const nsAString& aVariableName,
                          const nsAString& aPropValue,
                          nsIURI* aSheetURL,
                          nsIURI* aBaseURL,
                          nsIPrincipal* aSheetPrincipal,
                          css::Declaration* aDeclaration,
                          bool* aChanged,
                          bool aIsImportant);
@@ -436,19 +443,27 @@ protected:
 
   bool ParseRuleSet(RuleAppendFunc aAppendFunc, void* aProcessData,
                     bool aInsideBraces = false);
   bool ParseAtRule(RuleAppendFunc aAppendFunc, void* aProcessData,
                    bool aInAtRule);
   bool ParseCharsetRule(RuleAppendFunc aAppendFunc, void* aProcessData);
   bool ParseImportRule(RuleAppendFunc aAppendFunc, void* aProcessData);
   bool ParseURLOrString(nsString& aURL);
-  bool GatherMedia(nsMediaList* aMedia,
-                   bool aInAtRule);
-  bool ParseMediaQuery(bool aInAtRule, nsMediaQuery **aQuery,
+  bool GatherMedia(nsMediaList* aMedia, bool aInAtRule);
+
+  enum eMediaQueryType { eMediaQueryNormal,
+                         // Parsing an at rule
+                         eMediaQueryAtRule,
+                         // Attempt to consume a single media-condition and
+                         // stop. Note that the spec defines "expression and/or
+                         // expression" as one condition but "expression,
+                         // expression" as two.
+                         eMediaQuerySingleCondition };
+  bool ParseMediaQuery(eMediaQueryType aMode, nsMediaQuery **aQuery,
                        bool *aHitStop);
   bool ParseMediaQueryExpression(nsMediaQuery* aQuery);
   void ProcessImport(const nsString& aURLSpec,
                      nsMediaList* aMedia,
                      RuleAppendFunc aAppendFunc,
                      void* aProcessData);
   bool ParseGroupRule(css::GroupRule* aRule, RuleAppendFunc aAppendFunc,
                       void* aProcessData);
@@ -1596,16 +1611,100 @@ CSSParserImpl::ParseMediaList(const nsSu
   NS_ASSERTION(parsedOK, "GatherMedia returned false; we probably want to avoid "
                          "trashing aMediaList");
 
   CLEAR_ERROR();
   ReleaseScanner();
   mHTMLMediaMode = false;
 }
 
+//  <source-size-list> = <source-size>#?
+//  <source-size> = <media-condition>? <length>
+bool
+CSSParserImpl::ParseSourceSizeList(const nsAString& aBuffer,
+                                   nsIURI* aURI, // for error reporting
+                                   uint32_t aLineNumber, // for error reporting
+                                   InfallibleTArray< nsAutoPtr<nsMediaQuery> >& aQueries,
+                                   InfallibleTArray<nsCSSValue>& aValues,
+                                   bool aHTMLMode)
+{
+  aQueries.Clear();
+  aValues.Clear();
+
+  // fake base URI since media value lists don't have URIs in them
+  nsCSSScanner scanner(aBuffer, aLineNumber);
+  css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
+  InitScanner(scanner, reporter, aURI, aURI, nullptr);
+
+  // See ParseMediaList comment about HTML mode
+  mHTMLMediaMode = aHTMLMode;
+
+  bool hitError = false;
+  for (;;) {
+    nsAutoPtr<nsMediaQuery> query;
+    nsCSSValue value;
+
+    bool hitStop;
+    if (!ParseMediaQuery(eMediaQuerySingleCondition, getter_Transfers(query),
+                         &hitStop)) {
+      NS_ASSERTION(!hitStop, "should return true when hit stop");
+      hitError = true;
+      break;
+    }
+
+    if (!query) {
+      REPORT_UNEXPECTED_EOF(PEParseSourceSizeListEOF);
+      NS_ASSERTION(hitStop,
+                   "should return hitStop or an error if returning no query");
+      hitError = true;
+      break;
+    }
+
+    if (hitStop) {
+      // Empty conditions (e.g. just a bare value) should be treated as always
+      // matching (a query with no expressions fails to match, so a negated one
+      // always matches.)
+      query->SetNegated();
+    }
+
+    if (!ParseNonNegativeVariant(value, VARIANT_LPCALC, nullptr)) {
+      hitError = true;
+      break;
+    }
+
+    aQueries.AppendElement(query.forget());
+    aValues.AppendElement(value);
+
+    if (!GetToken(true)) {
+      // Expected EOF
+      break;
+    }
+
+    if (eCSSToken_Symbol != mToken.mType || mToken.mSymbol != ',') {
+      REPORT_UNEXPECTED_TOKEN(PEParseSourceSizeListNotComma);
+      hitError = true;
+      break;
+    }
+  }
+
+  if (hitError) {
+    // Per spec, a parse failure in this list invalidates it
+    // entirely. Currently, this grammar is specified standalone and not part of
+    // any larger grammar, so it doesn't make sense to try to advance the token
+    // beyond it.
+    OUTPUT_ERROR();
+  }
+
+  CLEAR_ERROR();
+  ReleaseScanner();
+  mHTMLMediaMode = false;
+
+  return !hitError;
+}
+
 bool
 CSSParserImpl::ParseColorString(const nsSubstring& aBuffer,
                                 nsIURI* aURI, // for error reporting
                                 uint32_t aLineNumber, // for error reporting
                                 nsCSSValue& aValue)
 {
   nsCSSScanner scanner(aBuffer, aLineNumber);
   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
@@ -2728,38 +2827,41 @@ CSSParserImpl::ParseURLOrString(nsString
     aURL = mToken.mIdent;
     return true;
   }
   UngetToken();
   return false;
 }
 
 bool
-CSSParserImpl::ParseMediaQuery(bool aInAtRule,
+CSSParserImpl::ParseMediaQuery(eMediaQueryType aQueryType,
                                nsMediaQuery **aQuery,
                                bool *aHitStop)
 {
   *aQuery = nullptr;
   *aHitStop = false;
+  bool inAtRule = aQueryType == eMediaQueryAtRule;
+  // Attempt to parse a single condition and stop
+  bool singleCondition = aQueryType == eMediaQuerySingleCondition;
 
   // "If the comma-separated list is the empty list it is assumed to
   // specify the media query 'all'."  (css3-mediaqueries, section
   // "Media Queries")
   if (!GetToken(true)) {
     *aHitStop = true;
     // expected termination by EOF
-    if (!aInAtRule)
+    if (!inAtRule)
       return true;
 
     // unexpected termination by EOF
     REPORT_UNEXPECTED_EOF(PEGatherMediaEOF);
     return true;
   }
 
-  if (eCSSToken_Symbol == mToken.mType && aInAtRule &&
+  if (eCSSToken_Symbol == mToken.mType && inAtRule &&
       (mToken.mSymbol == ';' || mToken.mSymbol == '{' || mToken.mSymbol == '}' )) {
     *aHitStop = true;
     UngetToken();
     return true;
   }
   UngetToken();
 
   nsMediaQuery* query = new nsMediaQuery;
@@ -2770,16 +2872,22 @@ CSSParserImpl::ParseMediaQuery(bool aInA
     UngetToken(); // so ParseMediaQueryExpression can handle it
     query->SetType(nsGkAtoms::all);
     query->SetTypeOmitted();
     // Just parse the first expression here.
     if (!ParseMediaQueryExpression(query)) {
       OUTPUT_ERROR();
       query->SetHadUnknownExpression();
     }
+  } else if (singleCondition) {
+    // Since we are only trying to consume a single condition, which precludes
+    // media types and not/only, this should be the same as reaching immediate
+    // EOF (no condition to parse)
+    *aHitStop = true;
+    return true;
   } else {
     nsCOMPtr<nsIAtom> mediaType;
     bool gotNotOrOnly = false;
     for (;;) {
       if (!GetToken(true)) {
         REPORT_UNEXPECTED_EOF(PEGatherMediaEOF);
         return false;
       }
@@ -2814,59 +2922,67 @@ CSSParserImpl::ParseMediaQuery(bool aInA
     }
     query->SetType(mediaType);
   }
 
   for (;;) {
     if (!GetToken(true)) {
       *aHitStop = true;
       // expected termination by EOF
-      if (!aInAtRule)
+      if (!inAtRule)
         break;
 
       // unexpected termination by EOF
       REPORT_UNEXPECTED_EOF(PEGatherMediaEOF);
       break;
     }
 
-    if (eCSSToken_Symbol == mToken.mType && aInAtRule &&
+    if (eCSSToken_Symbol == mToken.mType && inAtRule &&
         (mToken.mSymbol == ';' || mToken.mSymbol == '{' || mToken.mSymbol == '}')) {
       *aHitStop = true;
       UngetToken();
       break;
     }
-    if (eCSSToken_Symbol == mToken.mType && mToken.mSymbol == ',') {
+    if (!singleCondition &&
+        eCSSToken_Symbol == mToken.mType && mToken.mSymbol == ',') {
       // Done with the expressions for this query
       break;
     }
     if (eCSSToken_Ident != mToken.mType ||
         !mToken.mIdent.LowerCaseEqualsLiteral("and")) {
-      REPORT_UNEXPECTED_TOKEN(PEGatherMediaNotComma);
-      UngetToken();
-      return false;
+      if (singleCondition) {
+        // We have a condition at this point -- if we're not chained to other
+        // conditions with and/or, we're done.
+        UngetToken();
+        break;
+      } else {
+        REPORT_UNEXPECTED_TOKEN(PEGatherMediaNotComma);
+        UngetToken();
+        return false;
+      }
     }
     if (!ParseMediaQueryExpression(query)) {
       OUTPUT_ERROR();
       query->SetHadUnknownExpression();
     }
   }
   return true;
 }
 
 // Returns false only when there is a low-level error in the scanner
 // (out-of-memory).
 bool
 CSSParserImpl::GatherMedia(nsMediaList* aMedia,
                            bool aInAtRule)
 {
+  eMediaQueryType type = aInAtRule ? eMediaQueryAtRule : eMediaQueryNormal;
   for (;;) {
     nsAutoPtr<nsMediaQuery> query;
     bool hitStop;
-    if (!ParseMediaQuery(aInAtRule, getter_Transfers(query),
-                         &hitStop)) {
+    if (!ParseMediaQuery(type, getter_Transfers(query), &hitStop)) {
       NS_ASSERTION(!hitStop, "should return true when hit stop");
       OUTPUT_ERROR();
       if (query) {
         query->SetHadUnknownExpression();
       }
       if (aInAtRule) {
         const char16_t stopChars[] =
           { char16_t(','), char16_t('{'), char16_t(';'), char16_t('}'), char16_t(0) };
@@ -14659,16 +14775,29 @@ nsCSSParser::ParseMediaList(const nsSubs
                             nsMediaList*       aMediaList,
                             bool               aHTMLMode)
 {
   static_cast<CSSParserImpl*>(mImpl)->
     ParseMediaList(aBuffer, aURI, aLineNumber, aMediaList, aHTMLMode);
 }
 
 bool
+nsCSSParser::ParseSourceSizeList(const nsAString& aBuffer,
+                                 nsIURI* aURI,
+                                 uint32_t aLineNumber,
+                                 InfallibleTArray< nsAutoPtr<nsMediaQuery> >& aQueries,
+                                 InfallibleTArray<nsCSSValue>& aValues,
+                                 bool aHTMLMode)
+{
+  return static_cast<CSSParserImpl*>(mImpl)->
+    ParseSourceSizeList(aBuffer, aURI, aLineNumber, aQueries, aValues,
+                        aHTMLMode);
+}
+
+bool
 nsCSSParser::ParseFontFamilyListString(const nsSubstring& aBuffer,
                                        nsIURI*            aURI,
                                        uint32_t           aLineNumber,
                                        nsCSSValue&        aValue)
 {
   return static_cast<CSSParserImpl*>(mImpl)->
     ParseFontFamilyListString(aBuffer, aURI, aLineNumber, aValue);
 }
--- a/layout/style/nsCSSParser.h
+++ b/layout/style/nsCSSParser.h
@@ -8,23 +8,25 @@
 #ifndef nsCSSParser_h___
 #define nsCSSParser_h___
 
 #include "mozilla/Attributes.h"
 
 #include "nsCSSProperty.h"
 #include "nsCSSScanner.h"
 #include "nsCOMPtr.h"
+#include "nsAutoPtr.h"
 #include "nsStringFwd.h"
 #include "nsTArrayForwardDeclare.h"
 
 class nsIPrincipal;
 class nsIURI;
 struct nsCSSSelectorList;
 class nsMediaList;
+class nsMediaQuery;
 class nsCSSKeyframeRule;
 class nsCSSValue;
 struct nsRuleData;
 
 namespace mozilla {
 class CSSStyleSheet;
 class CSSVariableValues;
 namespace css {
@@ -147,16 +149,35 @@ public:
    * |aLineNumber| are used for error reporting.
    */
   void ParseMediaList(const nsSubstring& aBuffer,
                       nsIURI*            aURL,
                       uint32_t           aLineNumber,
                       nsMediaList*       aMediaList,
                       bool               aHTMLMode);
 
+  /*
+   * Parse aBuffer into a list of media queries and their associated values,
+   * according to grammar:
+   *    <source-size-list> = <source-size>#?
+   *    <source-size> = <media-condition>? <length>
+   *
+   * Note that this grammar is top-level: The function expects to consume the
+   * entire input buffer.
+   *
+   * Output arrays overwritten (not appended) and are cleared in case of parse
+   * failure.
+   */
+  bool ParseSourceSizeList(const nsAString& aBuffer,
+                           nsIURI* aURI, // for error reporting
+                           uint32_t aLineNumber, // for error reporting
+                           InfallibleTArray< nsAutoPtr<nsMediaQuery> >& aQueries,
+                           InfallibleTArray<nsCSSValue>& aValues,
+                           bool aHTMLMode);
+
   /**
    * Parse aBuffer into a nsCSSValue |aValue|. Will return false
    * if aBuffer is not a valid font family list.
    */
   bool ParseFontFamilyListString(const nsSubstring& aBuffer,
                                  nsIURI*            aURL,
                                  uint32_t           aLineNumber,
                                  nsCSSValue&        aValue);
--- a/layout/tools/reftest/mach_commands.py
+++ b/layout/tools/reftest/mach_commands.py
@@ -79,20 +79,21 @@ class ReftestRunner(MozbuildObject):
             sys.path.append(build_path)
 
         self.tests_dir = os.path.join(self.topobjdir, '_tests')
         self.reftest_dir = os.path.join(self.tests_dir, 'reftest')
 
     def _manifest_file(self, suite):
         """Returns the manifest file used for a given test suite."""
         files = {
-          'reftest': 'reftest.list',
-          'reftest-ipc': 'reftest.list',
-          'crashtest': 'crashtests.list',
-          'crashtest-ipc': 'crashtests.list',
+            'reftest': 'reftest.list',
+            'reftest-ipc': 'reftest.list',
+            'crashtest': 'crashtests.list',
+            'crashtest-ipc': 'crashtests.list',
+            'jstestbrowser': 'jstests.list'
         }
         assert suite in files
         return files[suite]
 
     def _find_manifest(self, suite, test_file):
         assert test_file
         path_arg = self._wrap_path_argument(test_file)
         relpath = path_arg.relpath()
@@ -220,27 +221,27 @@ class ReftestRunner(MozbuildObject):
         test_file is a path to a test file. It can be a relative path from the
         top source directory, an absolute filename, or a directory containing
         test files.
 
         filter is a regular expression (in JS syntax, as could be passed to the
         RegExp constructor) to select which reftests to run from the manifest.
 
         suite is the type of reftest to run. It can be one of ('reftest',
-        'crashtest').
+        'crashtest', 'jstestbrowser').
 
         debugger is the program name (in $PATH) or the full path of the
         debugger to run.
 
         parallel indicates whether tests should be run in parallel or not.
 
         shuffle indicates whether to run tests in random order.
         """
 
-        if suite not in ('reftest', 'reftest-ipc', 'crashtest', 'crashtest-ipc'):
+        if suite not in ('reftest', 'reftest-ipc', 'crashtest', 'crashtest-ipc', 'jstestbrowser'):
             raise Exception('None or unrecognized reftest suite type.')
 
         env = {}
         extra_args = []
 
         if test_file:
             path = self._find_manifest(suite, test_file)
             if not os.path.exists(mozpack.path.join(self.topsrcdir, path)):
@@ -375,16 +376,22 @@ def B2GCommand(func):
 
 @CommandProvider
 class MachCommands(MachCommandBase):
     @Command('reftest', category='testing', description='Run reftests.')
     @ReftestCommand
     def run_reftest(self, test_file, **kwargs):
         return self._run_reftest(test_file, suite='reftest', **kwargs)
 
+    @Command('jstestbrowser', category='testing',
+        description='Run js/src/tests in the browser.')
+    @ReftestCommand
+    def run_jstestbrowser(self, test_file, **kwargs):
+        return self._run_reftest(test_file, suite='jstestbrowser', **kwargs)
+
     @Command('reftest-ipc', category='testing',
         description='Run IPC reftests.')
     @ReftestCommand
     def run_ipc(self, test_file, **kwargs):
         return self._run_reftest(test_file, suite='reftest-ipc', **kwargs)
 
     @Command('crashtest', category='testing',
         description='Run crashtests.')
--- a/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
+++ b/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
@@ -25,16 +25,18 @@
 
 #ifdef MOZ_WIDGET_ANDROID
 #include "AndroidJNIWrapper.h"
 #endif
 
 #include <algorithm>
 #include <math.h>
 
+#define DEFAULT_VIDEO_MAX_FRAMERATE 30
+
 namespace mozilla {
 
 static const char* logTag ="WebrtcVideoSessionConduit";
 
 // 32 bytes is what WebRTC CodecInst expects
 const unsigned int WebrtcVideoConduit::CODEC_PLNAME_SIZE = 32;
 
 /**
@@ -549,16 +551,18 @@ WebrtcVideoConduit::ConfigureSendMediaCo
 {
   CSFLogDebug(logTag,  "%s for %s", __FUNCTION__, codecConfig ? codecConfig->mName.c_str() : "<null>");
   bool codecFound = false;
   MediaConduitErrorCode condError = kMediaConduitNoError;
   int error = 0; //webrtc engine errors
   webrtc::VideoCodec  video_codec;
   std::string payloadName;
 
+  memset(&video_codec, 0, sizeof(video_codec));
+
   //validate basic params
   if((condError = ValidateCodecConfig(codecConfig,true)) != kMediaConduitNoError)
   {
     return condError;
   }
 
   //Check if we have same codec already applied
   if(CheckCodecsForMatch(mCurSendCodecConfig, codecConfig))
@@ -1284,20 +1288,22 @@ WebrtcVideoConduit::CodecConfigToWebRTCC
     PL_strncpyz(cinst.plName, "I420", sizeof(cinst.plName));
   } else {
     cinst.codecType = webrtc::kVideoCodecUnknown;
     PL_strncpyz(cinst.plName, "Unknown", sizeof(cinst.plName));
   }
 
   // width/height will be overridden on the first frame; they must be 'sane' for
   // SetSendCodec()
-  if (codecInfo->mMaxFrameRate > 0)
-  {
+  if (codecInfo->mMaxFrameRate > 0) {
     cinst.maxFramerate = codecInfo->mMaxFrameRate;
+  } else {
+    cinst.maxFramerate = DEFAULT_VIDEO_MAX_FRAMERATE;
   }
+
   cinst.minBitrate = mMinBitrate;
   cinst.startBitrate = mStartBitrate;
   cinst.maxBitrate = mMaxBitrate;
 
   if (cinst.codecType == webrtc::kVideoCodecH264)
   {
     cinst.codecSpecific.H264.profile = codecInfo->mProfile;
     cinst.codecSpecific.H264.constraints = codecInfo->mConstraints;
--- a/mfbt/MathAlgorithms.h
+++ b/mfbt/MathAlgorithms.h
@@ -183,16 +183,22 @@ CountTrailingZeroes32(uint32_t aValue)
 
 inline uint_fast8_t
 CountPopulation32(uint32_t aValue)
 {
   uint32_t x = aValue - ((aValue >> 1) & 0x55555555);
   x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
   return (((x + (x >> 4)) & 0xf0f0f0f) * 0x1010101) >> 24;
 }
+inline uint_fast8_t
+CountPopulation64(uint64_t aValue)
+{
+  return uint_fast8_t(CountPopulation32(aValue & 0xffffffff) +
+                      CountPopulation32(aValue >> 32));
+}
 
 inline uint_fast8_t
 CountLeadingZeroes64(uint64_t aValue)
 {
 #if defined(MOZ_BITSCAN_WINDOWS64)
   unsigned long index;
   _BitScanReverse64(&index, static_cast<unsigned __int64>(aValue));
   return uint_fast8_t(63 - index);
@@ -249,32 +255,39 @@ CountTrailingZeroes32(uint32_t aValue)
 
 inline uint_fast8_t
 CountPopulation32(uint32_t aValue)
 {
   return __builtin_popcount(aValue);
 }
 
 inline uint_fast8_t
+CountPopulation64(uint64_t aValue)
+{
+  return __builtin_popcountll(aValue);
+}
+
+inline uint_fast8_t
 CountLeadingZeroes64(uint64_t aValue)
 {
   return __builtin_clzll(aValue);
 }
 
 inline uint_fast8_t
 CountTrailingZeroes64(uint64_t aValue)
 {
   return __builtin_ctzll(aValue);
 }
 
 #else
 #  error "Implement these!"
 inline uint_fast8_t CountLeadingZeroes32(uint32_t aValue) MOZ_DELETE;
 inline uint_fast8_t CountTrailingZeroes32(uint32_t aValue) MOZ_DELETE;
 inline uint_fast8_t CountPopulation32(uint32_t aValue) MOZ_DELETE;
+inline uint_fast8_t CountPopulation64(uint64_t aValue) MOZ_DELETE;
 inline uint_fast8_t CountLeadingZeroes64(uint64_t aValue) MOZ_DELETE;
 inline uint_fast8_t CountTrailingZeroes64(uint64_t aValue) MOZ_DELETE;
 #endif
 
 } // namespace detail
 
 /**
  * Compute the number of high-order zero bits in the NON-ZERO number |aValue|.
@@ -316,16 +329,23 @@ CountTrailingZeroes32(uint32_t aValue)
  * Compute the number of one bits in the number |aValue|,
  */
 inline uint_fast8_t
 CountPopulation32(uint32_t aValue)
 {
   return detail::CountPopulation32(aValue);
 }
 
+/** Analogous to CoutPopulation32, but for 64-bit numbers */
+inline uint_fast8_t
+CountPopulation64(uint64_t aValue)
+{
+  return detail::CountPopulation64(aValue);
+}
+
 /** Analogous to CountLeadingZeroes32, but for 64-bit numbers. */
 inline uint_fast8_t
 CountLeadingZeroes64(uint64_t aValue)
 {
   MOZ_ASSERT(aValue != 0);
   return detail::CountLeadingZeroes64(aValue);
 }
 
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -3925,16 +3925,19 @@ pref("dom.vibrator.max_vibrate_ms", 1000
 pref("dom.vibrator.max_vibrate_list_len", 128);
 
 // Battery API
 pref("dom.battery.enabled", true);
 
 // Image srcset
 pref("dom.image.srcset.enabled", false);
 
+// <picture> element and sizes
+pref("dom.image.picture.enabled", false);
+
 // WebSMS
 pref("dom.sms.enabled", false);
 // Enable Latin characters replacement with corresponding ones in GSM SMS
 // 7-bit default alphabet.
 pref("dom.sms.strict7BitEncoding", false);
 pref("dom.sms.requestStatusReport", true);
 // Numeric default service id for SMS API calls with |serviceId| parameter
 // omitted.
--- a/netwerk/cache2/CacheEntry.cpp
+++ b/netwerk/cache2/CacheEntry.cpp
@@ -307,24 +307,40 @@ bool CacheEntry::Load(bool aTruncate, bo
 
   MOZ_ASSERT(!mFile);
 
   nsresult rv;
 
   nsAutoCString fileKey;
   rv = HashingKeyWithStorage(fileKey);
 
-  if (!aTruncate && NS_SUCCEEDED(rv)) {
+  // Check the index under two conditions for two states and take appropriate action:
+  // 1. When this is a disk entry and not told to truncate, check there is a disk file.
+  //    If not, set the 'truncate' flag to true so that this entry will open instantly
+  //    as a new one.
+  // 2. When this is a memory-only entry, check there is a disk file.
+  //    If there is or could be, doom that file.
+  if ((!aTruncate || !mUseDisk) && NS_SUCCEEDED(rv)) {
     // Check the index right now to know we have or have not the entry
     // as soon as possible.
     CacheIndex::EntryStatus status;
-    if (NS_SUCCEEDED(CacheIndex::HasEntry(fileKey, &status)) &&
-        status == CacheIndex::DOES_NOT_EXIST) {
-      LOG(("  entry doesn't exist according information from the index, truncating"));
-      aTruncate = true;
+    if (NS_SUCCEEDED(CacheIndex::HasEntry(fileKey, &status))) {
+      switch (status) {
+      case CacheIndex::DOES_NOT_EXIST:
+        LOG(("  entry doesn't exist according information from the index, truncating"));
+        aTruncate = true;
+        break;
+      case CacheIndex::EXISTS:
+      case CacheIndex::DO_NOT_KNOW:
+        if (!mUseDisk) {
+          LOG(("  entry open as memory-only, but there is (status=%d) a file, dooming it", status));
+          CacheFileIOManager::DoomFileByKey(fileKey, nullptr);
+        }
+        break;
+      }
     }
   }
 
   mFile = new CacheFile();
 
   BackgroundOp(Ops::REGISTER);
 
   bool directLoad = aTruncate || !mUseDisk;
@@ -835,40 +851,16 @@ void CacheEntry::OnOutputClosed()
 {
   // Called when the file's output stream is closed.  Invoke any callbacks
   // waiting for complete entry.
 
   mozilla::MutexAutoLock lock(mLock);
   InvokeCallbacks();
 }
 
-bool CacheEntry::IsUsingDiskLocked() const
-{
-  CacheStorageService::Self()->Lock().AssertCurrentThreadOwns();
-
-  return IsUsingDisk();
-}
-
-bool CacheEntry::SetUsingDisk(bool aUsingDisk)
-{
-  // Called by the service when this entry is reopen to reflect
-  // demanded storage target.
-
-  if (mState >= READY) {
-    // Don't modify after this entry has been filled.
-    return false;
-  }
-
-  CacheStorageService::Self()->Lock().AssertCurrentThreadOwns();
-
-  bool changed = mUseDisk != aUsingDisk;
-  mUseDisk = aUsingDisk;
-  return changed;
-}
-
 bool CacheEntry::IsReferenced() const
 {
   CacheStorageService::Self()->Lock().AssertCurrentThreadOwns();
 
   // Increasing this counter from 0 to non-null and this check both happen only
   // under the service lock.
   return mHandlesCount > 0;
 }
--- a/netwerk/cache2/CacheEntry.h
+++ b/netwerk/cache2/CacheEntry.h
@@ -62,18 +62,16 @@ public:
 
   CacheEntryHandle* NewHandle();
 
 public:
   uint32_t GetMetadataMemoryConsumption();
   nsCString const &GetStorageID() const { return mStorageID; }
   nsCString const &GetEnhanceID() const { return mEnhanceID; }
   nsIURI* GetURI() const { return mURI; }
-  // Accessible only under the CacheStorageService lock (asserts it)
-  bool IsUsingDiskLocked() const;
   // Accessible at any time
   bool IsUsingDisk() const { return mUseDisk; }
   bool SetUsingDisk(bool aUsingDisk);
   bool IsReferenced() const;
   bool IsFileDoomed();
 
   // Methods for entry management (eviction from memory),
   // called only on the management thread.
@@ -260,19 +258,17 @@ private:
 
   nsRefPtr<CacheFile> mFile;
   nsresult mFileStatus;
   nsCOMPtr<nsIURI> mURI;
   nsCString mEnhanceID;
   nsCString mStorageID;
 
   // Whether it's allowed to persist the data to disk
-  // Synchronized by the service management lock.
-  // Hence, leave it as a standalone boolean.
-  bool mUseDisk;
+  bool const mUseDisk;
 
   // Set when entry is doomed with AsyncDoom() or DoomAlreadyRemoved().
   // Left as a standalone flag to not bother with locking (there is no need).
   bool mIsDoomed;
 
   // Following flags are all synchronized with the cache entry lock.
 
   // Whether security info has already been looked up in metadata.
--- a/netwerk/cache2/CacheStorageService.cpp
+++ b/netwerk/cache2/CacheStorageService.cpp
@@ -285,17 +285,17 @@ private:
   WalkStorage(const nsACString& aKey,
               CacheEntry* aEntry,
               void* aClosure)
   {
     WalkMemoryCacheRunnable* walker =
       static_cast<WalkMemoryCacheRunnable*>(aClosure);
 
     // Ignore disk entries
-    if (aEntry->IsUsingDiskLocked())
+    if (aEntry->IsUsingDisk())
       return PL_DHASH_NEXT;
 
     walker->mSize += aEntry->GetMetadataMemoryConsumption();
 
     int64_t size;
     if (NS_SUCCEEDED(aEntry->GetDataSize(&size)))
       walker->mSize += size;
 
@@ -1288,45 +1288,47 @@ CacheStorageService::AddStorageEntry(nsC
 
     NS_ENSURE_FALSE(mShutdown, NS_ERROR_NOT_INITIALIZED);
 
     // Ensure storage table
     CacheEntryTable* entries;
     if (!sGlobalEntryTables->Get(aContextKey, &entries)) {
       entries = new CacheEntryTable(CacheEntryTable::ALL_ENTRIES);
       sGlobalEntryTables->Put(aContextKey, entries);
-      LOG(("  new storage entries table for context %s", aContextKey.BeginReading()));
+      LOG(("  new storage entries table for context '%s'", aContextKey.BeginReading()));
     }
 
     bool entryExists = entries->Get(entryKey, getter_AddRefs(entry));
 
-    // check whether the file is already doomed
-    if (entryExists && entry->IsFileDoomed() && !aReplace) {
-      LOG(("  file already doomed, replacing the entry"));
-      aReplace = true;
+    if (entryExists && !aReplace) {
+      // check whether the file is already doomed or we want to turn this entry
+      // to a memory-only.
+      if (MOZ_UNLIKELY(entry->IsFileDoomed())) {
+        LOG(("  file already doomed, replacing the entry"));
+        aReplace = true;
+      } else if (MOZ_UNLIKELY(!aWriteToDisk) && MOZ_LIKELY(entry->IsUsingDisk())) {
+        LOG(("  entry is persistnet but we want mem-only, replacing it"));
+        aReplace = true;
+      }
     }
 
     // If truncate is demanded, delete and doom the current entry
     if (entryExists && aReplace) {
       entries->Remove(entryKey);
 
       LOG(("  dooming entry %p for %s because of OPEN_TRUNCATE", entry.get(), entryKey.get()));
       // On purpose called under the lock to prevent races of doom and open on I/O thread
       // No need to remove from both memory-only and all-entries tables.  The new entry
       // will overwrite the shadow entry in its ctor.
       entry->DoomAlreadyRemoved();
 
       entry = nullptr;
       entryExists = false;
     }
 
-    if (entryExists && entry->SetUsingDisk(aWriteToDisk)) {
-      RecordMemoryOnlyEntry(entry, !aWriteToDisk, true /* overwrite */);
-    }
-
     // Ensure entry for the particular URL, if not read/only
     if (!entryExists && (aCreateIfNotExist || aReplace)) {
       // Entry is not in the hashtable or has just been truncated...
       entry = new CacheEntry(aContextKey, aURI, aIdExtension, aWriteToDisk);
       entries->Put(entryKey, entry);
       LOG(("  new entry %p for %s", entry.get(), entryKey.get()));
     }
 
@@ -1467,27 +1469,27 @@ CacheStorageService::DoomStorageEntry(Ca
   {
     mozilla::MutexAutoLock lock(mLock);
 
     NS_ENSURE_FALSE(mShutdown, NS_ERROR_NOT_INITIALIZED);
 
     CacheEntryTable* entries;
     if (sGlobalEntryTables->Get(contextKey, &entries)) {
       if (entries->Get(entryKey, getter_AddRefs(entry))) {
-        if (aStorage->WriteToDisk() || !entry->IsUsingDiskLocked()) {
+        if (aStorage->WriteToDisk() || !entry->IsUsingDisk()) {
           // When evicting from disk storage, purge
           // When evicting from memory storage and the entry is memory-only, purge
           LOG(("  purging entry %p for %s [storage use disk=%d, entry use disk=%d]",
-            entry.get(), entryKey.get(), aStorage->WriteToDisk(), entry->IsUsingDiskLocked()));
+            entry.get(), entryKey.get(), aStorage->WriteToDisk(), entry->IsUsingDisk()));
           entries->Remove(entryKey);
         }
         else {
           // Otherwise, leave it
           LOG(("  leaving entry %p for %s [storage use disk=%d, entry use disk=%d]",
-            entry.get(), entryKey.get(), aStorage->WriteToDisk(), entry->IsUsingDiskLocked()));
+            entry.get(), entryKey.get(), aStorage->WriteToDisk(), entry->IsUsingDisk()));
           entry = nullptr;
         }
       }
     }
   }
 
   if (entry) {
     LOG(("  dooming entry %p for %s", entry.get(), entryKey.get()));
@@ -1786,17 +1788,17 @@ size_t CollectEntryMemory(nsACString con
   CacheEntryTable* aTable = static_cast<CacheEntryTable*>(aClosure);
 
   size_t n = 0;
   n += aKey.SizeOfExcludingThisIfUnshared(mallocSizeOf);
 
   // Bypass memory-only entries, those will be reported when iterating
   // the memory only table. Memory-only entries are stored in both ALL_ENTRIES
   // and MEMORY_ONLY hashtables.
-  if (aTable->Type() == CacheEntryTable::MEMORY_ONLY || aEntry->IsUsingDiskLocked())
+  if (aTable->Type() == CacheEntryTable::MEMORY_ONLY || aEntry->IsUsingDisk())
     n += aEntry->SizeOfIncludingThis(mallocSizeOf);
 
   return n;
 }
 
 PLDHashOperator ReportStorageMemory(const nsACString& aKey,
                                     CacheEntryTable* aTable,
                                     void* aClosure)
--- a/netwerk/cache2/nsICacheStorageService.idl
+++ b/netwerk/cache2/nsICacheStorageService.idl
@@ -15,16 +15,21 @@ interface nsICacheStorageConsumptionObse
  */
 [scriptable, uuid(44de2fa4-1b0e-4cd3-9e32-211e936f721e)]
 interface nsICacheStorageService : nsISupports
 {
   /**
    * Get storage where entries will only remain in memory, never written
    * to the disk.
    *
+   * NOTE: Any existing disk entry for [URL|id-extension] will be doomed
+   * prior opening an entry using this memory-only storage.  Result of
+   * AsyncOpenURI will be a new and empty memory-only entry.  Using
+   * OPEN_READONLY open flag has no effect on this behavior.
+   *
    * @param aLoadContextInfo
    *    Information about the loading context, this focuses the storage JAR and
    *    respects separate storage for private browsing.
    */
   nsICacheStorage memoryCacheStorage(in nsILoadContextInfo aLoadContextInfo);
 
   /**
    * Get storage where entries will be written to disk when not forbidden by
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit/test_cache2-07a-open-memory.js
@@ -0,0 +1,53 @@
+function run_test()
+{
+  do_get_profile();
+
+  if (!newCacheBackEndUsed()) {
+    do_check_true(true, "This test doesn't run when the old cache back end is used since the behavior is different");
+    return;
+  }
+
+  // First check how behaves the memory storage.
+
+  asyncOpenCacheEntry("http://mem-first/", "memory", Ci.nsICacheStorage.OPEN_NORMALLY, null,
+    new OpenCallback(NEW, "mem1-meta", "mem1-data", function(entryM1) {
+      do_check_false(entryM1.persistent);
+      asyncOpenCacheEntry("http://mem-first/", "disk", Ci.nsICacheStorage.OPEN_NORMALLY, null,
+        new OpenCallback(NORMAL, "mem1-meta", "mem1-data", function(entryM2) {
+          do_check_false(entryM1.persistent);
+          do_check_false(entryM2.persistent);
+
+          // Now check the disk storage behavior.
+
+          asyncOpenCacheEntry("http://disk-first/", "disk", Ci.nsICacheStorage.OPEN_NORMALLY, null,
+            // Must wait for write, since opening the entry as memory-only before the disk one
+            // is written would cause NS_ERROR_NOT_AVAILABLE from openOutputStream when writing
+            // this disk entry since it's doomed during opening of the memory-only entry for the same URL.
+            new OpenCallback(NEW|WAITFORWRITE, "disk1-meta", "disk1-data", function(entryD1) {
+              do_check_true(entryD1.persistent);
+              // Now open the same URL as a memory-only entry, the disk entry must be doomed.
+              asyncOpenCacheEntry("http://disk-first/", "memory", Ci.nsICacheStorage.OPEN_NORMALLY, null,
+                // This must be recreated
+                new OpenCallback(NEW, "mem2-meta", "mem2-data", function(entryD2) {
+                  do_check_true(entryD1.persistent);
+                  do_check_false(entryD2.persistent);
+                  // Check we get it back, even when opening via the disk storage
+                  asyncOpenCacheEntry("http://disk-first/", "disk", Ci.nsICacheStorage.OPEN_NORMALLY, null,
+                    new OpenCallback(NORMAL, "mem2-meta", "mem2-data", function(entryD3) {
+                      do_check_true(entryD1.persistent);
+                      do_check_false(entryD2.persistent);
+                      do_check_false(entryD3.persistent);
+                      finish_cache2_test();
+                    })
+                  );
+                })
+              );
+            })
+          );
+        })
+      );
+    })
+  );
+
+  do_test_pending();
+}
--- a/netwerk/test/unit/xpcshell.ini
+++ b/netwerk/test/unit/xpcshell.ini
@@ -31,16 +31,19 @@ support-files =
 [test_cache2-04-oncacheentryavail-throws2x.js]
 [test_cache2-05-visit.js]
 [test_cache2-06-pb-mode.js]
 # Bug 675039, comment 6: "The difference is that the memory cache is disabled in Armv6 builds."
 skip-if = os == "android"
 [test_cache2-07-visit-memory.js]
 # Bug 675039, comment 6: "The difference is that the memory cache is disabled in Armv6 builds."
 skip-if = os == "android"
+[test_cache2-07a-open-memory.js]
+# Bug 675039, comment 6: "The difference is that the memory cache is disabled in Armv6 builds."
+skip-if = os == "android"
 [test_cache2-08-evict-disk-by-memory-storage.js]
 [test_cache2-09-evict-disk-by-uri.js]
 [test_cache2-10-evict-direct.js]
 [test_cache2-10b-evict-direct-immediate.js]
 [test_cache2-11-evict-memory.js]
 # Bug 675039, comment 6: "The difference is that the memory cache is disabled in Armv6 builds."
 skip-if = os == "android"
 [test_cache2-12-evict-disk.js]
--- a/parser/html/javasrc/AttributeName.java
+++ b/parser/html/javasrc/AttributeName.java
@@ -831,16 +831,17 @@ public final class AttributeName
     public static final AttributeName SLOPE = new AttributeName(ALL_NO_NS, SAME_LOCAL("slope"), ALL_NO_PREFIX, NCNAME_HTML | NCNAME_FOREIGN | NCNAME_LANG);
     public static final AttributeName SHAPE = new AttributeName(ALL_NO_NS, SAME_LOCAL("shape"), ALL_NO_PREFIX, NCNAME_HTML | NCNAME_FOREIGN | NCNAME_LANG | CASE_FOLDED);
     public static final AttributeName SCOPE = new AttributeName(ALL_NO_NS, SAME_LOCAL("scope"), ALL_NO_PREFIX, NCNAME_HTML | NCNAME_FOREIGN | NCNAME_LANG | CASE_FOLDED);
     public static final AttributeName SCALE = new AttributeName(ALL_NO_NS, SAME_LOCAL("scale"), ALL_NO_PREFIX, NCNAME_HTML | NCNAME_FOREIGN | NCNAME_LANG);
     public static final AttributeName SPEED = new AttributeName(ALL_NO_NS, SAME_LOCAL("speed"), ALL_NO_PREFIX, NCNAME_HTML | NCNAME_FOREIGN | NCNAME_LANG);
     public static final AttributeName STYLE = new AttributeName(ALL_NO_NS, SAME_LOCAL("style"), ALL_NO_PREFIX, NCNAME_HTML | NCNAME_FOREIGN | NCNAME_LANG);
     public static final AttributeName RULES = new AttributeName(ALL_NO_NS, SAME_LOCAL("rules"), ALL_NO_PREFIX, NCNAME_HTML | NCNAME_FOREIGN | NCNAME_LANG | CASE_FOLDED);
     public static final AttributeName STEMH = new AttributeName(ALL_NO_NS, SAME_LOCAL("stemh"), ALL_NO_PREFIX, NCNAME_HTML | NCNAME_FOREIGN | NCNAME_LANG);
+    public static final AttributeName SIZES = new AttributeName(ALL_NO_NS, SAME_LOCAL("sizes"), ALL_NO_PREFIX, NCNAME_HTML | NCNAME_FOREIGN | NCNAME_LANG);
     public static final AttributeName STEMV = new AttributeName(ALL_NO_NS, SAME_LOCAL("stemv"), ALL_NO_PREFIX, NCNAME_HTML | NCNAME_FOREIGN | NCNAME_LANG);
     public static final AttributeName START = new AttributeName(ALL_NO_NS, SAME_LOCAL("start"), ALL_NO_PREFIX, NCNAME_HTML | NCNAME_FOREIGN | NCNAME_LANG);
     public static final AttributeName XMLNS = new AttributeName(XMLNS_NS, SAME_LOCAL("xmlns"), ALL_NO_PREFIX, IS_XMLNS);
     public static final AttributeName ACCEPT = new AttributeName(ALL_NO_NS, SAME_LOCAL("accept"), ALL_NO_PREFIX, NCNAME_HTML | NCNAME_FOREIGN | NCNAME_LANG);
     public static final AttributeName ACCENT = new AttributeName(ALL_NO_NS, SAME_LOCAL("accent"), ALL_NO_PREFIX, NCNAME_HTML | NCNAME_FOREIGN | NCNAME_LANG);
     public static final AttributeName ASCENT = new AttributeName(ALL_NO_NS, SAME_LOCAL("ascent"), ALL_NO_PREFIX, NCNAME_HTML | NCNAME_FOREIGN | NCNAME_LANG);
     public static final AttributeName ACTIVE = new AttributeName(ALL_NO_NS, SAME_LOCAL("active"), ALL_NO_PREFIX, NCNAME_HTML | NCNAME_FOREIGN | NCNAME_LANG | CASE_FOLDED | BOOLEAN);
     public static final AttributeName ALTIMG = new AttributeName(ALL_NO_NS, SAME_LOCAL("altimg"), ALL_NO_PREFIX, NCNAME_HTML | NCNAME_FOREIGN | NCNAME_LANG);
@@ -1415,16 +1416,17 @@ public final class AttributeName
     SLOPE,
     SHAPE,
     SCOPE,
     SCALE,
     SPEED,
     STYLE,
     RULES,
     STEMH,
+    SIZES,
     STEMV,
     START,
     XMLNS,
     ACCEPT,
     ACCENT,
     ASCENT,
     ACTIVE,
     ALTIMG,
@@ -2000,16 +2002,17 @@ public final class AttributeName
     190977533,
     190992569,
     191006194,
     191033518,
     191038774,
     191096249,
     191166163,
     191194426,
+    191443343,
     191522106,
     191568039,
     200104642,
     202506661,
     202537381,
     202602917,
     203070590,
     203120766,
--- a/parser/html/javasrc/ElementName.java
+++ b/parser/html/javasrc/ElementName.java
@@ -688,16 +688,17 @@ public final class ElementName
     public static final ElementName MFENCED = new ElementName("mfenced", "mfenced", TreeBuilder.OTHER);
     public static final ElementName MPADDED = new ElementName("mpadded", "mpadded", TreeBuilder.OTHER);
     public static final ElementName MARQUEE = new ElementName("marquee", "marquee", TreeBuilder.MARQUEE_OR_APPLET | SPECIAL | SCOPING);
     public static final ElementName MACTION = new ElementName("maction", "maction", TreeBuilder.OTHER);
     public static final ElementName MSUBSUP = new ElementName("msubsup", "msubsup", TreeBuilder.OTHER);
     public static final ElementName NOEMBED = new ElementName("noembed", "noembed", TreeBuilder.NOEMBED | SPECIAL);
     public static final ElementName POLYGON = new ElementName("polygon", "polygon", TreeBuilder.OTHER);
     public static final ElementName PATTERN = new ElementName("pattern", "pattern", TreeBuilder.OTHER);
+    public static final ElementName PICTURE = new ElementName("picture", "picture", TreeBuilder.OTHER);
     public static final ElementName PRODUCT = new ElementName("product", "product", TreeBuilder.OTHER);
     public static final ElementName SETDIFF = new ElementName("setdiff", "setdiff", TreeBuilder.OTHER);
     public static final ElementName SECTION = new ElementName("section", "section", TreeBuilder.ADDRESS_OR_ARTICLE_OR_ASIDE_OR_DETAILS_OR_DIR_OR_FIGCAPTION_OR_FIGURE_OR_FOOTER_OR_HEADER_OR_HGROUP_OR_MAIN_OR_NAV_OR_SECTION_OR_SUMMARY | SPECIAL);
     public static final ElementName SUMMARY = new ElementName("summary", "summary", TreeBuilder.ADDRESS_OR_ARTICLE_OR_ASIDE_OR_DETAILS_OR_DIR_OR_FIGCAPTION_OR_FIGURE_OR_FOOTER_OR_HEADER_OR_HGROUP_OR_MAIN_OR_NAV_OR_SECTION_OR_SUMMARY | SPECIAL);
     public static final ElementName TENDSTO = new ElementName("tendsto", "tendsto", TreeBuilder.OTHER);
     public static final ElementName UPLIMIT = new ElementName("uplimit", "uplimit", TreeBuilder.OTHER);
     public static final ElementName ALTGLYPH = new ElementName("altglyph", "altGlyph", TreeBuilder.OTHER);
     public static final ElementName BASEFONT = new ElementName("basefont", "basefont", TreeBuilder.LINK_OR_BASEFONT_OR_BGSOUND | SPECIAL);
@@ -1085,16 +1086,17 @@ public final class ElementName
     MFENCED,
     MPADDED,
     MARQUEE,
     MACTION,
     MSUBSUP,
     NOEMBED,
     POLYGON,
     PATTERN,
+    PICTURE,
     PRODUCT,
     SETDIFF,
     SECTION,
     SUMMARY,
     TENDSTO,
     UPLIMIT,
     ALTGLYPH,
     BASEFONT,
@@ -1483,16 +1485,17 @@ public final class ElementName
     248648814,
     248648836,
     248682161,
     248986932,
     249058914,
     249697357,
     252132601,
     252135604,
+    251841204,
     252317348,
     255007012,
     255278388,
     255641645,
     256365156,
     257566121,
     269763372,
     271202790,
--- a/parser/html/nsHtml5AtomList.h
+++ b/parser/html/nsHtml5AtomList.h
@@ -177,16 +177,17 @@ HTML5_ATOM(vlink, "vlink")
 HTML5_ATOM(value, "value")
 HTML5_ATOM(slope, "slope")
 HTML5_ATOM(shape, "shape")
 HTML5_ATOM(scope, "scope")
 HTML5_ATOM(scale, "scale")
 HTML5_ATOM(speed, "speed")
 HTML5_ATOM(rules, "rules")
 HTML5_ATOM(stemh, "stemh")
+HTML5_ATOM(sizes, "sizes")
 HTML5_ATOM(stemv, "stemv")
 HTML5_ATOM(start, "start")
 HTML5_ATOM(accept, "accept")
 HTML5_ATOM(accent, "accent")
 HTML5_ATOM(ascent, "ascent")
 HTML5_ATOM(active, "active")
 HTML5_ATOM(altimg, "altimg")
 HTML5_ATOM(action, "action")
@@ -940,16 +941,17 @@ HTML5_ATOM(isindex, "isindex")
 HTML5_ATOM(logbase, "logbase")
 HTML5_ATOM(listing, "listing")
 HTML5_ATOM(mfenced, "mfenced")
 HTML5_ATOM(mpadded, "mpadded")
 HTML5_ATOM(marquee, "marquee")
 HTML5_ATOM(maction, "maction")
 HTML5_ATOM(msubsup, "msubsup")
 HTML5_ATOM(polygon, "polygon")
+HTML5_ATOM(picture, "picture")
 HTML5_ATOM(product, "product")
 HTML5_ATOM(setdiff, "setdiff")
 HTML5_ATOM(section, "section")
 HTML5_ATOM(tendsto, "tendsto")
 HTML5_ATOM(uplimit, "uplimit")
 HTML5_ATOM(altglyph, "altglyph")
 HTML5_ATOM(altGlyph, "altGlyph")
 HTML5_ATOM(basefont, "basefont")
--- a/parser/html/nsHtml5AttributeName.cpp
+++ b/parser/html/nsHtml5AttributeName.cpp
@@ -309,16 +309,17 @@ nsHtml5AttributeName* nsHtml5AttributeNa
 nsHtml5AttributeName* nsHtml5AttributeName::ATTR_SLOPE = nullptr;
 nsHtml5AttributeName* nsHtml5AttributeName::ATTR_SHAPE = nullptr;
 nsHtml5AttributeName* nsHtml5AttributeName::ATTR_SCOPE = nullptr;
 nsHtml5AttributeName* nsHtml5AttributeName::ATTR_SCALE = nullptr;
 nsHtml5AttributeName* nsHtml5AttributeName::ATTR_SPEED = nullptr;
 nsHtml5AttributeName* nsHtml5AttributeName::ATTR_STYLE = nullptr;
 nsHtml5AttributeName* nsHtml5AttributeName::ATTR_RULES = nullptr;
 nsHtml5AttributeName* nsHtml5AttributeName::ATTR_STEMH = nullptr;
+nsHtml5AttributeName* nsHtml5AttributeName::ATTR_SIZES = nullptr;
 nsHtml5AttributeName* nsHtml5AttributeName::ATTR_STEMV = nullptr;
 nsHtml5AttributeName* nsHtml5AttributeName::ATTR_START = nullptr;
 nsHtml5AttributeName* nsHtml5AttributeName::ATTR_XMLNS = nullptr;
 nsHtml5AttributeName* nsHtml5AttributeName::ATTR_ACCEPT = nullptr;
 nsHtml5AttributeName* nsHtml5AttributeName::ATTR_ACCENT = nullptr;
 nsHtml5AttributeName* nsHtml5AttributeName::ATTR_ASCENT = nullptr;
 nsHtml5AttributeName* nsHtml5AttributeName::ATTR_ACTIVE = nullptr;
 nsHtml5AttributeName* nsHtml5AttributeName::ATTR_ALTIMG = nullptr;
@@ -774,17 +775,17 @@ nsHtml5AttributeName* nsHtml5AttributeNa
 nsHtml5AttributeName* nsHtml5AttributeName::ATTR_VERYVERYTHICKMATHSPACE = nullptr;
 nsHtml5AttributeName* nsHtml5AttributeName::ATTR_STRIKETHROUGH_POSITION = nullptr;
 nsHtml5AttributeName* nsHtml5AttributeName::ATTR_STRIKETHROUGH_THICKNESS = nullptr;
 nsHtml5AttributeName* nsHtml5AttributeName::ATTR_EXTERNALRESOURCESREQUIRED = nullptr;
 nsHtml5AttributeName* nsHtml5AttributeName::ATTR_GLYPH_ORIENTATION_VERTICAL = nullptr;
 nsHtml5AttributeName* nsHtml5AttributeName::ATTR_COLOR_INTERPOLATION_FILTERS = nullptr;
 nsHtml5AttributeName* nsHtml5AttributeName::ATTR_GLYPH_ORIENTATION_HORIZONTAL = nullptr;
 nsHtml5AttributeName** nsHtml5AttributeName::ATTRIBUTE_NAMES = 0;
-static int32_t const ATTRIBUTE_HASHES_DATA[] = { 1153, 1383, 1601, 1793, 1827, 1857, 68600, 69146, 69177, 70237, 70270, 71572, 71669, 72415, 72444, 74846, 74904, 74943, 75001, 75276, 75590, 84742, 84839, 85575, 85963, 85992, 87204, 88074, 88171, 89130, 89163, 3207892, 3283895, 3284791, 3338752, 3358197, 3369562, 3539124, 3562402, 3574260, 3670335, 3696933, 3721879, 135280021, 135346322, 136317019, 136475749, 136548517, 136652214, 136884919, 136902418, 136942992, 137292068, 139120259, 139785574, 142250603, 142314056, 142331176, 142519584, 144752417, 145106895, 146147200, 146765926, 148805544, 149655723, 149809441, 150018784, 150445028, 150813181, 150923321, 152528754, 152536216, 152647366, 152962785, 155219321, 155654904, 157317483, 157350248, 157437941, 157447478, 157604838, 157685404, 157894402, 158315188, 166078431, 169409980, 169700259, 169856932, 170007032, 170409695, 170466488, 170513710, 170608367, 173028944, 173896963, 176090625, 176129212, 179390001, 179489057, 179627464, 179840468, 179849042, 180004216, 181779081, 183027151, 183645319, 183698797, 185922012, 185997252, 188312483, 188675799, 190977533, 190992569, 191006194, 191033518, 191038774, 191096249, 191166163, 191194426, 191522106, 191568039, 200104642, 202506661, 202537381, 202602917, 203070590, 203120766, 203389054, 203690071, 203971238, 203986524, 209040857, 209125756, 212055489, 212322418, 212746849, 213002877, 213055164, 213088023, 213259873, 213273386, 213435118, 213437318, 213438231, 213493071, 213532268, 213542834, 213584431, 213659891, 215285828, 215880731, 216112976, 216684637, 217369699, 217565298, 217576549, 218186795, 219743185, 220082234, 221623802, 221986406, 222283890, 223089542, 223138630, 223311265, 224431494, 224547358, 224587256, 224589550, 224655650, 224785518, 224810917, 224813302, 225126263, 225429618, 225432950, 225440869, 236107233, 236709921, 236838947, 237117095, 237143271, 237172455, 237209953, 237354143, 237372743, 237668065, 237703073, 237714273, 239743521, 240512803, 240522627, 240560417, 240656513, 241015715, 241062755, 241065383, 243523041, 245865199, 246261793, 246556195, 246774817, 246923491, 246928419, 246981667, 247014847, 247058369, 247112833, 247118177, 247119137, 247128739, 247316903, 249533729, 250235623, 250269543, 251402351, 252339047, 253260911, 253293679, 254844367, 255547879, 256077281, 256345377, 258124199, 258354465, 258605063, 258744193, 258845603, 258856961, 258926689, 269869248, 270174334, 270709417, 270778994, 270781796, 271102503, 271478858, 271490090, 272870654, 273335275, 273369140, 273924313, 274108530, 274116736, 276818662, 277476156, 279156579, 279349675, 280108533, 280128712, 280132869, 280162403, 280280292, 280413430, 280506130, 280677397, 280678580, 280686710, 280689066, 282736758, 283110901, 283275116, 283823226, 283890012, 284479340, 284606461, 286700477, 286798916, 291557706, 291665349, 291804100, 292138018, 292166446, 292418738, 292451039, 300298041, 300374839, 300597935, 303073389, 303083839, 303266673, 303354997, 303430688, 303576261, 303724281, 303819694, 304242723, 304382625, 306247792, 307227811, 307468786, 307724489, 309671175, 310252031, 310358241, 310373094, 310833159, 311015256, 313357609, 313683893, 313701861, 313706996, 313707317, 313710350, 314027746, 314038181, 314091299, 314205627, 314233813, 316741830, 316797986, 317486755, 317794164, 320076137, 322657125, 322887778, 323506876, 323572412, 323605180, 325060058, 325320188, 325398738, 325541490, 325671619, 333868843, 336806130, 337212108, 337282686, 337285434, 337585223, 338036037, 338298087, 338566051, 340943551, 341190970, 342995704, 343352124, 343912673, 344585053, 346977248, 347218098, 347262163, 347278576, 347438191, 347655959, 347684788, 347726430, 347727772, 347776035, 347776629, 349500753, 350880161, 350887073, 353384123, 355496998, 355906922, 355979793, 356545959, 358637867, 358905016, 359164318, 359247286, 359350571, 359579447, 365560330, 367399355, 367420285, 367510727, 368013212, 370234760, 370353345, 370710317, 371074566, 371122285, 371194213, 371448425, 371448430, 371545055, 371593469, 371596922, 371758751, 371964792, 372151328, 376550136, 376710172, 376795771, 376826271, 376906556, 380514830, 380774774, 380775037, 381030322, 381136500, 381281631, 381282269, 381285504, 381330595, 381331422, 381335911, 381336484, 383907298, 383917408, 384595009, 384595013, 387799894, 387823201, 392581647, 392584937, 392742684, 392906485, 393003349, 400644707, 400973830, 404428547, 404432113, 404432865, 404469244, 404478897, 404694860, 406887479, 408294949, 408789955, 410022510, 410467324, 410586448, 410945965, 411845275, 414327152, 414327932, 414329781, 414346257, 414346439, 414639928, 414835998, 414894517, 414986533, 417465377, 417465381, 417492216, 418259232, 419310946, 420103495, 420242342, 420380455, 420658662, 420717432, 423183880, 424539259, 425929170, 425972964, 426050649, 426126450, 426142833, 426607922, 437289840, 437347469, 437412335, 437423943, 437455540, 437462252, 437597991, 437617485, 437986305, 437986507, 437986828, 437987072, 438015591, 438034813, 438038966, 438179623, 438347971, 438483573, 438547062, 438895551, 441592676, 442032555, 443548979, 447881379, 447881655, 447881895, 447887844, 448416189, 448445746, 448449012, 450942191, 452816744, 453668677, 454434495, 456610076, 456642844, 456738709, 457544600, 459451897, 459680944, 468058810, 468083581, 470964084, 471470955, 471567278, 472267822, 481177859, 481210627, 481435874, 481455115, 481485378, 481490218, 485105638, 486005878, 486383494, 487988916, 488103783, 490661867, 491574090, 491578272, 493041952, 493441205, 493582844, 493716979, 504577572, 504740359, 505091638, 505592418, 505656212, 509516275, 514998531, 515571132, 515594682, 518712698, 521362273, 526592419, 526807354, 527348842, 538294791, 539214049, 544689535, 545535009, 548544752, 548563346, 548595116, 551679010, 558034099, 560329411, 560356209, 560671018, 560671152, 560692590, 560845442, 569212097, 569474241, 572252718, 572768481, 575326764, 576174758, 576190819, 582099184, 582099438, 582372519, 582558889, 586552164, 591325418, 594231990, 594243961, 605711268, 615672071, 616086845, 621792370, 624879850, 627432831, 640040548, 654392808, 658675477, 659420283, 672891587, 694768102, 705890982, 725543146, 759097578, 761686526, 795383908, 843809551, 878105336, 908643300, 945213471 };
+static int32_t const ATTRIBUTE_HASHES_DATA[] = { 1153, 1383, 1601, 1793, 1827, 1857, 68600, 69146, 69177, 70237, 70270, 71572, 71669, 72415, 72444, 74846, 74904, 74943, 75001, 75276, 75590, 84742, 84839, 85575, 85963, 85992, 87204, 88074, 88171, 89130, 89163, 3207892, 3283895, 3284791, 3338752, 3358197, 3369562, 3539124, 3562402, 3574260, 3670335, 3696933, 3721879, 135280021, 135346322, 136317019, 136475749, 136548517, 136652214, 136884919, 136902418, 136942992, 137292068, 139120259, 139785574, 142250603, 142314056, 142331176, 142519584, 144752417, 145106895, 146147200, 146765926, 148805544, 149655723, 149809441, 150018784, 150445028, 150813181, 150923321, 152528754, 152536216, 152647366, 152962785, 155219321, 155654904, 157317483, 157350248, 157437941, 157447478, 157604838, 157685404, 157894402, 158315188, 166078431, 169409980, 169700259, 169856932, 170007032, 170409695, 170466488, 170513710, 170608367, 173028944, 173896963, 176090625, 176129212, 179390001, 179489057, 179627464, 179840468, 179849042, 180004216, 181779081, 183027151, 183645319, 183698797, 185922012, 185997252, 188312483, 188675799, 190977533, 190992569, 191006194, 191033518, 191038774, 191096249, 191166163, 191194426, 191443343, 191522106, 191568039, 200104642, 202506661, 202537381, 202602917, 203070590, 203120766, 203389054, 203690071, 203971238, 203986524, 209040857, 209125756, 212055489, 212322418, 212746849, 213002877, 213055164, 213088023, 213259873, 213273386, 213435118, 213437318, 213438231, 213493071, 213532268, 213542834, 213584431, 213659891, 215285828, 215880731, 216112976, 216684637, 217369699, 217565298, 217576549, 218186795, 219743185, 220082234, 221623802, 221986406, 222283890, 223089542, 223138630, 223311265, 224431494, 224547358, 224587256, 224589550, 224655650, 224785518, 224810917, 224813302, 225126263, 225429618, 225432950, 225440869, 236107233, 236709921, 236838947, 237117095, 237143271, 237172455, 237209953, 237354143, 237372743, 237668065, 237703073, 237714273, 239743521, 240512803, 240522627, 240560417, 240656513, 241015715, 241062755, 241065383, 243523041, 245865199, 246261793, 246556195, 246774817, 246923491, 246928419, 246981667, 247014847, 247058369, 247112833, 247118177, 247119137, 247128739, 247316903, 249533729, 250235623, 250269543, 251402351, 252339047, 253260911, 253293679, 254844367, 255547879, 256077281, 256345377, 258124199, 258354465, 258605063, 258744193, 258845603, 258856961, 258926689, 269869248, 270174334, 270709417, 270778994, 270781796, 271102503, 27147885