merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 19 Jan 2015 16:09:46 +0100
changeset 224489 f8e4fdb89a058fd12700e4f694d4397c5b3e855a
parent 224456 0e9496c4e398cd81f2e0ece755adeff281aa3c17 (current diff)
parent 224488 837613fc3a49ae3bca3e2651348e14c53803774c (diff)
child 224497 dcb4c5573aef75c9d07e5aff3610f08b9f74ac15
push id28131
push usercbook@mozilla.com
push dateMon, 19 Jan 2015 15:10:25 +0000
treeherdermozilla-central@f8e4fdb89a05 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone38.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
js/src/tests/ecma_7/SIMD/float32x4-minmax.js
js/src/tests/ecma_7/SIMD/float32x4clamp.js
netwerk/test/TestPageLoad.cpp
netwerk/test/TestPerf.cpp
netwerk/test/TestSyncHTTP.cpp
netwerk/test/TestThreadedIO.cpp
xpcom/tests/CvtURL.cpp
--- a/browser/base/content/test/general/mochitest.ini
+++ b/browser/base/content/test/general/mochitest.ini
@@ -24,16 +24,16 @@ support-files =
   offlineEvent.cacheManifest^headers^
   offlineEvent.html
   subtst_contextmenu.html
   video.ogg
 
 [test_bug364677.html]
 [test_bug395533.html]
 [test_contextmenu.html]
-skip-if = toolkit == "gtk2" || toolkit == "gtk3" # disabled on Linux due to bug 513558
+skip-if = toolkit == "gtk2" || toolkit == "gtk3" || (os == 'mac' && os_version != '10.6') # disabled on Linux due to bug 513558, on Mac after 10.6 due to bug 792304
 [test_contextmenu_input.html]
 skip-if = toolkit == "gtk2" || toolkit == "gtk3" # disabled on Linux due to bug 513558
 [test_feed_discovery.html]
 [test_offlineNotification.html]
 skip-if = buildapp == 'mulet' # Bug 1066070 - I don't think either popup notifications nor addon install stuff works?
 [test_offline_gzip.html]
 skip-if = buildapp == 'mulet' # Bug 1066070 - I don't think either popup notifications nor addon install stuff works?
--- a/browser/base/content/test/general/test_contextmenu.html
+++ b/browser/base/content/test/general/test_contextmenu.html
@@ -847,26 +847,20 @@ function waitForEvents(event)
     loaded = true;
   if (painted && loaded) {
     subwindow.removeEventListener("MozAfterPaint", waitForEvents, false);
     subwindow.onload = null;
     startTest();
   }
 }
 
-const isOSXMtnLion = navigator.userAgent.indexOf("Mac OS X 10.8") != -1;
+SpecialPowers.setBoolPref("plugins.click_to_play", true);
+setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY);
 
-if (isOSXMtnLion) {
-  todo(false, "Mountain Lion doesn't like this test (bug 792304)");
-} else {
-  SpecialPowers.setBoolPref("plugins.click_to_play", true);
-  setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY);
+var subwindow = window.open("./subtst_contextmenu.html", "contextmenu-subtext", "width=600,height=800");
+subwindow.addEventListener("MozAfterPaint", waitForEvents, false);
+subwindow.onload = waitForEvents;
 
-  var subwindow = window.open("./subtst_contextmenu.html", "contextmenu-subtext", "width=600,height=800");
-  subwindow.addEventListener("MozAfterPaint", waitForEvents, false);
-  subwindow.onload = waitForEvents;
-
-  SimpleTest.waitForExplicitFinish();
-}
+SimpleTest.waitForExplicitFinish();
 </script>
 </pre>
 </body>
 </html>
--- a/caps/tests/mochitest/test_bug995943.xul
+++ b/caps/tests/mochitest/test_bug995943.xul
@@ -21,18 +21,20 @@ https://bugzilla.mozilla.org/show_bug.cg
   const Cc = Components.classes;
   const Ci = Components.interfaces;
   Cu.import("resource://gre/modules/Services.jsm");
   function debug(msg) { info(msg); }
 
   /** Test for CAPS file:// URI prefs. **/
   SimpleTest.waitForExplicitFinish();
   SimpleTest.requestCompleteLog();
-  if (Services.appinfo.OS == "Darwin") // See bug 1067022
-    SimpleTest.expectAssertions(0, 1);
+  if (navigator.userAgent.indexOf("Mac OS X 10.10") != -1)
+    SimpleTest.expectAssertions(5); // See bug 1067022
+  else if (Services.appinfo.OS == "Darwin")
+    SimpleTest.expectAssertions(0, 1); // See bug 1067022
 
   var rootdir = Services.appinfo.OS == "WINNT" ? "file:///C:" : "file:///";
 
   function checkLoadFileURI(domain, shouldLoad) {
     debug("Invoking checkLoadFileURI with domain: " + domain + ", shouldLoad: " + shouldLoad);
     return new Promise(function(resolve, reject) {
       $('ifr').addEventListener('load', function l1() {
         debug("Invoked l1 for " + domain);
--- a/dom/base/ScriptSettings.h
+++ b/dom/base/ScriptSettings.h
@@ -314,18 +314,18 @@ private:
 
   AutoJSAPI(const AutoJSAPI&) = delete;
   AutoJSAPI& operator= (const AutoJSAPI&) = delete;
 };
 
 /*
  * A class that represents a new script entry point.
  */
-class AutoEntryScript : public AutoJSAPI,
-                        protected ScriptSettingsStackEntry {
+class MOZ_STACK_CLASS AutoEntryScript : public AutoJSAPI,
+                                        protected ScriptSettingsStackEntry {
 public:
   explicit AutoEntryScript(nsIGlobalObject* aGlobalObject,
                   bool aIsMainThread = NS_IsMainThread(),
                   // Note: aCx is mandatory off-main-thread.
                   JSContext* aCx = nullptr);
 
   ~AutoEntryScript();
 
@@ -336,20 +336,20 @@ public:
 private:
   // It's safe to make this a weak pointer, since it's the subject principal
   // when we go on the stack, so can't go away until after we're gone.  In
   // particular, this is only used from the CallSetup constructor, and only in
   // the aIsJSImplementedWebIDL case.  And in that case, the subject principal
   // is the principal of the callee function that is part of the CallArgs just a
   // bit up the stack, and which will outlive us.  So we know the principal
   // can't go away until then either.
-  nsIPrincipal* mWebIDLCallerPrincipal;
+  nsIPrincipal* MOZ_NON_OWNING_REF mWebIDLCallerPrincipal;
   friend nsIPrincipal* GetWebIDLCallerPrincipal();
 
-  nsIDocShell* mDocShellForJSRunToCompletion;
+  nsCOMPtr<nsIDocShell> mDocShellForJSRunToCompletion;
 
   bool mIsMainThread;
 };
 
 /*
  * A class that can be used to force a particular incumbent script on the stack.
  */
 class AutoIncumbentScript : protected ScriptSettingsStackEntry {
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -150,17 +150,19 @@ enum EventNameType {
   EventNameType_HTMLBodyOrFramesetOnly = 0x0020,
 
   EventNameType_HTMLXUL = 0x0003,
   EventNameType_All = 0xFFFF
 };
 
 struct EventNameMapping
 {
-  nsIAtom* mAtom;
+  // This holds pointers to nsGkAtoms members, and is therefore safe as a
+  // non-owning reference.
+  nsIAtom* MOZ_OWNING_REF mAtom;
   uint32_t mId;
   int32_t  mType;
   mozilla::EventClassID mEventClassID;
 };
 
 struct nsShortcutCandidate {
   nsShortcutCandidate(uint32_t aCharCode, bool aIgnoreShift) :
     mCharCode(aCharCode), mIgnoreShift(aIgnoreShift)
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -675,17 +675,17 @@ skip-if = buildapp == 'b2g' || toolkit =
 [test_domparser_null_char.html]
 [test_domparsing.html]
 [test_elementTraversal.html]
 [test_element_closest.html]
 [test_encodeToStringWithMaxLength.html]
 [test_fileapi.html]
 skip-if = e10s
 [test_fileapi_slice.html]
-disabled = Busted on B2G, Android, E10S and now Mulet. Bug 775227.
+skip-if = buildapp == 'b2g' || buildapp == 'mulet' || toolkit == 'android' || e10s #bug 775227
 [test_getElementById.html]
 [test_html_colors_quirks.html]
 [test_html_colors_standards.html]
 [test_html_in_xhr.html]
 [test_htmlcopyencoder.html]
 [test_htmlcopyencoder.xhtml]
 [test_ipc_messagemanager_blob.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 936226
--- a/dom/base/test/test_fileapi_slice.html
+++ b/dom/base/test/test_fileapi_slice.html
@@ -17,21 +17,16 @@ https://bugzilla.mozilla.org/show_bug.cg
   <canvas id=testcanvas hidden moz-opaque></canvas>
   <input id="fileList" type="file"></input>
 </p>
 <div id="content" style="display: none">
 </div>
 
 <pre id="test">
 <script class="testbody" type="text/javascript">
-const isOSXMtnLion = navigator.userAgent.indexOf("Mac OS X 10.8") != -1;
-
-if (isOSXMtnLion) {
-  todo(false, "Mountain Lion doesn't like this test (bug 788999)");
-} else {
 var fileNum = 1;
 SimpleTest.waitForExplicitFinish();
 
 // Create files containing data we'll test with. We'll want long
 // strings to ensure they span multiple buffers while loading
 
 // Create a decent-sized image
 cx = $("canvas").getContext('2d');
@@ -131,12 +126,11 @@ expectedTestCount++;
 
 // image past end
 var imgfile = createFileWithData(testBinaryData + fileData);
 is(imgfile.size, size + testBinaryData.length, "correct file size (past end)");
 var img = new Image;
 img.src = URL.createObjectURL(imgfile.slice(testBinaryData.length, testBinaryData.length + size + 1000));
 img.onload = imageLoadHandler;
 expectedTestCount++;
-}
 </script>
 </pre>
 </body> </html>
--- a/dom/bindings/DOMString.h
+++ b/dom/bindings/DOMString.h
@@ -170,17 +170,19 @@ public:
   }
 
 private:
   // We need to be able to act like a string as needed
   Maybe<nsAutoString> mString;
 
   // For callees that know we exist, we can be a stringbuffer/length/null-flag
   // triple.
-  nsStringBuffer* mStringBuffer;
+  nsStringBuffer* MOZ_UNSAFE_REF("The ways in which this can be safe are "
+                                 "documented above and enforced through "
+                                 "assertions") mStringBuffer;
   uint32_t mLength;
   bool mIsNull;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_DOMString_h
--- a/dom/events/EventListenerManager.h
+++ b/dom/events/EventListenerManager.h
@@ -553,17 +553,17 @@ protected:
   uint32_t mMayHaveScrollWheelEventListener : 1;
   uint32_t mMayHaveMouseEnterLeaveEventListener : 1;
   uint32_t mMayHavePointerEnterLeaveEventListener : 1;
   uint32_t mClearingListeners : 1;
   uint32_t mIsMainThreadELM : 1;
   uint32_t mNoListenerForEvent : 23;
 
   nsAutoTObserverArray<Listener, 2> mListeners;
-  dom::EventTarget* mTarget;  // WEAK
+  dom::EventTarget* MOZ_NON_OWNING_REF mTarget;
   nsCOMPtr<nsIAtom> mNoListenerForEventAtom;
 
   friend class ELMCreationDetector;
   static uint32_t sMainThreadCreatedCount;
 };
 
 } // namespace mozilla
 
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -13,16 +13,17 @@
 #include <stdint.h>
 
 #include "MediaDecoderStateMachine.h"
 #include "MediaDecoderStateMachineScheduler.h"
 #include "AudioSink.h"
 #include "nsTArray.h"
 #include "MediaDecoder.h"
 #include "MediaDecoderReader.h"
+#include "mozilla/MathAlgorithms.h"
 #include "mozilla/mozalloc.h"
 #include "VideoUtils.h"
 #include "mozilla/dom/TimeRanges.h"
 #include "nsDeque.h"
 #include "AudioSegment.h"
 #include "VideoSegment.h"
 #include "ImageContainer.h"
 #include "nsComponentManagerUtils.h"
@@ -160,17 +161,17 @@ static const uint32_t QUICK_BUFFERING_LO
 // QUICK_BUFFERING_LOW_DATA_USECS.
 static_assert(QUICK_BUFFERING_LOW_DATA_USECS <= AMPLE_AUDIO_USECS,
               "QUICK_BUFFERING_LOW_DATA_USECS is too large");
 
 // The amount of instability we tollerate in calls to
 // MediaDecoderStateMachine::UpdateEstimatedDuration(); changes of duration
 // less than this are ignored, as they're assumed to be the result of
 // instability in the duration estimation.
-static const int64_t ESTIMATED_DURATION_FUZZ_FACTOR_USECS = USECS_PER_S / 2;
+static const uint64_t ESTIMATED_DURATION_FUZZ_FACTOR_USECS = USECS_PER_S / 2;
 
 static TimeDuration UsecsToDuration(int64_t aUsecs) {
   return TimeDuration::FromMicroseconds(aUsecs);
 }
 
 static int64_t DurationToUsecs(TimeDuration aDuration) {
   return static_cast<int64_t>(aDuration.ToSeconds() * USECS_PER_S);
 }
@@ -1444,17 +1445,17 @@ void MediaDecoderStateMachine::SetDurati
   mEndTime = mStartTime + aDuration;
 }
 
 void MediaDecoderStateMachine::UpdateEstimatedDuration(int64_t aDuration)
 {
   AssertCurrentThreadInMonitor();
   int64_t duration = GetDuration();
   if (aDuration != duration &&
-      std::abs(aDuration - duration) > ESTIMATED_DURATION_FUZZ_FACTOR_USECS) {
+      mozilla::Abs(aDuration - duration) > ESTIMATED_DURATION_FUZZ_FACTOR_USECS) {
     SetDuration(aDuration);
     nsCOMPtr<nsIRunnable> event =
       NS_NewRunnableMethod(mDecoder, &MediaDecoder::DurationChanged);
     NS_DispatchToMainThread(event);
   }
 }
 
 void MediaDecoderStateMachine::SetMediaEndTime(int64_t aEndTime)
--- a/dom/system/gonk/GonkGPSGeolocationProvider.cpp
+++ b/dom/system/gonk/GonkGPSGeolocationProvider.cpp
@@ -54,16 +54,20 @@
 using namespace mozilla;
 using namespace mozilla::dom;
 
 static const int kDefaultPeriod = 1000; // ms
 static bool gDebug_isLoggingEnabled = false;
 static bool gDebug_isGPSLocationIgnored = false;
 static const char* kNetworkConnStateChangedTopic = "network-connection-state-changed";
 static const char* kMozSettingsChangedTopic = "mozsettings-changed";
+#ifdef MOZ_B2G_RIL
+static const char* kPrefRilNumRadioInterfaces = "ril.numRadioInterfaces";
+static const char* kSettingRilDefaultServiceId = "ril.data.defaultServiceId";
+#endif
 // Both of these settings can be toggled in the Gaia Developer settings screen.
 static const char* kSettingDebugEnabled = "geolocation.debugging.enabled";
 static const char* kSettingDebugGpsIgnored = "geolocation.debugging.gps-locations-ignored";
 
 // While most methods of GonkGPSGeolocationProvider should only be
 // called from main thread, we deliberately put the Init and ShutdownGPS
 // methods off main thread to avoid blocking.
 NS_IMPL_ISUPPORTS(GonkGPSGeolocationProvider,
@@ -284,16 +288,20 @@ GonkGPSGeolocationProvider::AGPSRILRefLo
 #endif // MOZ_B2G_RIL
 
 GonkGPSGeolocationProvider::GonkGPSGeolocationProvider()
   : mStarted(false)
   , mSupportsScheduling(false)
 #ifdef MOZ_B2G_RIL
   , mSupportsMSB(false)
   , mSupportsMSA(false)
+  , mRilDataServiceId(0)
+  , mNumberOfRilServices(1)
+  , mObservingNetworkConnStateChange(false)
+  , mObservingSettingsChange(false)
 #endif
   , mSupportsSingleShot(false)
   , mSupportsTimeInjection(false)
   , mGpsInterface(nullptr)
 {
 }
 
 GonkGPSGeolocationProvider::~GonkGPSGeolocationProvider()
@@ -709,27 +717,34 @@ GonkGPSGeolocationProvider::SetupAGPS()
   int32_t suplPort = Preferences::GetInt("geo.gps.supl_port", -1);
   if (!suplServer.IsEmpty() && suplPort > 0) {
     mAGpsInterface->set_server(AGPS_TYPE_SUPL, suplServer.get(), suplPort);
   } else {
     NS_WARNING("Cannot get SUPL server settings");
     return;
   }
 
-  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
-  if (obs) {
-    obs->AddObserver(this, kNetworkConnStateChangedTopic, false);
-  }
+  // Request RIL date service ID for correct RadioInterface object first due to
+  // multi-SIM case needs it to handle AGPS related stuffs. For single SIM, 0
+  // will be returned as default RIL data service ID.
+  RequestSettingValue(kSettingRilDefaultServiceId);
+}
 
+void
+GonkGPSGeolocationProvider::UpdateRadioInterface()
+{
   nsCOMPtr<nsIRadioInterfaceLayer> ril = do_GetService("@mozilla.org/ril;1");
-  if (ril) {
-    // TODO: Bug 878748 - B2G GPS: acquire correct RadioInterface instance in
-    // MultiSIM configuration
-    ril->GetRadioInterface(0 /* clientId */, getter_AddRefs(mRadioInterface));
-  }
+  NS_ENSURE_TRUE_VOID(ril);
+  ril->GetRadioInterface(mRilDataServiceId, getter_AddRefs(mRadioInterface));
+}
+
+bool
+GonkGPSGeolocationProvider::IsValidRilServiceId(uint32_t aServiceId)
+{
+  return aServiceId < mNumberOfRilServices;
 }
 #endif // MOZ_B2G_RIL
 
 
 NS_IMPL_ISUPPORTS(GonkGPSGeolocationProvider::NetworkLocationUpdate,
                   nsIGeolocationUpdate)
 
 NS_IMETHODIMP
@@ -843,19 +858,22 @@ GonkGPSGeolocationProvider::Startup()
   }
 
   RequestSettingValue(kSettingDebugEnabled);
   RequestSettingValue(kSettingDebugGpsIgnored);
 
   // Setup an observer to watch changes to the setting.
   nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
   if (observerService) {
+    MOZ_ASSERT(!mObservingSettingsChange);
     nsresult rv = observerService->AddObserver(this, kMozSettingsChangedTopic, false);
     if (NS_FAILED(rv)) {
       NS_WARNING("geo: Gonk GPS AddObserver failed");
+    } else {
+      mObservingSettingsChange = true;
     }
   }
 
   if (!mInitThread) {
     nsresult rv = NS_NewThread(getter_AddRefs(mInitThread));
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
@@ -867,16 +885,19 @@ GonkGPSGeolocationProvider::Startup()
     nsresult rv = mNetworkLocationProvider->Startup();
     if (NS_SUCCEEDED(rv)) {
       nsRefPtr<NetworkLocationUpdate> update = new NetworkLocationUpdate();
       mNetworkLocationProvider->Watch(update);
     }
   }
 
   mStarted = true;
+#ifdef MOZ_B2G_RIL
+  mNumberOfRilServices = Preferences::GetUint(kPrefRilNumRadioInterfaces, 1);
+#endif
   return NS_OK;
 }
 
 NS_IMETHODIMP
 GonkGPSGeolocationProvider::Watch(nsIGeolocationUpdate* aCallback)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
@@ -901,21 +922,25 @@ GonkGPSGeolocationProvider::Shutdown()
 
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   if (obs) {
     nsresult rv;
 #ifdef MOZ_B2G_RIL
     rv = obs->RemoveObserver(this, kNetworkConnStateChangedTopic);
     if (NS_FAILED(rv)) {
       NS_WARNING("geo: Gonk GPS network state RemoveObserver failed");
+    } else {
+      mObservingNetworkConnStateChange = false;
     }
 #endif
     rv = obs->RemoveObserver(this, kMozSettingsChangedTopic);
     if (NS_FAILED(rv)) {
       NS_WARNING("geo: Gonk GPS mozsettings RemoveObserver failed");
+    } else {
+      mObservingSettingsChange = false;
     }
   }
 
   mInitThread->Dispatch(NS_NewRunnableMethod(this, &GonkGPSGeolocationProvider::ShutdownGPS),
                         NS_DISPATCH_NORMAL);
 
   return NS_OK;
 }
@@ -1038,16 +1063,25 @@ GonkGPSGeolocationProvider::Observe(nsIS
                                             gDebug_isGPSLocationIgnored);
       }
       return NS_OK;
     } else if (setting.mKey.EqualsASCII(kSettingDebugEnabled)) {
       nsContentUtils::LogMessageToConsole("geo: received mozsettings-changed: logging\n");
       gDebug_isLoggingEnabled =
         setting.mValue.isBoolean() ? setting.mValue.toBoolean() : false;
       return NS_OK;
+    } else if (setting.mKey.EqualsASCII(kSettingRilDefaultServiceId)) {
+      if (!setting.mValue.isNumber() ||
+          !IsValidRilServiceId(setting.mValue.toNumber())) {
+        return NS_ERROR_UNEXPECTED;
+      }
+
+      mRilDataServiceId = setting.mValue.toNumber();
+      UpdateRadioInterface();
+      return NS_OK;
     }
   }
 
   return NS_OK;
 }
 
 /** nsISettingsServiceCallback **/
 
@@ -1066,16 +1100,42 @@ GonkGPSGeolocationProvider::Handle(const
       nsAutoJSString apn;
       if (!apn.init(cx, aResult.toString())) {
         return NS_ERROR_FAILURE;
       }
       if (!apn.IsEmpty()) {
         SetAGpsDataConn(apn);
       }
     }
+  } else if (aName.EqualsASCII(kSettingRilDefaultServiceId)) {
+    uint32_t id = 0;
+    JSContext *cx = nsContentUtils::GetCurrentJSContext();
+    NS_ENSURE_TRUE(cx, NS_OK);
+    if (!JS::ToUint32(cx, aResult, &id)) {
+      return NS_ERROR_FAILURE;
+    }
+
+    if (!IsValidRilServiceId(id)) {
+      return NS_ERROR_UNEXPECTED;
+    }
+
+    mRilDataServiceId = id;
+    UpdateRadioInterface();
+
+    MOZ_ASSERT(!mObservingNetworkConnStateChange);
+
+    // Now we know which service ID to deal with, observe necessary topic then
+    nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+    NS_ENSURE_TRUE(obs, NS_OK);
+
+    if (NS_FAILED(obs->AddObserver(this, kNetworkConnStateChangedTopic, false))) {
+      NS_WARNING("Failed to add network state changed observer!");
+    } else {
+      mObservingNetworkConnStateChange = true;
+    }
   }
 #endif // MOZ_B2G_RIL
   return NS_OK;
 }
 
 NS_IMETHODIMP
 GonkGPSGeolocationProvider::HandleError(const nsAString& aErrorMessage)
 {
--- a/dom/system/gonk/GonkGPSGeolocationProvider.h
+++ b/dom/system/gonk/GonkGPSGeolocationProvider.h
@@ -77,16 +77,18 @@ private:
 #endif
 
   void Init();
   void StartGPS();
   void ShutdownGPS();
   void InjectLocation(double latitude, double longitude, float accuracy);
   void RequestSettingValue(const char* aKey);
 #ifdef MOZ_B2G_RIL
+  void UpdateRadioInterface();
+  bool IsValidRilServiceId(uint32_t aServiceId);
   void SetupAGPS();
   int32_t GetDataConnectionState();
   void SetAGpsDataConn(nsAString& aApn);
   void RequestDataConnection();
   void ReleaseDataConnection();
   void RequestSetID(uint32_t flags);
   void SetReferenceLocation();
 #endif
@@ -96,16 +98,23 @@ private:
   static GonkGPSGeolocationProvider* sSingleton;
 
   bool mStarted;
 
   bool mSupportsScheduling;
 #ifdef MOZ_B2G_RIL
   bool mSupportsMSB;
   bool mSupportsMSA;
+  uint32_t mRilDataServiceId;
+  // mNumberOfRilServices indicates how many SIM slots supported on device, and
+  // RadioInterfaceLayer.js takes responsibility to set up the corresponding
+  // preference value.
+  uint32_t mNumberOfRilServices;
+  bool mObservingNetworkConnStateChange;
+  bool mObservingSettingsChange;
 #endif
   bool mSupportsSingleShot;
   bool mSupportsTimeInjection;
 
   const GpsInterface* mGpsInterface;
 #ifdef MOZ_B2G_RIL
   const AGpsInterface* mAGpsInterface;
   const AGpsRilInterface* mAGpsRilInterface;
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -2032,16 +2032,22 @@ public:
     // Assumes that WorkerJSRuntimeStats will hold a reference to |path|, and
     // not a copy, as TryToMapAddon() may later modify if.
     nsCString path;
     WorkerJSRuntimeStats rtStats(path);
 
     {
       MutexAutoLock lock(mMutex);
 
+      if (!mWorkerPrivate ||
+          !mWorkerPrivate->BlockAndCollectRuntimeStats(&rtStats, aAnonymize)) {
+        // Returning NS_OK here will effectively report 0 memory.
+        return NS_OK;
+      }
+
       path.AppendLiteral("explicit/workers/workers(");
       if (aAnonymize && !mWorkerPrivate->Domain().IsEmpty()) {
         path.AppendLiteral("<anonymized-domain>)/worker(<anonymized-url>");
       } else {
         nsCString escapedDomain(mWorkerPrivate->Domain());
         if (escapedDomain.IsEmpty()) {
           escapedDomain += "chrome";
         } else {
@@ -2051,22 +2057,16 @@ public:
         path.AppendLiteral(")/worker(");
         NS_ConvertUTF16toUTF8 escapedURL(mWorkerPrivate->ScriptURL());
         escapedURL.ReplaceChar('/', '\\');
         path.Append(escapedURL);
       }
       path.AppendPrintf(", 0x%p)/", static_cast<void*>(mWorkerPrivate));
 
       TryToMapAddon(path);
-
-      if (!mWorkerPrivate ||
-          !mWorkerPrivate->BlockAndCollectRuntimeStats(&rtStats, aAnonymize)) {
-        // Returning NS_OK here will effectively report 0 memory.
-        return NS_OK;
-      }
     }
 
     return xpc::ReportJSRuntimeExplicitTreeStats(rtStats, path,
                                                  aCallback, aClosure,
                                                  aAnonymize);
   }
 
 private:
--- a/editor/libeditor/nsHTMLURIRefObject.cpp
+++ b/editor/libeditor/nsHTMLURIRefObject.cpp
@@ -34,42 +34,16 @@
     applet: codebase, archive <list>
     object: codebase, data, classid, usemap
     head:   profile
     del:    cite
     ins:    cite
     q:      cite
  */
 
-/* Here is how to open a channel for testing
-   (from embed/qa/testembed/Tests.cpp):
-
-  nsCOMPtr<nsIChannel> theChannel;
-  nsCString uri;
-  nsCOMPtr<nsIURI> theURI;
-  rv = NS_NewURI(getter_AddRefs(theURI), theSpec);
-  if (!theURI)
-    error;
-  rv = NS_OpenURI(getter_AddRefs(theChannel), theURI, nullptr, theLoadGroup);
-  if (!theChannel)
-    error;
-  nsCOMPtr<nsILoadGroup> theLoadGroup(do_CreateInstance(NS_LOADGROUP_CONTRACTID));
-  if (!theLoadGroup)
-    error;
-		nsCOMPtr<nsIStreamListener> listener(static_cast<nsIStreamListener*>(qaBrowserImpl));
-		//nsCOMPtr<nsIWeakReference> thisListener(do_GetWeakReference(listener));
-		//qaWebBrowser->AddWebBrowserListener(thisListener, NS_GET_IID(nsIStreamListener));
-
-		// this calls nsIStreamListener::OnDataAvailable()
-		rv = theChannel->AsyncOpen(listener, nullptr);
-
-		nsCOMPtr<nsIRequest> theRequest = do_QueryInterface(theChannel);
-    // Now we can do things on nsIRequest (like what?)
- */
-
 #include "nsHTMLURIRefObject.h"
 
 #include "mozilla/mozalloc.h"
 #include "nsAString.h"
 #include "nsDebug.h"
 #include "nsError.h"
 #include "nsID.h"
 #include "nsIDOMAttr.h"
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -229,16 +229,17 @@ private:
   DECL_GFX_PREF(Once, "gfx.touch.resample.vsync-adjust",       TouchVsyncSampleAdjust, int32_t, 5);
   DECL_GFX_PREF(Once, "gfx.touch.resample.delay-threshold",    TouchResampleVsyncDelayThreshold, int32_t, 20);
   DECL_GFX_PREF(Once, "gfx.touch.resample.old-touch-threshold",TouchResampleOldTouchThreshold, int32_t, 17);
 
   DECL_GFX_PREF(Live, "gl.msaa-level",                         MSAALevel, uint32_t, 2);
 
   DECL_GFX_PREF(Once, "image.cache.timeweight",                ImageCacheTimeWeight, int32_t, 500);
   DECL_GFX_PREF(Once, "image.cache.size",                      ImageCacheSize, int32_t, 5*1024*1024);
+  DECL_GFX_PREF(Live, "image.downscale-during-decode.enabled", ImageDownscaleDuringDecodeEnabled, bool, false);
   DECL_GFX_PREF(Live, "image.high_quality_downscaling.enabled", ImageHQDownscalingEnabled, bool, false);
   DECL_GFX_PREF(Live, "image.high_quality_downscaling.min_factor", ImageHQDownscalingMinFactor, uint32_t, 1000);
   DECL_GFX_PREF(Live, "image.high_quality_upscaling.max_size", ImageHQUpscalingMaxSize, uint32_t, 20971520);
   DECL_GFX_PREF(Once, "image.mem.decode_bytes_at_a_time",      ImageMemDecodeBytesAtATime, uint32_t, 200000);
   DECL_GFX_PREF(Live, "image.mem.decodeondraw",                ImageMemDecodeOnDraw, bool, false);
   DECL_GFX_PREF(Live, "image.mem.discardable",                 ImageMemDiscardable, bool, false);
   DECL_GFX_PREF(Once, "image.mem.surfacecache.discard_factor", ImageMemSurfaceCacheDiscardFactor, uint32_t, 1);
   DECL_GFX_PREF(Once, "image.mem.surfacecache.max_size_kb",    ImageMemSurfaceCacheMaxSizeKB, uint32_t, 100 * 1024);
--- a/image/decoders/nsICODecoder.cpp
+++ b/image/decoders/nsICODecoder.cpp
@@ -633,29 +633,29 @@ nsICODecoder::NeedsNewFrame() const
   if (mContainedDecoder) {
     return mContainedDecoder->NeedsNewFrame();
   }
 
   return Decoder::NeedsNewFrame();
 }
 
 nsresult
-nsICODecoder::AllocateFrame()
+nsICODecoder::AllocateFrame(const nsIntSize& aTargetSize /* = nsIntSize() */)
 {
   nsresult rv;
 
   if (mContainedDecoder) {
-    rv = mContainedDecoder->AllocateFrame();
+    rv = mContainedDecoder->AllocateFrame(aTargetSize);
     mCurrentFrame = mContainedDecoder->GetCurrentFrameRef();
     mProgress |= mContainedDecoder->TakeProgress();
     mInvalidRect.Union(mContainedDecoder->TakeInvalidRect());
     return rv;
   }
 
   // Grab a strong ref that we'll later hand over to the contained decoder. This
   // lets us avoid creating a RawAccessFrameRef off-main-thread.
-  rv = Decoder::AllocateFrame();
+  rv = Decoder::AllocateFrame(aTargetSize);
   mRefForContainedDecoder = GetCurrentFrameRef();
   return rv;
 }
 
 } // namespace image
 } // namespace mozilla
--- a/image/decoders/nsICODecoder.h
+++ b/image/decoders/nsICODecoder.h
@@ -35,17 +35,18 @@ public:
   // Obtains the height of the icon directory entry
   uint32_t GetRealHeight() const
   {
     return mDirEntry.mHeight == 0 ? 256 : mDirEntry.mHeight;
   }
 
   virtual void WriteInternal(const char* aBuffer, uint32_t aCount) MOZ_OVERRIDE;
   virtual void FinishInternal() MOZ_OVERRIDE;
-  virtual nsresult AllocateFrame() MOZ_OVERRIDE;
+  virtual nsresult AllocateFrame(const nsIntSize& aTargetSize
+                                   /* = nsIntSize() */) MOZ_OVERRIDE;
 
 protected:
   virtual bool NeedsNewFrame() const MOZ_OVERRIDE;
 
 private:
   // Writes to the contained decoder and sets the appropriate errors
   // Returns true if there are no errors.
   bool WriteToContainedDecoder(const char* aBuffer, uint32_t aCount);
--- a/image/public/imgIContainer.idl
+++ b/image/public/imgIContainer.idl
@@ -64,17 +64,17 @@ native nsIntSizeByVal(nsIntSize);
 
 /**
  * imgIContainer is the interface that represents an image. It allows
  * access to frames as Thebes surfaces. It also allows drawing of images
  * onto Thebes contexts.
  *
  * Internally, imgIContainer also manages animation of images.
  */
-[scriptable, builtinclass, uuid(14ea6fa5-183e-4409-ac88-110bd2e05292)]
+[scriptable, builtinclass, uuid(4adb4c92-284d-4f5a-85e7-e924ec57510f)]
 interface imgIContainer : nsISupports
 {
   /**
    * The width of the container rectangle.  In the case of any error,
    * zero is returned, and an exception will be thrown.
    */
   readonly attribute int32_t width;
 
@@ -340,22 +340,38 @@ interface imgIContainer : nsISupports
    * the image will at some point fire off decode notifications. Calling draw()
    * or getFrame() triggers the same mechanism internally. Thus, if you want to
    * be sure that the image will be decoded but don't want to access it until
    * then, you must call requestDecode().
    */
   void requestDecode();
 
   /*
-   * This is equivalent to requestDecode() but it also decodes some of the
-   * image.
+   * This is equivalent to requestDecode() but it also synchronously decodes
+   * images that can be decoded "quickly" according to some heuristic.
    */
   [noscript] void startDecoding();
 
   /*
+   * This method is equivalent to requestDecode(), but enables the caller to
+   * provide more detailed information about the decode request.
+   *
+   * @param aSize The size to which the image should be scaled while decoding,
+   *              if possible. If the image cannot be scaled to this size while
+   *              being decoded, it will be decoded at its intrinsic size.
+   * @param aFlags Flags of the FLAG_* variety. Only the decode flags
+   *               (FLAG_DECODE_*) and FLAG_SYNC_DECODE (which will
+   *               synchronously decode images that can be decoded "quickly",
+   *               just like startDecoding() does) are accepted; others will be
+   *               ignored.
+   */
+  [noscript] void requestDecodeForSize([const] in nsIntSize aSize,
+                                       in uint32_t aFlags);
+
+  /*
    * Returns true if no more decoding can be performed on this image. Images
    * with errors return true since they cannot be decoded any further. Note that
    * because decoded images may be discarded, isDecoded() may return false even
    * if it has returned true in the past.
    */
   [noscript, notxpcom, nostdcall] bool isDecoded();
 
   /**
--- a/image/src/Decoder.cpp
+++ b/image/src/Decoder.cpp
@@ -323,21 +323,28 @@ Decoder::FinishSharedDecoder()
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!HasError()) {
     FinishInternal();
   }
 }
 
 nsresult
-Decoder::AllocateFrame()
+Decoder::AllocateFrame(const nsIntSize& aTargetSize /* = nsIntSize() */)
 {
   MOZ_ASSERT(mNeedsNewFrame);
 
+  nsIntSize targetSize = aTargetSize;
+  if (targetSize == nsIntSize()) {
+    MOZ_ASSERT(HasSize());
+    targetSize = mImageMetadata.GetSize();
+  }
+
   mCurrentFrame = EnsureFrame(mNewFrameData.mFrameNum,
+                              targetSize,
                               mNewFrameData.mFrameRect,
                               mDecodeFlags,
                               mNewFrameData.mFormat,
                               mNewFrameData.mPaletteDepth,
                               mCurrentFrame.get());
 
   if (mCurrentFrame) {
     // Gather the raw pointers the decoders will use.
@@ -361,16 +368,17 @@ Decoder::AllocateFrame()
     mNeedsToFlushData = true;
   }
 
   return mCurrentFrame ? NS_OK : NS_ERROR_FAILURE;
 }
 
 RawAccessFrameRef
 Decoder::EnsureFrame(uint32_t aFrameNum,
+                     const nsIntSize& aTargetSize,
                      const nsIntRect& aFrameRect,
                      uint32_t aDecodeFlags,
                      SurfaceFormat aFormat,
                      uint8_t aPaletteDepth,
                      imgFrame* aPreviousFrame)
 {
   if (mDataError || NS_FAILED(mFailCode)) {
     return RawAccessFrameRef();
@@ -378,18 +386,18 @@ Decoder::EnsureFrame(uint32_t aFrameNum,
 
   MOZ_ASSERT(aFrameNum <= mFrameCount, "Invalid frame index!");
   if (aFrameNum > mFrameCount) {
     return RawAccessFrameRef();
   }
 
   // Adding a frame that doesn't already exist. This is the normal case.
   if (aFrameNum == mFrameCount) {
-    return InternalAddFrame(aFrameNum, aFrameRect, aDecodeFlags, aFormat,
-                            aPaletteDepth, aPreviousFrame);
+    return InternalAddFrame(aFrameNum, aTargetSize, aFrameRect, aDecodeFlags,
+                            aFormat, aPaletteDepth, aPreviousFrame);
   }
 
   // We're replacing a frame. It must be the first frame; there's no reason to
   // ever replace any other frame, since the first frame is the only one we
   // speculatively allocate without knowing what the decoder really needs.
   // XXX(seth): I'm not convinced there's any reason to support this at all. We
   // should figure out how to avoid triggering this and rip it out.
   MOZ_ASSERT(aFrameNum == 0, "Replacing a frame other than the first?");
@@ -410,70 +418,69 @@ Decoder::EnsureFrame(uint32_t aFrameNum,
 
   MOZ_ASSERT(ref, "No ref to current frame?");
 
   // Reinitialize the old frame.
   nsIntSize oldSize = ThebesIntSize(aPreviousFrame->GetImageSize());
   bool nonPremult =
     aDecodeFlags & imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA;
   if (NS_FAILED(aPreviousFrame->ReinitForDecoder(oldSize, aFrameRect, aFormat,
-                                               aPaletteDepth, nonPremult))) {
+                                                 aPaletteDepth, nonPremult))) {
     NS_WARNING("imgFrame::ReinitForDecoder should succeed");
     mFrameCount = 0;
     aPreviousFrame->Abort();
     return RawAccessFrameRef();
   }
 
   return ref;
 }
 
 RawAccessFrameRef
 Decoder::InternalAddFrame(uint32_t aFrameNum,
+                          const nsIntSize& aTargetSize,
                           const nsIntRect& aFrameRect,
                           uint32_t aDecodeFlags,
                           SurfaceFormat aFormat,
                           uint8_t aPaletteDepth,
                           imgFrame* aPreviousFrame)
 {
   MOZ_ASSERT(aFrameNum <= mFrameCount, "Invalid frame index!");
   if (aFrameNum > mFrameCount) {
     return RawAccessFrameRef();
   }
 
-  MOZ_ASSERT(mImageMetadata.HasSize());
-  nsIntSize imageSize(mImageMetadata.GetWidth(), mImageMetadata.GetHeight());
-  if (imageSize.width <= 0 || imageSize.height <= 0 ||
+  if (aTargetSize.width <= 0 || aTargetSize.height <= 0 ||
       aFrameRect.width <= 0 || aFrameRect.height <= 0) {
     NS_WARNING("Trying to add frame with zero or negative size");
     return RawAccessFrameRef();
   }
 
-  if (!SurfaceCache::CanHold(imageSize.ToIntSize())) {
+  if (!SurfaceCache::CanHold(aTargetSize.ToIntSize())) {
     NS_WARNING("Trying to add frame that's too large for the SurfaceCache");
     return RawAccessFrameRef();
   }
 
   nsRefPtr<imgFrame> frame = new imgFrame();
   bool nonPremult =
     aDecodeFlags & imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA;
-  if (NS_FAILED(frame->InitForDecoder(imageSize, aFrameRect, aFormat,
+  if (NS_FAILED(frame->InitForDecoder(aTargetSize, aFrameRect, aFormat,
                                       aPaletteDepth, nonPremult))) {
     NS_WARNING("imgFrame::Init should succeed");
     return RawAccessFrameRef();
   }
 
   RawAccessFrameRef ref = frame->RawAccessRef();
   if (!ref) {
     frame->Abort();
     return RawAccessFrameRef();
   }
 
   InsertOutcome outcome =
     SurfaceCache::Insert(frame, ImageKey(mImage.get()),
-                         RasterSurfaceKey(imageSize.ToIntSize(),
+                         RasterSurfaceKey(aTargetSize.ToIntSize(),
                                           aDecodeFlags,
                                           aFrameNum),
                          Lifetime::Persistent);
   if (outcome != InsertOutcome::SUCCESS) {
     // We either hit InsertOutcome::FAILURE, which is a temporary failure due to
     // low memory (we know it's not permanent because we checked CanHold()
     // above), or InsertOutcome::FAILURE_ALREADY_PRESENT, which means that
     // another decoder beat us to decoding this frame. Either way, we should
@@ -594,32 +601,35 @@ Decoder::PostFrameStop(Opacity aFrameOpa
 
   mCurrentFrame->Finish(aFrameOpacity, aDisposalMethod, aTimeout, aBlendMethod);
 
   mProgress |= FLAG_FRAME_COMPLETE | FLAG_ONLOAD_UNBLOCKED;
 
   // If we're not sending partial invalidations, then we send an invalidation
   // here when the first frame is complete.
   if (!mSendPartialInvalidations && !mIsAnimated) {
-    mInvalidRect.UnionRect(mInvalidRect, mCurrentFrame->GetRect());
+    mInvalidRect.UnionRect(mInvalidRect,
+                           nsIntRect(nsIntPoint(0, 0), GetSize()));
   }
 }
 
 void
-Decoder::PostInvalidation(nsIntRect& aRect)
+Decoder::PostInvalidation(const nsIntRect& aRect,
+                          const Maybe<nsIntRect>& aRectAtTargetSize
+                            /* = Nothing() */)
 {
   // We should be mid-frame
   NS_ABORT_IF_FALSE(mInFrame, "Can't invalidate when not mid-frame!");
   NS_ABORT_IF_FALSE(mCurrentFrame, "Can't invalidate when not mid-frame!");
 
   // Record this invalidation, unless we're not sending partial invalidations
   // or we're past the first frame.
   if (mSendPartialInvalidations && !mIsAnimated) {
     mInvalidRect.UnionRect(mInvalidRect, aRect);
-    mCurrentFrame->ImageUpdated(aRect);
+    mCurrentFrame->ImageUpdated(aRectAtTargetSize.valueOr(aRect));
   }
 }
 
 void
 Decoder::PostDecodeDone(int32_t aLoopCount /* = 0 */)
 {
   NS_ABORT_IF_FALSE(!IsSizeDecode(), "Can't be done with decoding with size decode!");
   NS_ABORT_IF_FALSE(!mInFrame, "Can't be done decoding if we're mid-frame!");
--- a/image/src/Decoder.h
+++ b/image/src/Decoder.h
@@ -123,16 +123,34 @@ public:
   bool IsSizeDecode() { return mSizeDecode; }
   void SetSizeDecode(bool aSizeDecode)
   {
     MOZ_ASSERT(!mInitialized, "Shouldn't be initialized yet");
     mSizeDecode = aSizeDecode;
   }
 
   /**
+   * If this decoder supports downscale-during-decode, sets the target size that
+   * this image should be decoded to.
+   *
+   * If this decoder *doesn't* support downscale-during-decode, returns
+   * NS_ERROR_NOT_AVAILABLE. If the provided size is unacceptable, returns
+   * another error.
+   *
+   * Returning NS_OK from this method is a promise that the decoder will decode
+   * the image to the requested target size unless it encounters an error.
+   *
+   * This must be called before Init() is called.
+   */
+  virtual nsresult SetTargetSize(const nsIntSize& aSize)
+  {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  /**
    * Set whether should send partial invalidations.
    *
    * If @aSend is true, we'll send partial invalidations when decoding the first
    * frame of the image, so image notifications observers will be able to
    * gradually draw in the image as it downloads.
    *
    * If @aSend is false (the default), we'll only send an invalidation when we
    * complete the first frame.
@@ -228,16 +246,22 @@ public:
   bool HasSize() const { return mImageMetadata.HasSize(); }
   void SetSizeOnImage();
 
   void SetSize(const nsIntSize& aSize, const Orientation& aOrientation)
   {
     PostSize(aSize.width, aSize.height, aOrientation);
   }
 
+  nsIntSize GetSize() const
+  {
+    MOZ_ASSERT(HasSize());
+    return mImageMetadata.GetSize();
+  }
+
   // Use HistogramCount as an invalid Histogram ID
   virtual Telemetry::ID SpeedHistogram() { return Telemetry::HistogramCount; }
 
   ImageMetadata& GetImageMetadata() { return mImageMetadata; }
 
   /**
    * Returns a weak pointer to the image associated with this decoder.
    */
@@ -254,17 +278,17 @@ public:
                     uint32_t width, uint32_t height,
                     gfx::SurfaceFormat format,
                     uint8_t palette_depth = 0);
   virtual bool NeedsNewFrame() const { return mNeedsNewFrame; }
 
 
   // Try to allocate a frame as described in mNewFrameData and return the
   // status code from that attempt. Clears mNewFrameData.
-  virtual nsresult AllocateFrame();
+  virtual nsresult AllocateFrame(const nsIntSize& aTargetSize = nsIntSize());
 
   already_AddRefed<imgFrame> GetCurrentFrame()
   {
     nsRefPtr<imgFrame> frame = mCurrentFrame.get();
     return frame.forget();
   }
 
   RawAccessFrameRef GetCurrentFrameRef()
@@ -327,19 +351,29 @@ protected:
   // Specify whether this frame is opaque as an optimization.
   // For animated images, specify the disposal, blend method and timeout for
   // this frame.
   void PostFrameStop(Opacity aFrameOpacity = Opacity::SOME_TRANSPARENCY,
                      DisposalMethod aDisposalMethod = DisposalMethod::KEEP,
                      int32_t aTimeout = 0,
                      BlendMethod aBlendMethod = BlendMethod::OVER);
 
-  // Called by the decoders when they have a region to invalidate. We may not
-  // actually pass these invalidations on right away.
-  void PostInvalidation(nsIntRect& aRect);
+  /**
+   * Called by the decoders when they have a region to invalidate. We may not
+   * actually pass these invalidations on right away.
+   *
+   * @param aRect The invalidation rect in the coordinate system of the unscaled
+   *              image (that is, the image at its intrinsic size).
+   * @param aRectAtTargetSize If not Nothing(), the invalidation rect in the
+   *                          coordinate system of the scaled image (that is,
+   *                          the image at our target decoding size). This must
+   *                          be supplied if we're downscaling during decode.
+   */
+  void PostInvalidation(const nsIntRect& aRect,
+                        const Maybe<nsIntRect>& aRectAtTargetSize = Nothing());
 
   // Called by the decoders when they have successfully decoded the image. This
   // may occur as the result of the decoder getting to the appropriate point in
   // the stream, or by us calling FinishInternal().
   //
   // May not be called mid-frame.
   //
   // For animated images, specify the loop count. -1 means loop forever, 0
@@ -356,26 +390,32 @@ protected:
   bool NeedsToFlushData() const { return mNeedsToFlushData; }
 
   /**
    * Ensures that a given frame number exists with the given parameters, and
    * returns a RawAccessFrameRef for that frame.
    * It is not possible to create sparse frame arrays; you can only append
    * frames to the current frame array, or if there is only one frame in the
    * array, replace that frame.
+   * @aTargetSize specifies the target size we're decoding to. If we're not
+   * downscaling during decode, this will always be the same as the image's
+   * intrinsic size.
+   *
    * If a non-paletted frame is desired, pass 0 for aPaletteDepth.
    */
   RawAccessFrameRef EnsureFrame(uint32_t aFrameNum,
+                                const nsIntSize& aTargetSize,
                                 const nsIntRect& aFrameRect,
                                 uint32_t aDecodeFlags,
                                 gfx::SurfaceFormat aFormat,
                                 uint8_t aPaletteDepth,
                                 imgFrame* aPreviousFrame);
 
   RawAccessFrameRef InternalAddFrame(uint32_t aFrameNum,
+                                     const nsIntSize& aTargetSize,
                                      const nsIntRect& aFrameRect,
                                      uint32_t aDecodeFlags,
                                      gfx::SurfaceFormat aFormat,
                                      uint8_t aPaletteDepth,
                                      imgFrame* aPreviousFrame);
 
   /*
    * Member variables.
--- a/image/src/DynamicImage.cpp
+++ b/image/src/DynamicImage.cpp
@@ -250,16 +250,22 @@ DynamicImage::RequestDecode()
 }
 
 NS_IMETHODIMP
 DynamicImage::StartDecoding()
 {
   return NS_OK;
 }
 
+NS_IMETHODIMP
+DynamicImage::RequestDecodeForSize(const nsIntSize& aSize, uint32_t aFlags)
+{
+  return NS_OK;
+}
+
 bool
 DynamicImage::IsDecoded()
 {
   return true;
 }
 
 NS_IMETHODIMP
 DynamicImage::LockImage()
--- a/image/src/Image.h
+++ b/image/src/Image.h
@@ -47,21 +47,26 @@ public:
    * INIT_FLAG_DECODE_ON_DRAW: The container should decode on draw rather than
    * decoding on load.
    *
    * INIT_FLAG_TRANSIENT: The container is likely to exist for only a short time
    * before being destroyed. (For example, containers for
    * multipart/x-mixed-replace image parts fall into this category.) If this
    * flag is set, INIT_FLAG_DISCARDABLE and INIT_FLAG_DECODE_ON_DRAW must not be
    * set.
+   *
+   * INIT_FLAG_DOWNSCALE_DURING_DECODE: The container should attempt to
+   * downscale images during decoding instead of decoding them to their
+   * intrinsic size.
    */
-  static const uint32_t INIT_FLAG_NONE           = 0x0;
-  static const uint32_t INIT_FLAG_DISCARDABLE    = 0x1;
-  static const uint32_t INIT_FLAG_DECODE_ON_DRAW = 0x2;
-  static const uint32_t INIT_FLAG_TRANSIENT      = 0x4;
+  static const uint32_t INIT_FLAG_NONE                     = 0x0;
+  static const uint32_t INIT_FLAG_DISCARDABLE              = 0x1;
+  static const uint32_t INIT_FLAG_DECODE_ON_DRAW           = 0x2;
+  static const uint32_t INIT_FLAG_TRANSIENT                = 0x4;
+  static const uint32_t INIT_FLAG_DOWNSCALE_DURING_DECODE  = 0x8;
 
   /**
    * Creates a new image container.
    *
    * @param aMimeType The mimetype of the image.
    * @param aFlags Initialization flags of the INIT_FLAG_* variety.
    */
   virtual nsresult Init(const char* aMimeType,
--- a/image/src/ImageFactory.cpp
+++ b/image/src/ImageFactory.cpp
@@ -26,24 +26,32 @@
 
 namespace mozilla {
 namespace image {
 
 /*static*/ void
 ImageFactory::Initialize()
 { }
 
+static bool
+ShouldDownscaleDuringDecode(const nsCString& aMimeType)
+{
+  // Not enabled for anything yet.
+  return false;
+}
+
 static uint32_t
-ComputeImageFlags(ImageURL* uri, bool isMultiPart)
+ComputeImageFlags(ImageURL* uri, const nsCString& aMimeType, bool isMultiPart)
 {
   nsresult rv;
 
   // We default to the static globals.
   bool isDiscardable = gfxPrefs::ImageMemDiscardable();
   bool doDecodeOnDraw = gfxPrefs::ImageMemDecodeOnDraw();
+  bool doDownscaleDuringDecode = gfxPrefs::ImageDownscaleDuringDecodeEnabled();
 
   // We want UI to be as snappy as possible and not to flicker. Disable
   // discarding and decode-on-draw for chrome URLS.
   bool isChrome = false;
   rv = uri->SchemeIs("chrome", &isChrome);
   if (NS_SUCCEEDED(rv) && isChrome) {
     isDiscardable = doDecodeOnDraw = false;
   }
@@ -51,50 +59,58 @@ ComputeImageFlags(ImageURL* uri, bool is
   // We don't want resources like the "loading" icon to be discardable or
   // decode-on-draw either.
   bool isResource = false;
   rv = uri->SchemeIs("resource", &isResource);
   if (NS_SUCCEEDED(rv) && isResource) {
     isDiscardable = doDecodeOnDraw = false;
   }
 
+  // Downscale-during-decode is only enabled for certain content types.
+  if (doDownscaleDuringDecode && !ShouldDownscaleDuringDecode(aMimeType)) {
+    doDownscaleDuringDecode = false;
+  }
+
   // For multipart/x-mixed-replace, we basically want a direct channel to the
-  // decoder. Disable both for this case as well.
+  // decoder. Disable everything for this case.
   if (isMultiPart) {
-    isDiscardable = doDecodeOnDraw = false;
+    isDiscardable = doDecodeOnDraw = doDownscaleDuringDecode = false;
   }
 
   // We have all the information we need.
   uint32_t imageFlags = Image::INIT_FLAG_NONE;
   if (isDiscardable) {
     imageFlags |= Image::INIT_FLAG_DISCARDABLE;
   }
   if (doDecodeOnDraw) {
     imageFlags |= Image::INIT_FLAG_DECODE_ON_DRAW;
   }
   if (isMultiPart) {
     imageFlags |= Image::INIT_FLAG_TRANSIENT;
   }
+  if (doDownscaleDuringDecode) {
+    imageFlags |= Image::INIT_FLAG_DOWNSCALE_DURING_DECODE;
+  }
 
   return imageFlags;
 }
 
 /* static */ already_AddRefed<Image>
 ImageFactory::CreateImage(nsIRequest* aRequest,
                           ProgressTracker* aProgressTracker,
                           const nsCString& aMimeType,
                           ImageURL* aURI,
                           bool aIsMultiPart,
                           uint32_t aInnerWindowId)
 {
   MOZ_ASSERT(gfxPrefs::SingletonExists(),
              "Pref observers should have been initialized already");
 
   // Compute the image's initialization flags.
-  uint32_t imageFlags = ComputeImageFlags(aURI, aIsMultiPart);
+  uint32_t imageFlags = ComputeImageFlags(aURI, aMimeType, aIsMultiPart);
 
   // Select the type of image to create based on MIME type.
   if (aMimeType.EqualsLiteral(IMAGE_SVG_XML)) {
     return CreateVectorImage(aRequest, aProgressTracker, aMimeType,
                              aURI, imageFlags, aInnerWindowId);
   } else {
     return CreateRasterImage(aRequest, aProgressTracker, aMimeType,
                              aURI, imageFlags, aInnerWindowId);
--- a/image/src/ImageMetadata.h
+++ b/image/src/ImageMetadata.h
@@ -48,16 +48,17 @@ public:
     }
   }
 
   bool HasSize() const { return mSize.isSome(); }
   bool HasOrientation() const { return mOrientation.isSome(); }
 
   int32_t GetWidth() const { return mSize->width; }
   int32_t GetHeight() const { return mSize->height; }
+  nsIntSize GetSize() const { return *mSize; }
   Orientation GetOrientation() const { return *mOrientation; }
 
 private:
   // The hotspot found on cursors, or -1 if none was found.
   int32_t mHotspotX;
   int32_t mHotspotY;
 
   // The loop count for animated images, or -1 for infinite loop.
--- a/image/src/ImageWrapper.cpp
+++ b/image/src/ImageWrapper.cpp
@@ -219,16 +219,22 @@ ImageWrapper::RequestDecode()
 }
 
 NS_IMETHODIMP
 ImageWrapper::StartDecoding()
 {
   return mInnerImage->StartDecoding();
 }
 
+NS_IMETHODIMP
+ImageWrapper::RequestDecodeForSize(const nsIntSize& aSize, uint32_t aFlags)
+{
+  return mInnerImage->RequestDecodeForSize(aSize, aFlags);
+}
+
 bool
 ImageWrapper::IsDecoded()
 {
   return mInnerImage->IsDecoded();
 }
 
 NS_IMETHODIMP
 ImageWrapper::LockImage()
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -29,16 +29,17 @@
 #include "nsJPEGDecoder.h"
 #include "nsBMPDecoder.h"
 #include "nsICODecoder.h"
 #include "nsIconDecoder.h"
 
 #include "gfxContext.h"
 
 #include "mozilla/gfx/2D.h"
+#include "mozilla/DebugOnly.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/Move.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Services.h"
 #include <stdint.h>
 #include "mozilla/Telemetry.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/ClearOnShutdown.h"
@@ -310,33 +311,39 @@ RasterImage::Init(const char* aMimeType,
     return NS_ERROR_FAILURE;
 
   NS_ENSURE_ARG_POINTER(aMimeType);
 
   // We must be non-discardable and non-decode-on-draw for
   // transient images.
   MOZ_ASSERT(!(aFlags & INIT_FLAG_TRANSIENT) ||
                (!(aFlags & INIT_FLAG_DISCARDABLE) &&
-                !(aFlags & INIT_FLAG_DECODE_ON_DRAW)),
-             "Transient images can't be discardable or decode-on-draw");
+                !(aFlags & INIT_FLAG_DECODE_ON_DRAW) &&
+                !(aFlags & INIT_FLAG_DOWNSCALE_DURING_DECODE)),
+             "Illegal init flags for transient image");
 
   // Store initialization data
   mSourceDataMimeType.Assign(aMimeType);
   mDiscardable = !!(aFlags & INIT_FLAG_DISCARDABLE);
   mDecodeOnDraw = !!(aFlags & INIT_FLAG_DECODE_ON_DRAW);
   mTransient = !!(aFlags & INIT_FLAG_TRANSIENT);
+  mDownscaleDuringDecode = !!(aFlags & INIT_FLAG_DOWNSCALE_DURING_DECODE);
+
+#ifndef MOZ_ENABLE_SKIA
+  // Downscale-during-decode requires Skia.
+  mDownscaleDuringDecode = false;
+#endif
 
   // Lock this image's surfaces in the SurfaceCache if we're not discardable.
   if (!mDiscardable) {
     SurfaceCache::LockImage(ImageKey(this));
   }
 
   // Create the initial size decoder.
-  nsresult rv = Decode(DecodeStrategy::ASYNC, DECODE_FLAGS_DEFAULT,
-                       /* aDoSizeDecode = */ true);
+  nsresult rv = Decode(DecodeStrategy::ASYNC, Nothing(), DECODE_FLAGS_DEFAULT);
   if (NS_FAILED(rv)) {
     return NS_ERROR_FAILURE;
   }
 
   // Mark us as initialized
   mInitialized = true;
 
   return NS_OK;
@@ -458,69 +465,91 @@ RasterImage::GetType(uint16_t *aType)
 NS_IMETHODIMP_(uint16_t)
 RasterImage::GetType()
 {
   return imgIContainer::TYPE_RASTER;
 }
 
 DrawableFrameRef
 RasterImage::LookupFrameInternal(uint32_t aFrameNum,
-                                 const nsIntSize& aSize,
+                                 const IntSize& aSize,
                                  uint32_t aFlags)
 {
   if (!mAnim) {
     NS_ASSERTION(aFrameNum == 0,
                  "Don't ask for a frame > 0 if we're not animated!");
     aFrameNum = 0;
   }
 
   if (mAnim && aFrameNum > 0) {
     MOZ_ASSERT(DecodeFlags(aFlags) == DECODE_FLAGS_DEFAULT,
                "Can't composite frames with non-default decode flags");
     return mAnim->GetCompositedFrame(aFrameNum);
   }
 
-  return SurfaceCache::Lookup(ImageKey(this),
-                              RasterSurfaceKey(aSize.ToIntSize(),
-                                               DecodeFlags(aFlags),
-                                               aFrameNum));
+  Maybe<uint32_t> alternateFlags;
+  if (IsOpaque()) {
+    // If we're opaque, we can always substitute a frame that was decoded with a
+    // different decode flag for premultiplied alpha, because that can only
+    // matter for frames with transparency.
+    alternateFlags = Some(aFlags ^ FLAG_DECODE_NO_PREMULTIPLY_ALPHA);
+  }
+
+  // We don't want any substitution for sync decodes (except the premultiplied
+  // alpha optimization above), so we use SurfaceCache::Lookup in this case.
+  if (aFlags & FLAG_SYNC_DECODE) {
+    return SurfaceCache::Lookup(ImageKey(this),
+                                RasterSurfaceKey(aSize,
+                                                 DecodeFlags(aFlags),
+                                                 aFrameNum),
+                                alternateFlags);
+  }
+
+  // We'll return the best match we can find to the requested frame.
+  return SurfaceCache::LookupBestMatch(ImageKey(this),
+                                       RasterSurfaceKey(aSize,
+                                                        DecodeFlags(aFlags),
+                                                        aFrameNum),
+                                       alternateFlags);
 }
 
 DrawableFrameRef
 RasterImage::LookupFrame(uint32_t aFrameNum,
                          const nsIntSize& aSize,
                          uint32_t aFlags,
                          bool aShouldSyncNotify /* = true */)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  DrawableFrameRef ref = LookupFrameInternal(aFrameNum, aSize, aFlags);
+  IntSize requestedSize = CanDownscaleDuringDecode(aSize, aFlags)
+                        ? aSize.ToIntSize()
+                        : mSize.ToIntSize();
+
+  DrawableFrameRef ref = LookupFrameInternal(aFrameNum, requestedSize, aFlags);
+
+  if (!ref && !mHasSize) {
+    // We can't request a decode without knowing our intrinsic size. Give up.
+    return DrawableFrameRef();
+  }
 
-  if (!ref && IsOpaque() && aFrameNum == 0) {
-    // We can use non-premultiplied alpha frames when premultipled alpha is
-    // requested, or vice versa, if this image is opaque. Try again with the bit
-    // toggled.
-    ref = LookupFrameInternal(aFrameNum, aSize,
-                              aFlags ^ FLAG_DECODE_NO_PREMULTIPLY_ALPHA);
+  if (!ref || ref->GetImageSize() != requestedSize) {
+    // The OS threw this frame away. We need to redecode if we can.
+    MOZ_ASSERT(!mAnim, "Animated frames should be locked");
+
+    WantDecodedFrames(ThebesIntSize(requestedSize), aFlags, aShouldSyncNotify);
+
+    // If we can sync decode, we should already have the frame.
+    if ((aFlags & FLAG_SYNC_DECODE) && aShouldSyncNotify) {
+      ref = LookupFrameInternal(aFrameNum, requestedSize, aFlags);
+    }
   }
 
   if (!ref) {
-    // The OS threw this frame away. We need to redecode if we can.
-    MOZ_ASSERT(!mAnim, "Animated frames should be locked");
-
-    WantDecodedFrames(aFlags, aShouldSyncNotify);
-
-    // If we were able to sync decode, we should already have the frame. If we
-    // had to decode asynchronously, maybe we've gotten lucky.
-    ref = LookupFrameInternal(aFrameNum, aSize, aFlags);
-
-    if (!ref) {
-      // We didn't successfully redecode, so just fail.
-      return DrawableFrameRef();
-    }
+    // We still weren't able to get a frame. Give up.
+    return DrawableFrameRef();
   }
 
   if (ref->GetCompositingFailed()) {
     return DrawableFrameRef();
   }
 
   MOZ_ASSERT(!ref || !ref->GetIsPaletted(), "Should not have paletted frame");
 
@@ -1117,18 +1146,17 @@ RasterImage::OnImageDataComplete(nsIRequ
 
   // Let decoders know that there won't be any more data coming.
   mSourceBuffer->Complete(aStatus);
 
   if (!mHasSize) {
     // We need to guarantee that we've gotten the image's size, or at least
     // determined that we won't be able to get it, before we deliver the load
     // event. That means we have to do a synchronous size decode here.
-    Decode(DecodeStrategy::SYNC_IF_POSSIBLE, DECODE_FLAGS_DEFAULT,
-           /* aDoSizeDecode = */ true);
+    Decode(DecodeStrategy::SYNC_IF_POSSIBLE, Nothing(), DECODE_FLAGS_DEFAULT);
   }
 
   // Determine our final status, giving precedence to Necko failure codes. We
   // check after running the size decode above in case it triggered an error.
   nsresult finalStatus = mError ? NS_ERROR_FAILURE : NS_OK;
   if (NS_FAILED(aStatus)) {
     finalStatus = aStatus;
   }
@@ -1244,23 +1272,26 @@ RasterImage::Discard()
 bool
 RasterImage::CanDiscard() {
   return mHasSourceData &&       // ...have the source data...
          !mAnim;                 // Can never discard animated images
 }
 
 // Sets up a decoder for this image.
 already_AddRefed<Decoder>
-RasterImage::CreateDecoder(bool aDoSizeDecode, uint32_t aFlags)
+RasterImage::CreateDecoder(const Maybe<nsIntSize>& aSize, uint32_t aFlags)
 {
   // Make sure we actually get size before doing a full decode.
-  if (aDoSizeDecode) {
+  if (aSize) {
+    MOZ_ASSERT(mHasSize, "Must do a size decode before a full decode!");
+    MOZ_ASSERT(mDownscaleDuringDecode || *aSize == mSize,
+               "Can only decode to our intrinsic size if we're not allowed to "
+               "downscale-during-decode");
+  } else {
     MOZ_ASSERT(!mHasSize, "Should not do unnecessary size decodes");
-  } else {
-    MOZ_ASSERT(mHasSize, "Must do a size decode before a full decode!");
   }
 
   // Figure out which decoder we want.
   eDecoderType type = GetDecoderType(mSourceDataMimeType.get());
   if (type == eDecoderType_unknown) {
     return nullptr;
   }
 
@@ -1291,37 +1322,48 @@ RasterImage::CreateDecoder(bool aDoSizeD
       break;
     default:
       MOZ_ASSERT_UNREACHABLE("Unknown decoder type");
   }
 
   MOZ_ASSERT(decoder, "Should have a decoder now");
 
   // Initialize the decoder.
-  decoder->SetSizeDecode(aDoSizeDecode);
+  decoder->SetSizeDecode(!aSize);
   decoder->SetSendPartialInvalidations(!mHasBeenDecoded);
   decoder->SetImageIsTransient(mTransient);
   decoder->SetDecodeFlags(DecodeFlags(aFlags));
-  if (!aDoSizeDecode) {
+  if (aSize) {
     // We already have the size; tell the decoder so it can preallocate a
     // frame.  By default, we create an ARGB frame with no offset. If decoders
     // need a different type, they need to ask for it themselves.
+    // XXX(seth): Note that we call SetSize() and NeedNewFrame() with the
+    // image's intrinsic size, but AllocateFrame with our target size.
     decoder->SetSize(mSize, mOrientation);
-    decoder->NeedNewFrame(0, 0, 0, mSize.width, mSize.height,
+    decoder->NeedNewFrame(0, 0, 0, aSize->width, aSize->height,
                           SurfaceFormat::B8G8R8A8);
-    decoder->AllocateFrame();
+    decoder->AllocateFrame(*aSize);
   }
   decoder->SetIterator(mSourceBuffer->Iterator());
+
+  // Set a target size for downscale-during-decode if applicable.
+  if (mDownscaleDuringDecode && aSize && *aSize != mSize) {
+    DebugOnly<nsresult> rv = decoder->SetTargetSize(*aSize);
+    MOZ_ASSERT(nsresult(rv) != NS_ERROR_NOT_AVAILABLE,
+               "We're downscale-during-decode but decoder doesn't support it?");
+    MOZ_ASSERT(NS_SUCCEEDED(rv), "Bad downscale-during-decode target size?");
+  }
+
   decoder->Init();
 
   if (NS_FAILED(decoder->GetDecoderError())) {
     return nullptr;
   }
 
-  if (!aDoSizeDecode) {
+  if (!aSize) {
     Telemetry::GetHistogramById(Telemetry::IMAGE_DECODE_COUNT)->Subtract(mDecodeCount);
     mDecodeCount++;
     Telemetry::GetHistogramById(Telemetry::IMAGE_DECODE_COUNT)->Add(mDecodeCount);
 
     if (mDecodeCount > sMaxDecodeCount) {
       // Don't subtract out 0 from the histogram, because that causes its count
       // to go negative, which is not kosher.
       if (sMaxDecodeCount > 0) {
@@ -1331,121 +1373,124 @@ RasterImage::CreateDecoder(bool aDoSizeD
       Telemetry::GetHistogramById(Telemetry::IMAGE_MAX_DECODE_COUNT)->Add(sMaxDecodeCount);
     }
   }
 
   return decoder.forget();
 }
 
 void
-RasterImage::WantDecodedFrames(uint32_t aFlags, bool aShouldSyncNotify)
+RasterImage::WantDecodedFrames(const nsIntSize& aSize, uint32_t aFlags,
+                               bool aShouldSyncNotify)
 {
   if (aShouldSyncNotify) {
     // We can sync notify, which means we can also sync decode.
     if (aFlags & FLAG_SYNC_DECODE) {
-      Decode(DecodeStrategy::SYNC_IF_POSSIBLE, aFlags);
+      Decode(DecodeStrategy::SYNC_IF_POSSIBLE, Some(aSize), aFlags);
       return;
     }
 
     // Here we are explicitly trading off flashing for responsiveness in the
     // case that we're redecoding an image (see bug 845147).
     Decode(mHasBeenDecoded ? DecodeStrategy::ASYNC
                            : DecodeStrategy::SYNC_FOR_SMALL_IMAGES,
-           aFlags);
+           Some(aSize), aFlags);
     return;
   }
 
   // We can't sync notify, so do an async decode.
-  Decode(DecodeStrategy::ASYNC, aFlags);
+  Decode(DecodeStrategy::ASYNC, Some(aSize), aFlags);
 }
 
 //******************************************************************************
 /* void requestDecode() */
 NS_IMETHODIMP
 RasterImage::RequestDecode()
 {
-  MOZ_ASSERT(NS_IsMainThread());
-
-  if (mError) {
-    return NS_ERROR_FAILURE;
-  }
-  if (!mHasSize) {
-    mWantFullDecode = true;
-    return NS_OK;
-  }
-
-  // Look up the first frame of the image, which will implicitly start decoding
-  // if it's not available right now.
-  // XXX(seth): Passing false for aShouldSyncNotify here has the effect of
-  // decoding asynchronously, but that's not obvious from the argument name.
-  // This API needs to be reworked.
-  LookupFrame(0, mSize, DECODE_FLAGS_DEFAULT, /* aShouldSyncNotify = */ false);
-
-  return NS_OK;
+  return RequestDecodeForSize(mSize, DECODE_FLAGS_DEFAULT);
 }
 
 /* void startDecode() */
 NS_IMETHODIMP
 RasterImage::StartDecoding()
 {
   if (!NS_IsMainThread()) {
     return NS_DispatchToMainThread(
       NS_NewRunnableMethod(this, &RasterImage::StartDecoding));
   }
 
+  return RequestDecodeForSize(mSize, FLAG_SYNC_DECODE);
+}
+
+NS_IMETHODIMP
+RasterImage::RequestDecodeForSize(const nsIntSize& aSize, uint32_t aFlags)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
   if (mError) {
     return NS_ERROR_FAILURE;
   }
+
   if (!mHasSize) {
     mWantFullDecode = true;
     return NS_OK;
   }
 
+  // Fall back to our intrinsic size if we don't support
+  // downscale-during-decode.
+  nsIntSize targetSize = mDownscaleDuringDecode ? aSize : mSize;
+
+  // Sync decode small images if requested.
+  bool shouldSyncDecodeSmallImages = aFlags & FLAG_SYNC_DECODE;
+
   // Look up the first frame of the image, which will implicitly start decoding
   // if it's not available right now.
   // XXX(seth): Passing true for aShouldSyncNotify here has the effect of
-  // synchronously decoding small images, but that's not obvious from the
-  // argument name. This API needs to be reworked.
-  LookupFrame(0, mSize, DECODE_FLAGS_DEFAULT, /* aShouldSyncNotify = */ true);
+  // synchronously decoding small images, while passing false has the effect of
+  // decoding asynchronously, but that's not obvious from the argument name.
+  // This API needs to be reworked.
+  LookupFrame(0, targetSize, DecodeFlags(aFlags),
+              /* aShouldSyncNotify = */ shouldSyncDecodeSmallImages);
 
   return NS_OK;
 }
 
 bool
 RasterImage::IsDecoded()
 {
   // XXX(seth): We need to get rid of this; it's not reliable.
   return mHasBeenDecoded || mError;
 }
 
 NS_IMETHODIMP
 RasterImage::Decode(DecodeStrategy aStrategy,
-                    uint32_t aFlags,
-                    bool aDoSizeDecode /* = false */)
+                    const Maybe<nsIntSize>& aSize,
+                    uint32_t aFlags)
 {
-  MOZ_ASSERT(aDoSizeDecode || NS_IsMainThread());
+  MOZ_ASSERT(!aSize || NS_IsMainThread());
 
   if (mError) {
     return NS_ERROR_FAILURE;
   }
 
   // If we don't have a size yet, we can't do any other decoding.
-  if (!mHasSize && !aDoSizeDecode) {
+  if (!mHasSize && aSize) {
     mWantFullDecode = true;
     return NS_OK;
   }
 
   // Create a decoder.
-  nsRefPtr<Decoder> decoder = CreateDecoder(aDoSizeDecode, aFlags);
+  nsRefPtr<Decoder> decoder = CreateDecoder(aSize, aFlags);
   if (!decoder) {
     return NS_ERROR_FAILURE;
   }
 
-  if (!aDoSizeDecode) {
-    // Send out early notifications right away.
+  if (aSize) {
+    // This isn't a size decode (which doesn't send any early notifications), so
+    // send out notifications right away.
     NotifyProgress(decoder->TakeProgress(),
                    decoder->TakeInvalidRect(),
                    decoder->GetDecodeFlags());
 
     // Lock the image while we're decoding, so that it doesn't get evicted from
     // the SurfaceCache before we have a chance to realize that it's animated.
     // The corresponding unlock happens in FinalizeDecoder.
     LockImage();
@@ -1490,16 +1535,21 @@ RasterImage::CanScale(GraphicsFilter aFi
   // we usually have no way of updating what we've drawn, so HQ scaling is
   // useless.
   if (!gfxPrefs::ImageHQDownscalingEnabled() || !mHasSize || !mHasSourceData ||
       !(aFlags & imgIContainer::FLAG_HIGH_QUALITY_SCALING) ||
       aFilter != GraphicsFilter::FILTER_GOOD) {
     return false;
   }
 
+  // We don't HQ scale images that we can downscale during decode.
+  if (mDownscaleDuringDecode) {
+    return false;
+  }
+
   // We don't use the scaler for animated or transient images to avoid doing a
   // bunch of work on an image that just gets thrown away.
   if (mAnim || mTransient) {
     return false;
   }
 
   // If target size is 1:1 with original, don't scale.
   if (aSize == mSize) {
@@ -1525,16 +1575,50 @@ RasterImage::CanScale(GraphicsFilter aFi
   // If that's all it's getting us, I'd rather we just forbid that explicitly.
   gfx::Size scale(double(aSize.width) / mSize.width,
                   double(aSize.height) / mSize.height);
   gfxFloat minFactor = gfxPrefs::ImageHQDownscalingMinFactor() / 1000.0;
   return (scale.width < minFactor || scale.height < minFactor);
 #endif
 }
 
+bool
+RasterImage::CanDownscaleDuringDecode(const nsIntSize& aSize, uint32_t aFlags)
+{
+  // Check basic requirements: downscale-during-decode is enabled for this
+  // image, we have all the source data and know our size, the flags allow us to
+  // do it, and a 'good' filter is being used.
+  if (!mDownscaleDuringDecode || !mHasSize ||
+      !(aFlags & imgIContainer::FLAG_HIGH_QUALITY_SCALING)) {
+    return false;
+  }
+
+  // We don't downscale animated images during decode.
+  if (mAnim) {
+    return false;
+  }
+
+  // Never upscale.
+  if (aSize.width >= mSize.width || aSize.height >= mSize.height) {
+    return false;
+  }
+
+  // Zero or negative width or height is unacceptable.
+  if (aSize.width < 1 || aSize.height < 1) {
+    return false;
+  }
+
+  // There's no point in scaling if we can't store the result.
+  if (!SurfaceCache::CanHold(aSize.ToIntSize())) {
+    return false;
+  }
+
+  return true;
+}
+
 void
 RasterImage::NotifyNewScaledFrame()
 {
   // Send an invalidation so observers will repaint and can take advantage of
   // the new scaled frame if possible.
   NotifyProgress(NoProgress, nsIntRect(0, 0, mSize.width, mSize.height));
 }
 
@@ -1649,36 +1733,37 @@ RasterImage::Draw(gfxContext* aContext,
     return NS_ERROR_FAILURE;
 
   NS_ENSURE_ARG_POINTER(aContext);
 
   if (IsUnlocked() && mProgressTracker) {
     mProgressTracker->OnUnlockedDraw();
   }
 
-  // XXX(seth): For now, we deliberately don't look up a frame of size aSize
-  // (though DrawWithPreDownscaleIfNeeded will do so later). It doesn't make
-  // sense to do so until we support downscale-during-decode. Right now we need
-  // to make sure that we always touch an mSize-sized frame so that we have
-  // something to HQ scale.
+  // If we're not using GraphicsFilter::FILTER_GOOD, we shouldn't high-quality
+  // scale or downscale during decode.
+  uint32_t flags = aFilter == GraphicsFilter::FILTER_GOOD
+                 ? aFlags
+                 : aFlags & ~FLAG_HIGH_QUALITY_SCALING;
+
   DrawableFrameRef ref =
-    LookupFrame(GetRequestedFrameIndex(aWhichFrame), mSize, aFlags);
+    LookupFrame(GetRequestedFrameIndex(aWhichFrame), aSize, flags);
   if (!ref) {
     // Getting the frame (above) touches the image and kicks off decoding.
     if (mDrawStartTime.IsNull()) {
       mDrawStartTime = TimeStamp::Now();
     }
     return NS_OK;
   }
 
   bool shouldRecordTelemetry = !mDrawStartTime.IsNull() &&
                                ref->IsImageComplete();
 
   DrawWithPreDownscaleIfNeeded(Move(ref), aContext, aSize,
-                               aRegion, aFilter, aFlags);
+                               aRegion, aFilter, flags);
 
   if (shouldRecordTelemetry) {
       TimeDuration drawLatency = TimeStamp::Now() - mDrawStartTime;
       Telemetry::Accumulate(Telemetry::IMAGE_DECODE_ON_DRAW_LATENCY,
                             int32_t(drawLatency.ToMicroseconds()));
       mDrawStartTime = TimeStamp();
   }
 
@@ -1933,17 +2018,20 @@ RasterImage::OptimalImageSizeForDest(con
              "Unexpected destination size");
 
   if (mSize.IsEmpty() || aDest.IsEmpty()) {
     return nsIntSize(0, 0);
   }
 
   nsIntSize destSize(ceil(aDest.width), ceil(aDest.height));
 
-  if (CanScale(aFilter, destSize, aFlags)) {
+  if (aFilter == GraphicsFilter::FILTER_GOOD &&
+      CanDownscaleDuringDecode(destSize, aFlags)) {
+    return destSize;
+  } else if (CanScale(aFilter, destSize, aFlags)) {
     DrawableFrameRef frameRef =
       SurfaceCache::Lookup(ImageKey(this),
                            RasterSurfaceKey(destSize.ToIntSize(),
                                             DecodeFlags(aFlags),
                                             0));
 
     if (frameRef && frameRef->IsImageComplete()) {
         return destSize;  // We have an existing HQ scale for this size.
--- a/image/src/RasterImage.h
+++ b/image/src/RasterImage.h
@@ -291,17 +291,17 @@ private:
   TemporaryRef<gfx::SourceSurface> CopyFrame(uint32_t aWhichFrame,
                                              uint32_t aFlags,
                                              bool aShouldSyncNotify = true);
   TemporaryRef<gfx::SourceSurface> GetFrameInternal(uint32_t aWhichFrame,
                                                     uint32_t aFlags,
                                                     bool aShouldSyncNotify = true);
 
   DrawableFrameRef LookupFrameInternal(uint32_t aFrameNum,
-                                       const nsIntSize& aSize,
+                                       const gfx::IntSize& aSize,
                                        uint32_t aFlags);
   DrawableFrameRef LookupFrame(uint32_t aFrameNum,
                                const nsIntSize& aSize,
                                uint32_t aFlags,
                                bool aShouldSyncNotify = true);
   uint32_t GetCurrentFrameIndex() const;
   uint32_t GetRequestedFrameIndex(uint32_t aWhichFrame) const;
 
@@ -320,22 +320,36 @@ private:
   // that case we use our animation consumers count as a proxy for lock count.
   bool IsUnlocked() { return (mLockCount == 0 || (mAnim && mAnimationConsumers == 0)); }
 
 
   //////////////////////////////////////////////////////////////////////////////
   // Decoding.
   //////////////////////////////////////////////////////////////////////////////
 
-  already_AddRefed<Decoder> CreateDecoder(bool aDoSizeDecode, uint32_t aFlags);
+  /**
+   * Creates and runs a decoder, either synchronously or asynchronously
+   * according to @aStrategy. Passes the provided target size @aSize and decode
+   * flags @aFlags to CreateDecoder. If a size decode is desired, pass Nothing
+   * for @aSize.
+   */
+  NS_IMETHOD Decode(DecodeStrategy aStrategy,
+                    const Maybe<nsIntSize>& aSize,
+                    uint32_t aFlags);
 
-  void WantDecodedFrames(uint32_t aFlags, bool aShouldSyncNotify);
+  /**
+   * Creates a new decoder with a target size of @aSize and decode flags
+   * specified by @aFlags. If a size decode is desired, pass Nothing() for
+   * @aSize.
+   */
+  already_AddRefed<Decoder> CreateDecoder(const Maybe<nsIntSize>& aSize,
+                                          uint32_t aFlags);
 
-  NS_IMETHOD Decode(DecodeStrategy aStrategy, uint32_t aFlags,
-                    bool aDoSizeDecode = false);
+  void WantDecodedFrames(const nsIntSize& aSize, uint32_t aFlags,
+                         bool aShouldSyncNotify);
 
 private: // data
   nsIntSize                  mSize;
   Orientation                mOrientation;
 
   nsCOMPtr<nsIProperties>   mProperties;
 
   /// If this image is animated, a FrameAnimator which manages its animation.
@@ -373,16 +387,17 @@ private: // data
 
   // Boolean flags (clustered together to conserve space):
   bool                       mHasSize:1;       // Has SetSize() been called?
   bool                       mDecodeOnDraw:1;  // Decoding on draw?
   bool                       mTransient:1;     // Is the image short-lived?
   bool                       mDiscardable:1;   // Is container discardable?
   bool                       mHasSourceData:1; // Do we have source data?
   bool                       mHasBeenDecoded:1; // Decoded at least once?
+  bool                       mDownscaleDuringDecode:1;
 
   // Whether we're waiting to start animation. If we get a StartAnimation() call
   // but we don't yet have more than one frame, mPendingAnimation is set so that
   // we know to start animation later if/when we have more frames.
   bool                       mPendingAnimation:1;
 
   // Whether the animation can stop, due to running out
   // of frames, or no more owning request
@@ -403,16 +418,19 @@ private: // data
   //////////////////////////////////////////////////////////////////////////////
 
   // Initiates an HQ scale for the given frame, if possible.
   void RequestScale(imgFrame* aFrame, uint32_t aFlags, const nsIntSize& aSize);
 
   // Determines whether we can perform an HQ scale with the given parameters.
   bool CanScale(GraphicsFilter aFilter, const nsIntSize& aSize, uint32_t aFlags);
 
+  // Determines whether we can downscale during decode with the given parameters.
+  bool CanDownscaleDuringDecode(const nsIntSize& aSize, uint32_t aFlags);
+
   // Called by the HQ scaler when a new scaled frame is ready.
   void NotifyNewScaledFrame();
 
   friend class ScaleRunner;
 
 
   // Error handling.
   void DoError();
--- a/image/src/SurfaceCache.cpp
+++ b/image/src/SurfaceCache.cpp
@@ -243,25 +243,111 @@ public:
 
   already_AddRefed<CachedSurface> Lookup(const SurfaceKey& aSurfaceKey)
   {
     nsRefPtr<CachedSurface> surface;
     mSurfaces.Get(aSurfaceKey, getter_AddRefs(surface));
     return surface.forget();
   }
 
+  already_AddRefed<CachedSurface>
+  LookupBestMatch(const SurfaceKey&      aSurfaceKey,
+                  const Maybe<uint32_t>& aAlternateFlags)
+  {
+    // Try for a perfect match first.
+    nsRefPtr<CachedSurface> surface;
+    mSurfaces.Get(aSurfaceKey, getter_AddRefs(surface));
+    if (surface) {
+      return surface.forget();
+    }
+
+    // There's no perfect match, so find the best match we can.
+    MatchContext matchContext(aSurfaceKey, aAlternateFlags);
+    ForEach(TryToImproveMatch, &matchContext);
+    return matchContext.mBestMatch.forget();
+  }
+
   void ForEach(SurfaceTable::EnumReadFunction aFunction, void* aData)
   {
     mSurfaces.EnumerateRead(aFunction, aData);
   }
 
   void SetLocked(bool aLocked) { mLocked = aLocked; }
   bool IsLocked() const { return mLocked; }
 
 private:
+  struct MatchContext
+  {
+    MatchContext(const SurfaceKey& aIdealKey,
+                 const Maybe<uint32_t>& aAlternateFlags)
+      : mIdealKey(aIdealKey)
+      , mAlternateFlags(aAlternateFlags)
+    { }
+
+    const SurfaceKey& mIdealKey;
+    const Maybe<uint32_t> mAlternateFlags;
+    nsRefPtr<CachedSurface> mBestMatch;
+  };
+
+  static PLDHashOperator TryToImproveMatch(const SurfaceKey& aSurfaceKey,
+                                           CachedSurface*    aSurface,
+                                           void*             aContext)
+  {
+    auto context = static_cast<MatchContext*>(aContext);
+    const SurfaceKey& idealKey = context->mIdealKey;
+
+    // Matching the animation time and SVG context is required.
+    if (aSurfaceKey.AnimationTime() != idealKey.AnimationTime() ||
+        aSurfaceKey.SVGContext() != idealKey.SVGContext()) {
+      return PL_DHASH_NEXT;
+    }
+
+    // Matching the flags is required, but we can match the alternate flags as
+    // well if some were provided.
+    if (aSurfaceKey.Flags() != idealKey.Flags() &&
+        Some(aSurfaceKey.Flags()) != context->mAlternateFlags) {
+      return PL_DHASH_NEXT;
+    }
+
+    // Anything is better than nothing! (Within the constraints we just
+    // checked, of course.)
+    if (!context->mBestMatch) {
+      context->mBestMatch = aSurface;
+      return PL_DHASH_NEXT;
+    }
+
+    MOZ_ASSERT(context->mBestMatch, "Should have a current best match");
+    SurfaceKey bestMatchKey = context->mBestMatch->GetSurfaceKey();
+
+    // Compare sizes. We use an area-based heuristic here instead of computing a
+    // truly optimal answer, since it seems very unlikely to make a difference
+    // for realistic sizes.
+    int64_t idealArea = idealKey.Size().width * idealKey.Size().height;
+    int64_t surfaceArea = aSurfaceKey.Size().width * aSurfaceKey.Size().height;
+    int64_t bestMatchArea =
+      bestMatchKey.Size().width * bestMatchKey.Size().height;
+
+    // If the best match is smaller than the ideal size, prefer bigger sizes.
+    if (bestMatchArea < idealArea) {
+      if (surfaceArea > bestMatchArea) {
+        context->mBestMatch = aSurface;
+      }
+      return PL_DHASH_NEXT;
+    }
+
+    // Other, prefer sizes closer to the ideal size, but still not smaller.
+    if (idealArea <= surfaceArea && surfaceArea < bestMatchArea) {
+      context->mBestMatch = aSurface;
+      return PL_DHASH_NEXT;
+    }
+
+    // This surface isn't an improvement over the current best match.
+    return PL_DHASH_NEXT;
+  }
+
   SurfaceTable mSurfaces;
   bool         mLocked;
 };
 
 /**
  * SurfaceCacheImpl is responsible for determining which surfaces will be cached
  * and managing the surface cache data structures. Rather than interact with
  * SurfaceCacheImpl directly, client code interacts with SurfaceCache, which
@@ -448,16 +534,55 @@ public:
 
     if (!surface->IsLocked()) {
       mExpirationTracker.MarkUsed(surface);
     }
 
     return ref;
   }
 
+  DrawableFrameRef LookupBestMatch(const ImageKey         aImageKey,
+                                   const SurfaceKey&      aSurfaceKey,
+                                   const Maybe<uint32_t>& aAlternateFlags)
+  {
+    nsRefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
+    if (!cache)
+      return DrawableFrameRef();  // No cached surfaces for this image.
+
+    // Repeatedly look up the best match, trying again if the resulting surface
+    // has been freed by the operating system, until we can either lock a
+    // surface for drawing or there are no matching surfaces left.
+    // XXX(seth): This is O(N^2), but N is expected to be very small. If we
+    // encounter a performance problem here we can revisit this.
+
+    nsRefPtr<CachedSurface> surface;
+    DrawableFrameRef ref;
+    while (true) {
+      surface = cache->LookupBestMatch(aSurfaceKey, aAlternateFlags);
+      if (!surface) {
+        return DrawableFrameRef();  // Lookup in the per-image cache missed.
+      }
+
+      ref = surface->DrawableRef();
+      if (ref) {
+        break;
+      }
+
+      // The surface was released by the operating system. Remove the cache
+      // entry as well.
+      Remove(surface);
+    }
+
+    if (!surface->IsLocked()) {
+      mExpirationTracker.MarkUsed(surface);
+    }
+
+    return ref;
+  }
+
   void RemoveSurface(const ImageKey    aImageKey,
                      const SurfaceKey& aSurfaceKey)
   {
     nsRefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
     if (!cache)
       return;  // No cached surfaces for this image.
 
     nsRefPtr<CachedSurface> surface = cache->Lookup(aSurfaceKey);
@@ -782,25 +907,47 @@ SurfaceCache::Initialize()
 SurfaceCache::Shutdown()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(sInstance, "No singleton - was Shutdown() called twice?");
   sInstance = nullptr;
 }
 
 /* static */ DrawableFrameRef
-SurfaceCache::Lookup(const ImageKey    aImageKey,
-                     const SurfaceKey& aSurfaceKey)
+SurfaceCache::Lookup(const ImageKey         aImageKey,
+                     const SurfaceKey&      aSurfaceKey,
+                     const Maybe<uint32_t>& aAlternateFlags /* = Nothing() */)
 {
   if (!sInstance) {
     return DrawableFrameRef();
   }
 
   MutexAutoLock lock(sInstance->GetMutex());
-  return sInstance->Lookup(aImageKey, aSurfaceKey);
+
+  DrawableFrameRef ref = sInstance->Lookup(aImageKey, aSurfaceKey);
+  if (!ref && aAlternateFlags) {
+    ref = sInstance->Lookup(aImageKey,
+                            aSurfaceKey.WithNewFlags(*aAlternateFlags));
+  }
+
+  return ref;
+}
+
+/* static */ DrawableFrameRef
+SurfaceCache::LookupBestMatch(const ImageKey         aImageKey,
+                              const SurfaceKey&      aSurfaceKey,
+                              const Maybe<uint32_t>& aAlternateFlags
+                                /* = Nothing() */)
+{
+  if (!sInstance) {
+    return DrawableFrameRef();
+  }
+
+  MutexAutoLock lock(sInstance->GetMutex());
+  return sInstance->LookupBestMatch(aImageKey, aSurfaceKey, aAlternateFlags);
 }
 
 /* static */ InsertOutcome
 SurfaceCache::Insert(imgFrame*         aSurface,
                      const ImageKey    aImageKey,
                      const SurfaceKey& aSurfaceKey,
                      Lifetime          aLifetime)
 {
--- a/image/src/SurfaceCache.h
+++ b/image/src/SurfaceCache.h
@@ -58,16 +58,24 @@ public:
   {
     uint32_t hash = HashGeneric(mSize.width, mSize.height);
     hash = AddToHash(hash, mSVGContext.map(HashSIC).valueOr(0));
     hash = AddToHash(hash, mAnimationTime, mFlags);
     return hash;
   }
 
   IntSize Size() const { return mSize; }
+  Maybe<SVGImageContext> SVGContext() const { return mSVGContext; }
+  float AnimationTime() const { return mAnimationTime; }
+  uint32_t Flags() const { return mFlags; }
+
+  SurfaceKey WithNewFlags(uint32_t aFlags) const
+  {
+    return SurfaceKey(mSize, mSVGContext, mAnimationTime, aFlags);
+  }
 
 private:
   SurfaceKey(const IntSize& aSize,
              const Maybe<SVGImageContext>& aSVGContext,
              const float aAnimationTime,
              const uint32_t aFlags)
     : mSize(aSize)
     , mSVGContext(aSVGContext)
@@ -166,22 +174,51 @@ struct SurfaceCache
    * If the imgFrame was found in the cache, but had stored its surface in a
    * volatile buffer which was discarded by the OS, then it is automatically
    * removed from the cache and an empty DrawableFrameRef is returned. Note that
    * this will never happen to persistent surfaces associated with a locked
    * image; the cache keeps a strong reference to such surfaces internally.
    *
    * @param aImageKey    Key data identifying which image the surface belongs to.
    * @param aSurfaceKey  Key data which uniquely identifies the requested surface.
+   * @param aAlternateFlags If not Nothing(), a different set of flags than the
+   *                        ones specified in @aSurfaceKey which are also
+   *                        acceptable to the caller. This is more efficient
+   *                        than calling Lookup() twice, which requires taking a
+   *                        lock each time.
    *
    * @return a DrawableFrameRef to the imgFrame wrapping the requested surface,
    *         or an empty DrawableFrameRef if not found.
    */
   static DrawableFrameRef Lookup(const ImageKey    aImageKey,
-                                 const SurfaceKey& aSurfaceKey);
+                                 const SurfaceKey& aSurfaceKey,
+                                 const Maybe<uint32_t>& aAlternateFlags
+                                   = Nothing());
+
+  /**
+   * Looks up the best matching surface in the cache and returns a drawable
+   * reference to the imgFrame containing it.
+   *
+   * Returned surfaces may vary from the requested surface only in terms of
+   * size, unless @aAlternateFlags is specified.
+   *
+   * @param aImageKey    Key data identifying which image the surface belongs to.
+   * @param aSurfaceKey  Key data which identifies the ideal surface to return.
+   * @param aAlternateFlags If not Nothing(), a different set of flags than the
+   *                        ones specified in @aSurfaceKey which are also
+   *                        acceptable to the caller. This is much more
+   *                        efficient than calling LookupBestMatch() twice.
+   *
+   * @return a DrawableFrameRef to the imgFrame wrapping a surface similar to
+   *         the requested surface, or an empty DrawableFrameRef if not found.
+   */
+  static DrawableFrameRef LookupBestMatch(const ImageKey    aImageKey,
+                                          const SurfaceKey& aSurfaceKey,
+                                          const Maybe<uint32_t>& aAlternateFlags
+                                            = Nothing());
 
   /**
    * Insert a surface into the cache. If a surface with the same ImageKey and
    * SurfaceKey is already in the cache, Insert returns FAILURE_ALREADY_PRESENT.
    *
    * Each surface in the cache has a lifetime, either Transient or Persistent.
    * Transient surfaces can expire from the cache at any time. Persistent
    * surfaces can ordinarily also expire from the cache at any time, but if the
--- a/image/src/VectorImage.cpp
+++ b/image/src/VectorImage.cpp
@@ -894,16 +894,25 @@ VectorImage::RequestDecode()
 
 NS_IMETHODIMP
 VectorImage::StartDecoding()
 {
   // Nothing to do for SVG images
   return NS_OK;
 }
 
+NS_IMETHODIMP
+VectorImage::RequestDecodeForSize(const nsIntSize& aSize, uint32_t aFlags)
+{
+  // Nothing to do for SVG images, though in theory we could rasterize to the
+  // provided size ahead of time if we supported off-main-thread SVG
+  // rasterization...
+  return NS_OK;
+}
+
 bool
 VectorImage::IsDecoded()
 {
   return mIsFullyLoaded || mError;
 }
 
 //******************************************************************************
 /* void lockImage() */
--- a/image/test/crashtests/crashtests.list
+++ b/image/test/crashtests/crashtests.list
@@ -26,17 +26,17 @@ load invalid-icc-profile.jpg
 load 256-width.ico
 load 256-height.ico
 
 # A 3-frame animated GIF with an inordinate delay between the second and third
 # frame.
 HTTP load delayedframe.sjs
 
 asserts(0-1) load 681190.html # asserts can't create such a big surface
-skip-if(Android&&smallScreen) skip-if(B2G) load 694165-1.xhtml # nexus-s Android 2.3.6, bug 876275 for B2G on a VM
+skip-if(Android&&smallScreen) skip-if(B2G) skip-if(OSX==1010&&isDebugBuild) load 694165-1.xhtml # nexus-s Android 2.3.6, bug 876275 for B2G on a VM; bug 1123195 for OS X 10.10 debug
 load 732319-1.html
 load 844403-1.html
 
 load truncated-second-frame.png # bug 863975
 
 # Bug 863958
 # This icon's size is such that it leads to multiple writes to the PNG decoder
 # after we've gotten our size.
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -457,17 +457,17 @@ struct JSClass {
 // Implementing this efficiently requires that global objects have classes
 // with the following flags. Failure to use JSCLASS_GLOBAL_FLAGS was
 // previously allowed, but is now an ES5 violation and thus unsupported.
 //
 // JSCLASS_GLOBAL_APPLICATION_SLOTS is the number of slots reserved at
 // the beginning of every global object's slots for use by the
 // application.
 #define JSCLASS_GLOBAL_APPLICATION_SLOTS 4
-#define JSCLASS_GLOBAL_SLOT_COUNT      (JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 3 + 30)
+#define JSCLASS_GLOBAL_SLOT_COUNT      (JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 3 + 31)
 #define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n)                                    \
     (JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n)))
 #define JSCLASS_GLOBAL_FLAGS                                                  \
     JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(0)
 #define JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(clasp)                              \
   (((clasp)->flags & JSCLASS_IS_GLOBAL)                                       \
    && JSCLASS_RESERVED_SLOTS(clasp) >= JSCLASS_GLOBAL_SLOT_COUNT)
 
--- a/js/src/builtin/SIMD.cpp
+++ b/js/src/builtin/SIMD.cpp
@@ -26,16 +26,17 @@ using namespace js;
 
 using mozilla::ArrayLength;
 using mozilla::IsFinite;
 using mozilla::IsNaN;
 using mozilla::FloorLog2;
 
 namespace js {
 extern const JSFunctionSpec Float32x4Methods[];
+extern const JSFunctionSpec Float64x2Methods[];
 extern const JSFunctionSpec Int32x4Methods[];
 }
 
 ///////////////////////////////////////////////////////////////////////////
 // SIMD
 
 static const char *laneNames[] = {"lane 0", "lane 1", "lane 2", "lane3"};
 
@@ -60,16 +61,17 @@ template<class V>
 bool
 js::IsVectorObject(HandleValue v)
 {
     return CheckVectorObject(v, V::type);
 }
 
 template bool js::IsVectorObject<Int32x4>(HandleValue v);
 template bool js::IsVectorObject<Float32x4>(HandleValue v);
+template bool js::IsVectorObject<Float64x2>(HandleValue v);
 
 template<typename V>
 bool
 js::ToSimdConstant(JSContext *cx, HandleValue v, jit::SimdConstant *out)
 {
     typedef typename V::Elem Elem;
     if (!IsVectorObject<V>(v)) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SIMD_NOT_A_VECTOR);
@@ -119,16 +121,19 @@ static bool type##Lane##lane(JSContext *
     LANE_ACCESSOR(type, 0); \
     LANE_ACCESSOR(type, 1); \
     LANE_ACCESSOR(type, 2); \
     LANE_ACCESSOR(type, 3);
 
     FOUR_LANES_ACCESSOR(Int32x4);
     FOUR_LANES_ACCESSOR(Float32x4);
 #undef FOUR_LANES_ACCESSOR
+
+    LANE_ACCESSOR(Float64x2, 0);
+    LANE_ACCESSOR(Float64x2, 1);
 #undef LANE_ACCESSOR
 
 template<typename SimdType>
 static bool SignMask(JSContext *cx, unsigned argc, Value *vp)
 {
     typedef typename SimdType::Elem Elem;
 
     CallArgs args = CallArgsFromVp(argc, vp);
@@ -163,16 +168,17 @@ static bool SignMask(JSContext *cx, unsi
     return true;
 }
 
 #define SIGN_MASK(type) \
 static bool type##SignMask(JSContext *cx, unsigned argc, Value *vp) { \
     return SignMask<type>(cx, argc, vp); \
 }
     SIGN_MASK(Float32x4);
+    SIGN_MASK(Float64x2);
     SIGN_MASK(Int32x4);
 #undef SIGN_MASK
 
 const Class SimdTypeDescr::class_ = {
     "SIMD",
     JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
     nullptr, /* addProperty */
     nullptr, /* delProperty */
@@ -196,16 +202,23 @@ class Int32x4Defn {
 };
 class Float32x4Defn {
   public:
     static const SimdTypeDescr::Type type = SimdTypeDescr::TYPE_FLOAT32;
     static const JSFunctionSpec TypeDescriptorMethods[];
     static const JSPropertySpec TypedObjectProperties[];
     static const JSFunctionSpec TypedObjectMethods[];
 };
+class Float64x2Defn {
+  public:
+    static const SimdTypeDescr::Type type = SimdTypeDescr::TYPE_FLOAT64;
+    static const JSFunctionSpec TypeDescriptorMethods[];
+    static const JSPropertySpec TypedObjectProperties[];
+    static const JSFunctionSpec TypedObjectMethods[];
+};
 } // namespace js
 
 const JSFunctionSpec js::Float32x4Defn::TypeDescriptorMethods[] = {
     JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
     JS_SELF_HOSTED_FN("array", "ArrayShorthand", 1, 0),
     JS_SELF_HOSTED_FN("equivalent", "TypeDescrEquivalent", 1, 0),
     JS_FS_END
 };
@@ -219,16 +232,35 @@ const JSPropertySpec js::Float32x4Defn::
     JS_PS_END
 };
 
 const JSFunctionSpec js::Float32x4Defn::TypedObjectMethods[] = {
     JS_SELF_HOSTED_FN("toSource", "SimdToSource", 0, 0),
     JS_FS_END
 };
 
+const JSFunctionSpec js::Float64x2Defn::TypeDescriptorMethods[] = {
+    JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
+    JS_SELF_HOSTED_FN("array", "ArrayShorthand", 1, 0),
+    JS_SELF_HOSTED_FN("equivalent", "TypeDescrEquivalent", 1, 0),
+    JS_FS_END
+};
+
+const JSPropertySpec js::Float64x2Defn::TypedObjectProperties[] = {
+    JS_PSG("x", Float64x2Lane0, JSPROP_PERMANENT),
+    JS_PSG("y", Float64x2Lane1, JSPROP_PERMANENT),
+    JS_PSG("signMask", Float64x2SignMask, JSPROP_PERMANENT),
+    JS_PS_END
+};
+
+const JSFunctionSpec js::Float64x2Defn::TypedObjectMethods[] = {
+    JS_SELF_HOSTED_FN("toSource", "SimdToSource", 0, 0),
+    JS_FS_END
+};
+
 const JSFunctionSpec js::Int32x4Defn::TypeDescriptorMethods[] = {
     JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
     JS_SELF_HOSTED_FN("array", "ArrayShorthand", 1, 0),
     JS_SELF_HOSTED_FN("equivalent", "TypeDescrEquivalent", 1, 0),
     JS_FS_END,
 };
 
 const JSPropertySpec js::Int32x4Defn::TypedObjectProperties[] = {
@@ -263,16 +295,17 @@ CreateSimdClass(JSContext *cx, Handle<Gl
         return nullptr;
 
     typeDescr->initReservedSlot(JS_DESCR_SLOT_KIND, Int32Value(type::Simd));
     typeDescr->initReservedSlot(JS_DESCR_SLOT_STRING_REPR, StringValue(stringRepr));
     typeDescr->initReservedSlot(JS_DESCR_SLOT_ALIGNMENT, Int32Value(SimdTypeDescr::alignment(type)));
     typeDescr->initReservedSlot(JS_DESCR_SLOT_SIZE, Int32Value(SimdTypeDescr::size(type)));
     typeDescr->initReservedSlot(JS_DESCR_SLOT_OPAQUE, BooleanValue(false));
     typeDescr->initReservedSlot(JS_DESCR_SLOT_TYPE, Int32Value(T::type));
+    typeDescr->initReservedSlot(JS_DESCR_SLOT_LANES, Int32Value(SimdTypeDescr::lanes(type)));
 
     if (!CreateUserSizeAndAlignmentProperties(cx, typeDescr))
         return nullptr;
 
     // Create prototype property, which inherits from Object.prototype.
 
     RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx));
     if (!objProto)
@@ -293,16 +326,28 @@ CreateSimdClass(JSContext *cx, Handle<Gl
                                       T::TypedObjectMethods))
     {
         return nullptr;
     }
 
     return typeDescr;
 }
 
+const char*
+SimdTypeToMinimumLanesNumber(SimdTypeDescr &descr) {
+    switch (descr.type()) {
+      case SimdTypeDescr::TYPE_INT32:
+      case SimdTypeDescr::TYPE_FLOAT32:
+        return "3";
+      case SimdTypeDescr::TYPE_FLOAT64:
+        return "1";
+    }
+    MOZ_CRASH("Unexpected SIMD type description.");
+}
+
 bool
 SimdTypeDescr::call(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     Rooted<SimdTypeDescr*> descr(cx, &args.callee().as<SimdTypeDescr>());
     if (args.length() == 1) {
         // SIMD type used as a coercion
@@ -331,16 +376,23 @@ SimdTypeDescr::call(JSContext *cx, unsig
       case SimdTypeDescr::TYPE_FLOAT32: {
         float *mem = reinterpret_cast<float*>(result->typedMem());
         for (unsigned i = 0; i < 4; i++) {
             if (!RoundFloat32(cx, args.get(i), &mem[i]))
                 return false;
         }
         break;
       }
+      case SimdTypeDescr::TYPE_FLOAT64: {
+        double *mem = reinterpret_cast<double*>(result->typedMem());
+        for (unsigned i = 0; i < 2; i++) {
+            if (!ToNumber(cx, args[i], &mem[i]))
+                return false;
+        }
+      }
     }
     args.rval().setObject(*result);
     return true;
 }
 
 ///////////////////////////////////////////////////////////////////////////
 // SIMD class
 
@@ -381,16 +433,33 @@ SIMDObject::initClass(JSContext *cx, Han
     if (!JS_DefineFunctions(cx, float32x4Object, Float32x4Methods) ||
         !DefineProperty(cx, SIMD, cx->names().float32x4,
                         float32x4Value, nullptr, nullptr,
                         JSPROP_READONLY | JSPROP_PERMANENT))
     {
         return nullptr;
     }
 
+    // float64x2
+    RootedObject float64x2Object(cx);
+    float64x2Object = CreateSimdClass<Float64x2Defn>(cx, global,
+                                                     cx->names().float64x2);
+    if (!float64x2Object)
+        return nullptr;
+
+    // Define float64x2 functions and install as a property of the SIMD object.
+    RootedValue float64x2Value(cx, ObjectValue(*float64x2Object));
+    if (!JS_DefineFunctions(cx, float64x2Object, Float64x2Methods) ||
+        !DefineProperty(cx, SIMD, cx->names().float64x2,
+                        float64x2Value, nullptr, nullptr,
+                        JSPROP_READONLY | JSPROP_PERMANENT))
+    {
+        return nullptr;
+    }
+
     // int32x4
     RootedObject int32x4Object(cx);
     int32x4Object = CreateSimdClass<Int32x4Defn>(cx, global,
                                                  cx->names().int32x4);
     if (!int32x4Object)
         return nullptr;
 
     // Define int32x4 functions and install as a property of the SIMD object.
@@ -406,16 +475,17 @@ SIMDObject::initClass(JSContext *cx, Han
     RootedValue SIMDValue(cx, ObjectValue(*SIMD));
 
     // Everything is set up, install SIMD on the global object.
     if (!DefineProperty(cx, global, cx->names().SIMD, SIMDValue, nullptr, nullptr, 0))
         return nullptr;
 
     global->setConstructor(JSProto_SIMD, SIMDValue);
     global->setFloat32x4TypeDescr(*float32x4Object);
+    global->setFloat64x2TypeDescr(*float64x2Object);
     global->setInt32x4TypeDescr(*int32x4Object);
     return SIMD;
 }
 
 JSObject *
 js_InitSIMDClass(JSContext *cx, HandleObject obj)
 {
     MOZ_ASSERT(obj->is<GlobalObject>());
@@ -436,16 +506,17 @@ js::CreateSimd(JSContext *cx, typename V
         return nullptr;
 
     Elem *resultMem = reinterpret_cast<Elem *>(result->typedMem());
     memcpy(resultMem, data, sizeof(Elem) * V::lanes);
     return result;
 }
 
 template JSObject *js::CreateSimd<Float32x4>(JSContext *cx, Float32x4::Elem *data);
+template JSObject *js::CreateSimd<Float64x2>(JSContext *cx, Float64x2::Elem *data);
 template JSObject *js::CreateSimd<Int32x4>(JSContext *cx, Int32x4::Elem *data);
 
 namespace js {
 // Unary SIMD operators
 template<typename T>
 struct Abs {
     static inline T apply(T x) { return mozilla::Abs(x); }
 };
@@ -762,18 +833,20 @@ CompareFunc(JSContext *cx, unsigned argc
 
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() != 2 || !IsVectorObject<In>(args[0]) || !IsVectorObject<In>(args[1]))
         return ErrorBadArgs(cx);
 
     int32_t result[Int32x4::lanes];
     InElem *left = TypedObjectMemory<InElem *>(args[0]);
     InElem *right = TypedObjectMemory<InElem *>(args[1]);
-    for (unsigned i = 0; i < Int32x4::lanes; i++)
-        result[i] = Op<InElem>::apply(left[i], right[i]);
+    for (unsigned i = 0; i < Int32x4::lanes; i++) {
+        unsigned j = (i * In::lanes) / Int32x4::lanes;
+        result[i] = Op<InElem>::apply(left[j], right[j]);
+    }
 
     return StoreResult<Int32x4>(cx, args, result);
 }
 
 template<typename V, typename Vret>
 static bool
 FuncConvert(JSContext *cx, unsigned argc, Value *vp)
 {
@@ -782,17 +855,18 @@ FuncConvert(JSContext *cx, unsigned argc
 
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() != 1 || !IsVectorObject<V>(args[0]))
         return ErrorBadArgs(cx);
 
     Elem *val = TypedObjectMemory<Elem *>(args[0]);
     RetElem result[Vret::lanes];
     for (unsigned i = 0; i < Vret::lanes; i++)
-        result[i] = ConvertScalar<RetElem>(val[i]);
+        result[i] = i < V::lanes ? ConvertScalar<RetElem>(val[i]) : 0;
+
     return StoreResult<Vret>(cx, args, result);
 }
 
 template<typename V, typename Vret>
 static bool
 FuncConvertBits(JSContext *cx, unsigned argc, Value *vp)
 {
     typedef typename Vret::Elem RetElem;
@@ -853,37 +927,39 @@ Int32x4Bool(JSContext *cx, unsigned argc
     }
 
     int32_t result[Int32x4::lanes];
     for (unsigned i = 0; i < Int32x4::lanes; i++)
         result[i] = args[i].toBoolean() ? 0xFFFFFFFF : 0x0;
     return StoreResult<Int32x4>(cx, args, result);
 }
 
+template<typename In>
 static bool
-Float32x4Clamp(JSContext *cx, unsigned argc, Value *vp)
+Clamp(JSContext *cx, unsigned argc, Value *vp)
 {
+    typedef typename In::Elem InElem;
     CallArgs args = CallArgsFromVp(argc, vp);
-    if (args.length() != 3 || !IsVectorObject<Float32x4>(args[0]) ||
-        !IsVectorObject<Float32x4>(args[1]) || !IsVectorObject<Float32x4>(args[2]))
+    if (args.length() != 3 || !IsVectorObject<In>(args[0]) ||
+        !IsVectorObject<In>(args[1]) || !IsVectorObject<In>(args[2]))
     {
         return ErrorBadArgs(cx);
     }
 
-    float *val = TypedObjectMemory<float *>(args[0]);
-    float *lowerLimit = TypedObjectMemory<float *>(args[1]);
-    float *upperLimit = TypedObjectMemory<float *>(args[2]);
+    InElem *val = TypedObjectMemory<InElem *>(args[0]);
+    InElem *lowerLimit = TypedObjectMemory<InElem *>(args[1]);
+    InElem *upperLimit = TypedObjectMemory<InElem *>(args[2]);
 
-    float result[Float32x4::lanes];
-    for (unsigned i = 0; i < Float32x4::lanes; i++) {
+    InElem result[In::lanes];
+    for (unsigned i = 0; i < In::lanes; i++) {
         result[i] = val[i] < lowerLimit[i] ? lowerLimit[i] : val[i];
         result[i] = result[i] > upperLimit[i] ? upperLimit[i] : result[i];
     }
 
-    return StoreResult<Float32x4>(cx, args, result);
+    return StoreResult<In>(cx, args, result);
 }
 
 template<typename V>
 static bool
 BitSelect(JSContext *cx, unsigned argc, Value *vp)
 {
     typedef typename V::Elem Elem;
 
@@ -982,18 +1058,17 @@ Load(JSContext *cx, unsigned argc, Value
 
     Rooted<TypeDescr*> typeDescr(cx, &V::GetTypeDescr(*cx->global()));
     MOZ_ASSERT(typeDescr);
     Rooted<TypedObject *> result(cx, OutlineTypedObject::createZeroed(cx, typeDescr, 0));
     if (!result)
         return false;
 
     Elem *dest = reinterpret_cast<Elem*>(result->typedMem());
-    for (unsigned i = 0; i < NumElem; i++)
-        dest[i] = typedArrayData[i];
+    memcpy(dest, typedArrayData, sizeof(Elem) * NumElem);
 
     args.rval().setObject(*result);
     return true;
 }
 
 template<class V, unsigned NumElem>
 static bool
 Store(JSContext *cx, unsigned argc, Value *vp)
@@ -1007,31 +1082,39 @@ Store(JSContext *cx, unsigned argc, Valu
     Elem *typedArrayData = nullptr;
     if (!TypedArrayDataPtrFromArgs<Elem, NumElem>(cx, args, &typedArrayData))
         return false;
 
     if (!IsVectorObject<V>(args[2]))
         return ErrorBadArgs(cx);
 
     Elem *src = TypedObjectMemory<Elem*>(args[2]);
-    for (unsigned i = 0; i < NumElem; i++)
-        typedArrayData[i] = src[i];
+    memcpy(typedArrayData, src, sizeof(Elem) * NumElem);
 
     args.rval().setObject(args[2].toObject());
     return true;
 }
 
 #define DEFINE_SIMD_FLOAT32X4_FUNCTION(Name, Func, Operands, Flags) \
 bool                                                                \
 js::simd_float32x4_##Name(JSContext *cx, unsigned argc, Value *vp)  \
 {                                                                   \
     return Func(cx, argc, vp);                                      \
 }
 FLOAT32X4_FUNCTION_LIST(DEFINE_SIMD_FLOAT32X4_FUNCTION)
-#undef DEFINE_SIMD_FLOAT32x4_FUNCTION
+#undef DEFINE_SIMD_FLOAT32X4_FUNCTION
+
+#define DEFINE_SIMD_FLOAT64X2_FUNCTION(Name, Func, Operands, Flags) \
+bool                                                                \
+js::simd_float64x2_##Name(JSContext *cx, unsigned argc, Value *vp)  \
+{                                                                   \
+    return Func(cx, argc, vp);                                      \
+}
+FLOAT64X2_FUNCTION_LIST(DEFINE_SIMD_FLOAT64X2_FUNCTION)
+#undef DEFINE_SIMD_FLOAT64X2_FUNCTION
 
 #define DEFINE_SIMD_INT32X4_FUNCTION(Name, Func, Operands, Flags)   \
 bool                                                                \
 js::simd_int32x4_##Name(JSContext *cx, unsigned argc, Value *vp)    \
 {                                                                   \
     return Func(cx, argc, vp);                                      \
 }
 INT32X4_FUNCTION_LIST(DEFINE_SIMD_INT32X4_FUNCTION)
@@ -1040,15 +1123,23 @@ INT32X4_FUNCTION_LIST(DEFINE_SIMD_INT32X
 const JSFunctionSpec js::Float32x4Methods[] = {
 #define SIMD_FLOAT32X4_FUNCTION_ITEM(Name, Func, Operands, Flags)   \
         JS_FN(#Name, js::simd_float32x4_##Name, Operands, Flags),
         FLOAT32X4_FUNCTION_LIST(SIMD_FLOAT32X4_FUNCTION_ITEM)
 #undef SIMD_FLOAT32x4_FUNCTION_ITEM
         JS_FS_END
 };
 
+const JSFunctionSpec js::Float64x2Methods[] = {
+#define SIMD_FLOAT64X2_FUNCTION_ITEM(Name, Func, Operands, Flags)       \
+        JS_FN(#Name, js::simd_float64x2_##Name, Operands, Flags),
+        FLOAT64X2_FUNCTION_LIST(SIMD_FLOAT64X2_FUNCTION_ITEM)
+#undef SIMD_FLOAT64X2_FUNCTION_ITEM
+        JS_FS_END
+};
+
 const JSFunctionSpec js::Int32x4Methods[] = {
 #define SIMD_INT32X4_FUNCTION_ITEM(Name, Func, Operands, Flags)     \
         JS_FN(#Name, js::simd_int32x4_##Name, Operands, Flags),
         INT32X4_FUNCTION_LIST(SIMD_INT32X4_FUNCTION_ITEM)
 #undef SIMD_INT32X4_FUNCTION_ITEM
         JS_FS_END
 };
--- a/js/src/builtin/SIMD.h
+++ b/js/src/builtin/SIMD.h
@@ -17,16 +17,18 @@
 /*
  * JS SIMD functions.
  * Spec matching polyfill:
  * https://github.com/johnmccutchan/ecmascript_simd/blob/master/src/ecmascript_simd.js
  */
 
 #define FLOAT32X4_UNARY_FUNCTION_LIST(V)                                            \
   V(abs, (UnaryFunc<Float32x4, Abs, Float32x4>), 1, 0)                              \
+  V(fromFloat64x2, (FuncConvert<Float64x2, Float32x4> ), 1, 0)                      \
+  V(fromFloat64x2Bits, (FuncConvertBits<Float64x2, Float32x4>), 1, 0)               \
   V(fromInt32x4, (FuncConvert<Int32x4, Float32x4> ), 1, 0)                          \
   V(fromInt32x4Bits, (FuncConvertBits<Int32x4, Float32x4>), 1, 0)                   \
   V(neg, (UnaryFunc<Float32x4, Neg, Float32x4>), 1, 0)                              \
   V(not, (CoercedUnaryFunc<Float32x4, Int32x4, Not, Float32x4>), 1, 0)              \
   V(reciprocal, (UnaryFunc<Float32x4, Rec, Float32x4>), 1, 0)                       \
   V(reciprocalSqrt, (UnaryFunc<Float32x4, RecSqrt, Float32x4>), 1, 0)               \
   V(splat, (FuncSplat<Float32x4>), 1, 0)                                            \
   V(sqrt, (UnaryFunc<Float32x4, Sqrt, Float32x4>), 1, 0)
@@ -59,32 +61,83 @@
   V(withX, (FuncWith<Float32x4, WithX>), 2, 0)                                      \
   V(withY, (FuncWith<Float32x4, WithY>), 2, 0)                                      \
   V(withZ, (FuncWith<Float32x4, WithZ>), 2, 0)                                      \
   V(withW, (FuncWith<Float32x4, WithW>), 2, 0)                                      \
   V(xor, (CoercedBinaryFunc<Float32x4, Int32x4, Xor, Float32x4>), 2, 0)
 
 #define FLOAT32X4_TERNARY_FUNCTION_LIST(V)                                          \
   V(bitselect, BitSelect<Float32x4>, 3, 0)                                          \
-  V(clamp, Float32x4Clamp, 3, 0)                                                    \
+  V(clamp, Clamp<Float32x4>, 3, 0)                                                  \
   V(select, Select<Float32x4>, 3, 0)
 
 #define FLOAT32X4_SHUFFLE_FUNCTION_LIST(V)                                          \
   V(swizzle, Swizzle<Float32x4>, 2, 0)                                              \
   V(shuffle, Shuffle<Float32x4>, 3, 0)
 
 #define FLOAT32X4_FUNCTION_LIST(V)                                                  \
   FLOAT32X4_UNARY_FUNCTION_LIST(V)                                                  \
   FLOAT32X4_BINARY_FUNCTION_LIST(V)                                                 \
   FLOAT32X4_TERNARY_FUNCTION_LIST(V)                                                \
   FLOAT32X4_SHUFFLE_FUNCTION_LIST(V)
 
+#define FLOAT64X2_UNARY_FUNCTION_LIST(V)                                            \
+  V(abs, (UnaryFunc<Float64x2, Abs, Float64x2>), 1, 0)                              \
+  V(fromFloat32x4, (FuncConvert<Float32x4, Float64x2> ), 1, 0)                      \
+  V(fromFloat32x4Bits, (FuncConvertBits<Float32x4, Float64x2>), 1, 0)               \
+  V(fromInt32x4, (FuncConvert<Int32x4, Float64x2> ), 1, 0)                          \
+  V(fromInt32x4Bits, (FuncConvertBits<Int32x4, Float64x2>), 1, 0)                   \
+  V(neg, (UnaryFunc<Float64x2, Neg, Float64x2>), 1, 0)                              \
+  V(reciprocal, (UnaryFunc<Float64x2, Rec, Float64x2>), 1, 0)                       \
+  V(reciprocalSqrt, (UnaryFunc<Float64x2, RecSqrt, Float64x2>), 1, 0)               \
+  V(splat, (FuncSplat<Float64x2>), 1, 0)                                            \
+  V(sqrt, (UnaryFunc<Float64x2, Sqrt, Float64x2>), 1, 0)
+
+#define FLOAT64X2_BINARY_FUNCTION_LIST(V)                                           \
+  V(add, (BinaryFunc<Float64x2, Add, Float64x2>), 2, 0)                             \
+  V(div, (BinaryFunc<Float64x2, Div, Float64x2>), 2, 0)                             \
+  V(equal, (CompareFunc<Float64x2, Equal>), 2, 0)                                   \
+  V(greaterThan, (CompareFunc<Float64x2, GreaterThan>), 2, 0)                       \
+  V(greaterThanOrEqual, (CompareFunc<Float64x2, GreaterThanOrEqual>), 2, 0)         \
+  V(lessThan, (CompareFunc<Float64x2, LessThan>), 2, 0)                             \
+  V(lessThanOrEqual, (CompareFunc<Float64x2, LessThanOrEqual>), 2, 0)               \
+  V(load,    (Load<Float64x2, 2>), 2, 0)                                            \
+  V(loadX,   (Load<Float64x2, 1>), 2, 0)                                            \
+  V(max, (BinaryFunc<Float64x2, Maximum, Float64x2>), 2, 0)                         \
+  V(maxNum, (BinaryFunc<Float64x2, MaxNum, Float64x2>), 2, 0)                       \
+  V(min, (BinaryFunc<Float64x2, Minimum, Float64x2>), 2, 0)                         \
+  V(minNum, (BinaryFunc<Float64x2, MinNum, Float64x2>), 2, 0)                       \
+  V(mul, (BinaryFunc<Float64x2, Mul, Float64x2>), 2, 0)                             \
+  V(notEqual, (CompareFunc<Float64x2, NotEqual>), 2, 0)                             \
+  V(store,    (Store<Float64x2, 2>), 3, 0)                                          \
+  V(storeX,   (Store<Float64x2, 1>), 3, 0)                                          \
+  V(sub, (BinaryFunc<Float64x2, Sub, Float64x2>), 2, 0)                             \
+  V(withX, (FuncWith<Float64x2, WithX>), 2, 0)                                      \
+  V(withY, (FuncWith<Float64x2, WithY>), 2, 0)
+
+#define FLOAT64X2_TERNARY_FUNCTION_LIST(V)                                          \
+  V(bitselect, BitSelect<Float64x2>, 3, 0)                                          \
+  V(clamp, Clamp<Float64x2>, 3, 0)                                                  \
+  V(select, Select<Float64x2>, 3, 0)
+
+#define FLOAT64X2_SHUFFLE_FUNCTION_LIST(V)                                          \
+  V(swizzle, Swizzle<Float64x2>, 2, 0)                                              \
+  V(shuffle, Shuffle<Float64x2>, 3, 0)
+
+#define FLOAT64X2_FUNCTION_LIST(V)                                                  \
+  FLOAT64X2_UNARY_FUNCTION_LIST(V)                                                  \
+  FLOAT64X2_BINARY_FUNCTION_LIST(V)                                                 \
+  FLOAT64X2_TERNARY_FUNCTION_LIST(V)                                                \
+  FLOAT64X2_SHUFFLE_FUNCTION_LIST(V)
+
 #define INT32X4_UNARY_FUNCTION_LIST(V)                                              \
   V(fromFloat32x4, (FuncConvert<Float32x4, Int32x4>), 1, 0)                         \
   V(fromFloat32x4Bits, (FuncConvertBits<Float32x4, Int32x4>), 1, 0)                 \
+  V(fromFloat64x2, (FuncConvert<Float64x2, Int32x4>), 1, 0)                         \
+  V(fromFloat64x2Bits, (FuncConvertBits<Float64x2, Int32x4>), 1, 0)                 \
   V(neg, (UnaryFunc<Int32x4, Neg, Int32x4>), 1, 0)                                  \
   V(not, (UnaryFunc<Int32x4, Not, Int32x4>), 1, 0)                                  \
   V(splat, (FuncSplat<Int32x4>), 0, 0)
 
 #define INT32X4_BINARY_FUNCTION_LIST(V)                                             \
   V(add, (BinaryFunc<Int32x4, Add, Int32x4>), 2, 0)                                 \
   V(and, (BinaryFunc<Int32x4, And, Int32x4>), 2, 0)                                 \
   V(equal, (CompareFunc<Int32x4, Equal>), 2, 0)                                     \
@@ -207,16 +260,36 @@ struct Float32x4 {
         *out = v.toNumber();
         return true;
     }
     static void setReturn(CallArgs &args, Elem value) {
         args.rval().setDouble(JS::CanonicalizeNaN(value));
     }
 };
 
+struct Float64x2 {
+    typedef double Elem;
+    static const unsigned lanes = 2;
+    static const SimdTypeDescr::Type type = SimdTypeDescr::TYPE_FLOAT64;
+
+    static TypeDescr &GetTypeDescr(GlobalObject &global) {
+        return global.float64x2TypeDescr().as<TypeDescr>();
+    }
+    static Elem toType(Elem a) {
+        return a;
+    }
+    static bool toType(JSContext *cx, JS::HandleValue v, Elem *out) {
+        *out = v.toNumber();
+        return true;
+    }
+    static void setReturn(CallArgs &args, Elem value) {
+        args.rval().setDouble(JS::CanonicalizeNaN(value));
+    }
+};
+
 struct Int32x4 {
     typedef int32_t Elem;
     static const unsigned lanes = 4;
     static const SimdTypeDescr::Type type = SimdTypeDescr::TYPE_INT32;
 
     static TypeDescr &GetTypeDescr(GlobalObject &global) {
         return global.int32x4TypeDescr().as<TypeDescr>();
     }
@@ -241,16 +314,22 @@ template<typename V>
 bool ToSimdConstant(JSContext *cx, HandleValue v, jit::SimdConstant *out);
 
 #define DECLARE_SIMD_FLOAT32X4_FUNCTION(Name, Func, Operands, Flags) \
 extern bool                                                          \
 simd_float32x4_##Name(JSContext *cx, unsigned argc, Value *vp);
 FLOAT32X4_FUNCTION_LIST(DECLARE_SIMD_FLOAT32X4_FUNCTION)
 #undef DECLARE_SIMD_FLOAT32X4_FUNCTION
 
+#define DECLARE_SIMD_FLOAT64X2_FUNCTION(Name, Func, Operands, Flags) \
+extern bool                                                          \
+simd_float64x2_##Name(JSContext *cx, unsigned argc, Value *vp);
+FLOAT64X2_FUNCTION_LIST(DECLARE_SIMD_FLOAT64X2_FUNCTION)
+#undef DECLARE_SIMD_FLOAT64X2_FUNCTION
+
 #define DECLARE_SIMD_INT32x4_FUNCTION(Name, Func, Operands, Flags)   \
 extern bool                                                          \
 simd_int32x4_##Name(JSContext *cx, unsigned argc, Value *vp);
 INT32X4_FUNCTION_LIST(DECLARE_SIMD_INT32x4_FUNCTION)
 #undef DECLARE_SIMD_INT32x4_FUNCTION
 
 }  /* namespace js */
 
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -401,40 +401,53 @@ js::ReferenceTypeDescr::call(JSContext *
         return true;
       }
     }
 
     MOZ_CRASH("Unhandled Reference type");
 }
 
 /***************************************************************************
- * X4 type objects
+ * SIMD type objects
  *
  * Note: these are partially defined in SIMD.cpp
  */
 
 static const int32_t SimdSizes[] = {
-#define SIMD_SIZE(_kind, _type, _name)                        \
-    sizeof(_type) * 4,
+#define SIMD_SIZE(_kind, _type, _name, _lanes)                 \
+    sizeof(_type) * _lanes,
     JS_FOR_EACH_SIMD_TYPE_REPR(SIMD_SIZE) 0
 #undef SIMD_SIZE
 };
 
+static int32_t SimdLanes[] = {
+#define SIMD_LANE(_kind, _type, _name, _lanes)                 \
+    _lanes,
+    JS_FOR_EACH_SIMD_TYPE_REPR(SIMD_LANE) 0
+#undef SIMD_LANE
+};
+
 int32_t
 SimdTypeDescr::size(Type t)
 {
     return SimdSizes[t];
 }
 
 int32_t
 SimdTypeDescr::alignment(Type t)
 {
     return SimdSizes[t];
 }
 
+int32_t
+SimdTypeDescr::lanes(Type t)
+{
+    return SimdLanes[t];
+}
+
 /***************************************************************************
  * ArrayMetaTypeDescr class
  */
 
 /*
  * For code like:
  *
  *   var A = new TypedObject.ArrayType(uint8, 10);
@@ -2690,16 +2703,26 @@ js::GetFloat32x4TypeDescr(JSContext *cx,
     CallArgs args = CallArgsFromVp(argc, vp);
     Rooted<GlobalObject*> global(cx, cx->global());
     MOZ_ASSERT(global);
     args.rval().setObject(global->float32x4TypeDescr());
     return true;
 }
 
 bool
+js::GetFloat64x2TypeDescr(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    Rooted<GlobalObject*> global(cx, cx->global());
+    MOZ_ASSERT(global);
+    args.rval().setObject(global->float64x2TypeDescr());
+    return true;
+}
+
+bool
 js::GetInt32x4TypeDescr(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     Rooted<GlobalObject*> global(cx, cx->global());
     MOZ_ASSERT(global);
     args.rval().setObject(global->int32x4TypeDescr());
     return true;
 }
--- a/js/src/builtin/TypedObject.h
+++ b/js/src/builtin/TypedObject.h
@@ -320,43 +320,46 @@ class ComplexTypeDescr : public TypeDesc
     // Returns the prototype that instances of this type descriptor
     // will have.
     TypedProto &instancePrototype() const {
         return getReservedSlot(JS_DESCR_SLOT_TYPROTO).toObject().as<TypedProto>();
     }
 };
 
 /*
- * Type descriptors `float32x4` and `int32x4`
+ * Type descriptors `float32x4`, `int32x4` and `float64x2`
  */
 class SimdTypeDescr : public ComplexTypeDescr
 {
   public:
     enum Type {
         TYPE_INT32 = JS_SIMDTYPEREPR_INT32,
         TYPE_FLOAT32 = JS_SIMDTYPEREPR_FLOAT32,
+        TYPE_FLOAT64 = JS_SIMDTYPEREPR_FLOAT64
     };
 
     static const type::Kind Kind = type::Simd;
     static const bool Opaque = false;
     static const Class class_;
     static int32_t size(Type t);
     static int32_t alignment(Type t);
+    static int32_t lanes(Type t);
 
     SimdTypeDescr::Type type() const {
         return (SimdTypeDescr::Type) getReservedSlot(JS_DESCR_SLOT_TYPE).toInt32();
     }
 
     static bool call(JSContext *cx, unsigned argc, Value *vp);
     static bool is(const Value &v);
 };
 
 #define JS_FOR_EACH_SIMD_TYPE_REPR(macro_)                             \
-    macro_(SimdTypeDescr::TYPE_INT32, int32_t, int32)                  \
-    macro_(SimdTypeDescr::TYPE_FLOAT32, float, float32)
+    macro_(SimdTypeDescr::TYPE_INT32, int32_t, int32, 4)               \
+    macro_(SimdTypeDescr::TYPE_FLOAT32, float, float32, 4)             \
+    macro_(SimdTypeDescr::TYPE_FLOAT64, double, float64, 2)
 
 bool IsTypedObjectClass(const Class *clasp); // Defined below
 bool IsTypedObjectArray(JSObject& obj);
 
 bool CreateUserSizeAndAlignmentProperties(JSContext *cx, HandleTypeDescr obj);
 
 class ArrayTypeDescr;
 
@@ -876,16 +879,24 @@ bool GetTypedObjectModule(JSContext *cx,
  * Usage: GetFloat32x4TypeDescr()
  *
  * Returns the float32x4 type object. SIMD pseudo-module must have
  * been initialized for this to be safe.
  */
 bool GetFloat32x4TypeDescr(JSContext *cx, unsigned argc, Value *vp);
 
 /*
+ * Usage: GetFloat64x2TypeDescr()
+ *
+ * Returns the float64x2 type object. SIMD pseudo-module must have
+ * been initialized for this to be safe.
+ */
+bool GetFloat64x2TypeDescr(JSContext *cx, unsigned argc, Value *vp);
+
+/*
  * Usage: GetInt32x4TypeDescr()
  *
  * Returns the int32x4 type object. SIMD pseudo-module must have
  * been initialized for this to be safe.
  */
 bool GetInt32x4TypeDescr(JSContext *cx, unsigned argc, Value *vp);
 
 /*
--- a/js/src/builtin/TypedObject.js
+++ b/js/src/builtin/TypedObject.js
@@ -147,16 +147,21 @@ function TypedObjectGetSimd(descr, typed
   switch (type) {
   case JS_SIMDTYPEREPR_FLOAT32:
     var x = Load_float32(typedObj, offset + 0);
     var y = Load_float32(typedObj, offset + 4);
     var z = Load_float32(typedObj, offset + 8);
     var w = Load_float32(typedObj, offset + 12);
     return GetFloat32x4TypeDescr()(x, y, z, w);
 
+  case JS_SIMDTYPEREPR_FLOAT64:
+    var x = Load_float64(typedObj, offset + 0);
+    var y = Load_float64(typedObj, offset + 8);
+    return GetFloat64x2TypeDescr()(x, y);
+
   case JS_SIMDTYPEREPR_INT32:
     var x = Load_int32(typedObj, offset + 0);
     var y = Load_int32(typedObj, offset + 4);
     var z = Load_int32(typedObj, offset + 8);
     var w = Load_int32(typedObj, offset + 12);
     return GetInt32x4TypeDescr()(x, y, z, w);
   }
 
@@ -317,16 +322,20 @@ function TypedObjectSetSimd(descr, typed
   var type = DESCR_TYPE(descr);
   switch (type) {
     case JS_SIMDTYPEREPR_FLOAT32:
       Store_float32(typedObj, offset + 0, Load_float32(fromValue, 0));
       Store_float32(typedObj, offset + 4, Load_float32(fromValue, 4));
       Store_float32(typedObj, offset + 8, Load_float32(fromValue, 8));
       Store_float32(typedObj, offset + 12, Load_float32(fromValue, 12));
       break;
+    case JS_SIMDTYPEREPR_FLOAT64:
+      Store_float64(typedObj, offset + 0, Load_float64(fromValue, 0));
+      Store_float64(typedObj, offset + 8, Load_float64(fromValue, 8));
+      break;
     case JS_SIMDTYPEREPR_INT32:
       Store_int32(typedObj, offset + 0, Load_int32(fromValue, 0));
       Store_int32(typedObj, offset + 4, Load_int32(fromValue, 4));
       Store_int32(typedObj, offset + 8, Load_int32(fromValue, 8));
       Store_int32(typedObj, offset + 12, Load_int32(fromValue, 12));
       break;
     default:
       assert(false, "Unhandled Simd type: " + type);
@@ -452,33 +461,53 @@ function TypedObjectArrayRedimension(new
 // SIMD
 
 function SimdProtoString(type) {
   switch (type) {
   case JS_SIMDTYPEREPR_INT32:
     return "int32x4";
   case JS_SIMDTYPEREPR_FLOAT32:
     return "float32x4";
+  case JS_SIMDTYPEREPR_FLOAT64:
+    return "float64x2";
+  }
+
+  assert(false, "Unhandled type constant");
+  return undefined;
+}
+
+function SimdTypeToLength(type) {
+  switch (type) {
+  case JS_SIMDTYPEREPR_INT32:
+  case JS_SIMDTYPEREPR_FLOAT32:
+    return 4;
+  case JS_SIMDTYPEREPR_FLOAT64:
+    return 2;
   }
 
   assert(false, "Unhandled type constant");
   return undefined;
 }
 
 function SimdToSource() {
   if (!IsObject(this) || !ObjectIsTypedObject(this))
     ThrowError(JSMSG_INCOMPATIBLE_PROTO, "SIMD", "toSource", typeof this);
 
   var descr = TypedObjectTypeDescr(this);
 
   if (DESCR_KIND(descr) != JS_TYPEREPR_SIMD_KIND)
     ThrowError(JSMSG_INCOMPATIBLE_PROTO, "SIMD", "toSource", typeof this);
 
   var type = DESCR_TYPE(descr);
-  return SimdProtoString(type)+"("+this.x+", "+this.y+", "+this.z+", "+this.w+")";
+  var protoString = SimdProtoString(type);
+  var length = SimdTypeToLength(type);
+  if (length == 4)
+    return protoString+"("+this.x+", "+this.y+", "+this.z+", "+this.w+")";
+  else if (length == 2)
+    return protoString+"("+this.x+", "+this.y+")";
 }
 
 ///////////////////////////////////////////////////////////////////////////
 // Miscellaneous
 
 function DescrsEquiv(descr1, descr2) {
   assert(IsObject(descr1) && ObjectIsTypeDescr(descr1), "descr1 not descr");
   assert(IsObject(descr2) && ObjectIsTypeDescr(descr2), "descr2 not descr");
--- a/js/src/builtin/TypedObjectConstants.h
+++ b/js/src/builtin/TypedObjectConstants.h
@@ -27,18 +27,19 @@
 #define JS_DESCR_SLOT_STRING_REPR        1  // Atomized string representation
 #define JS_DESCR_SLOT_ALIGNMENT          2  // Alignment in bytes
 #define JS_DESCR_SLOT_SIZE               3  // Size in bytes, else 0
 #define JS_DESCR_SLOT_OPAQUE             4  // Atomized string representation
 #define JS_DESCR_SLOT_TYPROTO            5  // Prototype for instances, if any
 #define JS_DESCR_SLOT_ARRAYPROTO         6  // Lazily created prototype for arrays
 #define JS_DESCR_SLOT_TRACE_LIST         7  // List of references for use in tracing
 
-// Slots on scalars, references, and x4s
+// Slots on scalars, references, and SIMD objects
 #define JS_DESCR_SLOT_TYPE               8  // Type code
+#define JS_DESCR_SLOT_LANES              9
 
 // Slots on array descriptors
 #define JS_DESCR_SLOT_ARRAY_ELEM_TYPE    8
 #define JS_DESCR_SLOT_ARRAY_LENGTH       9
 
 // Slots on struct type objects
 #define JS_DESCR_SLOT_STRUCT_FIELD_NAMES 8
 #define JS_DESCR_SLOT_STRUCT_FIELD_TYPES 9
@@ -80,10 +81,11 @@
 #define JS_REFERENCETYPEREPR_STRING     2
 
 // These constants are for use exclusively in JS code.  In C++ code,
 // prefer SimdTypeRepresentation::TYPE_INT32 etc, since that allows
 // you to write a switch which will receive a warning if you omit a
 // case.
 #define JS_SIMDTYPEREPR_INT32         0
 #define JS_SIMDTYPEREPR_FLOAT32       1
+#define JS_SIMDTYPEREPR_FLOAT64       2
 
 #endif
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/SIMD/clamp.js
@@ -0,0 +1,60 @@
+// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
+var float32x4 = SIMD.float32x4;
+var float64x2 = SIMD.float64x2;
+
+var summary = 'float32x4/float64x2 clamp';
+
+function test() {
+  // FIXME -- Bug 1068028: Amend to check for correctness of NaN/-0 border cases once the semantics are defined.
+
+  var a = float32x4(-20, 10, 30, 0.5);
+  var lower = float32x4(2, 1, 50, 0);
+  var upper = float32x4(2.5, 5, 55, 1);
+  var c = SIMD.float32x4.clamp(a, lower, upper);
+  assertEq(c.x, 2);
+  assertEq(c.y, 5);
+  assertEq(c.z, 50);
+  assertEq(c.w, 0.5);
+
+  var d = float32x4(-13.37, 10.46, 31.79, 0.54);
+  var lower1 = float32x4(2.1, 1.1, 50.13, 0.0);
+  var upper1 = float32x4(2.56, 5.55, 55.93, 1.1);
+  var f = SIMD.float32x4.clamp(d, lower1, upper1);
+  assertEq(f.x, Math.fround(2.1));
+  assertEq(f.y, Math.fround(5.55));
+  assertEq(f.z, Math.fround(50.13));
+  assertEq(f.w, Math.fround(0.54));
+
+  var g = float32x4(4, -Infinity, 10, -10);
+  var lower2 = float32x4(5, -Infinity, -Infinity, -Infinity);
+  var upper2 = float32x4(Infinity, 5, Infinity, Infinity);
+  var i = SIMD.float32x4.clamp(g, lower2, upper2);
+  assertEq(i.x, 5);
+  assertEq(i.y, -Infinity);
+  assertEq(i.z, 10);
+  assertEq(i.w, -10);
+
+  var j = float64x2(-20, 10);
+  var k = float64x2(2.125, 3);
+  var lower3 = float64x2(2, 1);
+  var upper3 = float64x2(2.5, 5);
+  var l = float64x2.clamp(j, lower3, upper3);
+  assertEq(l.x, 2);
+  assertEq(l.y, 5);
+  var m = float64x2.clamp(k, lower3, upper3);
+  assertEq(m.x, 2.125);
+  assertEq(m.y, 3);
+
+  var n = float64x2(-5, 5);
+  var lower4 = float64x2(-Infinity, 0);
+  var upper4 = float64x2(+Infinity, +Infinity);
+  var p = float64x2.clamp(n, lower4, upper4);
+  assertEq(p.x, -5);
+  assertEq(p.y, 5);
+
+  if (typeof reportCompare === "function")
+    reportCompare(true, true);
+}
+
+test();
+
--- a/js/src/tests/ecma_7/SIMD/comparisons.js
+++ b/js/src/tests/ecma_7/SIMD/comparisons.js
@@ -1,60 +1,80 @@
 // |reftest| skip-if(!this.hasOwnProperty("SIMD"))
 
 /*
  * Any copyright is dedicated to the Public Domain.
  * https://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var float32x4 = SIMD.float32x4;
+var float64x2 = SIMD.float64x2;
 var int32x4 = SIMD.int32x4;
 
 var fround = Math.fround;
 
 function boolToSimdLogical(b) {
     return b ? 0xFFFFFFFF | 0 : 0x0;
 }
 
 function testEqualFloat32x4(v, w) {
-    testBinaryFunc(v, w, float32x4.equal, (x, y) => boolToSimdLogical(fround(x) == fround(y)));
+    testBinaryCompare(v, w, float32x4.equal, (x, y) => boolToSimdLogical(fround(x) == fround(y)));
 }
 function testNotEqualFloat32x4(v, w) {
-    testBinaryFunc(v, w, float32x4.notEqual, (x, y) => boolToSimdLogical(fround(x) != fround(y)));
+    testBinaryCompare(v, w, float32x4.notEqual, (x, y) => boolToSimdLogical(fround(x) != fround(y)));
 }
 function testLessThanFloat32x4(v, w) {
-    testBinaryFunc(v, w, float32x4.lessThan, (x, y) => boolToSimdLogical(fround(x) < fround(y)));
+    testBinaryCompare(v, w, float32x4.lessThan, (x, y) => boolToSimdLogical(fround(x) < fround(y)));
 }
 function testLessThanOrEqualFloat32x4(v, w) {
-    testBinaryFunc(v, w, float32x4.lessThanOrEqual, (x, y) => boolToSimdLogical(fround(x) <= fround(y)));
+    testBinaryCompare(v, w, float32x4.lessThanOrEqual, (x, y) => boolToSimdLogical(fround(x) <= fround(y)));
 }
 function testGreaterThanFloat32x4(v, w) {
-    testBinaryFunc(v, w, float32x4.greaterThan, (x, y) => boolToSimdLogical(fround(x) > fround(y)));
+    testBinaryCompare(v, w, float32x4.greaterThan, (x, y) => boolToSimdLogical(fround(x) > fround(y)));
 }
 function testGreaterThanOrEqualFloat32x4(v, w) {
-    testBinaryFunc(v, w, float32x4.greaterThanOrEqual, (x, y) => boolToSimdLogical(fround(x) >= fround(y)));
+    testBinaryCompare(v, w, float32x4.greaterThanOrEqual, (x, y) => boolToSimdLogical(fround(x) >= fround(y)));
+}
+
+function testEqualFloat64x2(v, w) {
+    testBinaryCompare(v, w, float64x2.equal, (x, y) => boolToSimdLogical(x == y));
+}
+function testNotEqualFloat64x2(v, w) {
+    testBinaryCompare(v, w, float64x2.notEqual, (x, y) => boolToSimdLogical(x != y));
+}
+function testLessThanFloat64x2(v, w) {
+    testBinaryCompare(v, w, float64x2.lessThan, (x, y) => boolToSimdLogical(x < y));
+}
+function testLessThanOrEqualFloat64x2(v, w) {
+    testBinaryCompare(v, w, float64x2.lessThanOrEqual, (x, y) => boolToSimdLogical(x <= y));
+}
+function testGreaterThanFloat64x2(v, w) {
+    testBinaryCompare(v, w, float64x2.greaterThan, (x, y) => boolToSimdLogical(x > y));
+}
+function testGreaterThanOrEqualFloat64x2(v, w) {
+    testBinaryCompare(v, w, float64x2.greaterThanOrEqual, (x, y) => boolToSimdLogical(x >= y));
 }
 
 function testEqualInt32x4(v, w) {
-    testBinaryFunc(v, w, int32x4.equal, (x, y) => boolToSimdLogical(x == y));
+    testBinaryCompare(v, w, int32x4.equal, (x, y) => boolToSimdLogical(x == y));
 }
 function testNotEqualInt32x4(v, w) {
-    testBinaryFunc(v, w, int32x4.notEqual, (x, y) => boolToSimdLogical(x != y));
+    testBinaryCompare(v, w, int32x4.notEqual, (x, y) => boolToSimdLogical(x != y));
 }
 function testLessThanInt32x4(v, w) {
-    testBinaryFunc(v, w, int32x4.lessThan, (x, y) => boolToSimdLogical(x < y));
+    testBinaryCompare(v, w, int32x4.lessThan, (x, y) => boolToSimdLogical(x < y));
 }
 function testLessThanOrEqualInt32x4(v, w) {
-    testBinaryFunc(v, w, int32x4.lessThanOrEqual, (x, y) => boolToSimdLogical(x <= y));
+    testBinaryCompare(v, w, int32x4.lessThanOrEqual, (x, y) => boolToSimdLogical(x <= y));
 }
 function testGreaterThanInt32x4(v, w) {
-    testBinaryFunc(v, w, int32x4.greaterThan, (x, y) => boolToSimdLogical(x > y));
+    testBinaryCompare(v, w, int32x4.greaterThan, (x, y) => boolToSimdLogical(x > y));
 }
 function testGreaterThanOrEqualInt32x4(v, w) {
-    testBinaryFunc(v, w, int32x4.greaterThanOrEqual, (x, y) => boolToSimdLogical(x >= y));
+    testBinaryCompare(v, w, int32x4.greaterThanOrEqual, (x, y) => boolToSimdLogical(x >= y));
 }
 
 function test() {
   var float32x4val = [
       float32x4(1, 20, 30, 4),
       float32x4(10, 2, 3, 40),
       float32x4(9.999, 2.1234, 30.4443, 4),
       float32x4(10, 2.1233, 30.4444, 4.0001),
@@ -70,16 +90,42 @@ function test() {
           testNotEqualFloat32x4(v, w);
           testLessThanFloat32x4(v, w);
           testLessThanOrEqualFloat32x4(v, w);
           testGreaterThanFloat32x4(v, w);
           testGreaterThanOrEqualFloat32x4(v, w);
       }
   }
 
+  var float64x2val = [
+      float64x2(1, 20),
+      float64x2(10, 2),
+      float64x2(9.999, 2.1234),
+      float64x2(10, 2.1233),
+      float64x2(30.4443, 4),
+      float64x2(30.4444, 4.0001),
+      float64x2(NaN, -Infinity),
+      float64x2(+Infinity, NaN),
+      float64x2(+Infinity, -0),
+      float64x2(-0, -Infinity),
+      float64x2(13.37, 42.42),
+      float64x2(NaN, 0)
+  ];
+
+  for (v of float64x2val) {
+      for (w of float64x2val) {
+          testEqualFloat64x2(v, w);
+          testNotEqualFloat64x2(v, w);
+          testLessThanFloat64x2(v, w);
+          testLessThanOrEqualFloat64x2(v, w);
+          testGreaterThanFloat64x2(v, w);
+          testGreaterThanOrEqualFloat64x2(v, w);
+      }
+  }
+
   var int32x4val = [
       int32x4(1, 2, 3, 4),
       int32x4(-1, -2, -3, -4),
       int32x4(-1, 2, -3, 4),
       int32x4(1, -2, 3, -4),
       int32x4(INT32_MAX, INT32_MAX, INT32_MIN, INT32_MIN),
       int32x4(INT32_MAX, INT32_MIN, INT32_MAX, INT32_MIN)
   ];
deleted file mode 100644
--- a/js/src/tests/ecma_7/SIMD/float32x4-minmax.js
+++ /dev/null
@@ -1,54 +0,0 @@
-// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
-var BUGNUMBER = 946042;
-
-var float32x4 = SIMD.float32x4;
-
-function testMaxFloat32(v, w) {
-    return testBinaryFunc(v, w, float32x4.max, (x, y) => Math.fround(Math.max(x, y)));
-}
-function testMinFloat32(v, w) {
-    return testBinaryFunc(v, w, float32x4.min, (x, y) => Math.fround(Math.min(x, y)));
-}
-
-function maxNum(x, y) {
-    if (x != x)
-        return y;
-    if (y != y)
-        return x;
-    return Math.max(x, y);
-}
-
-function minNum(x, y) {
-    if (x != x)
-        return y;
-    if (y != y)
-        return x;
-    return Math.min(x, y);
-}
-
-function testMaxNumFloat32(v, w) {
-    return testBinaryFunc(v, w, float32x4.maxNum, maxNum);
-}
-function testMinNumFloat32(v, w) {
-    return testBinaryFunc(v, w, float32x4.minNum, minNum);
-}
-
-function test() {
-  print(BUGNUMBER + ": " + summary);
-
-  for ([v, w] of [[float32x4(1, 20, 30, 4), float32x4(10, 2, 3, 40)],
-                  [float32x4(9.999, 2.1234, 30.4443, 4), float32x4(10, 2.1233, 30.4444, 4.0001)],
-                  [float32x4(NaN, -Infinity, +Infinity, -0), float32x4(13.37, 42.42, NaN, 0)]])
-  {
-      testMinFloat32(v, w);
-      testMaxFloat32(v, w);
-      testMinNumFloat32(v, w);
-      testMaxNumFloat32(v, w);
-  }
-
-  if (typeof reportCompare === "function")
-    reportCompare(true, true);
-}
-
-test();
-
deleted file mode 100644
--- a/js/src/tests/ecma_7/SIMD/float32x4clamp.js
+++ /dev/null
@@ -1,45 +0,0 @@
-// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
-var BUGNUMBER = 946042;
-var float32x4 = SIMD.float32x4;
-var int32x4 = SIMD.int32x4;
-
-var summary = 'float32x4 clamp';
-
-function test() {
-  print(BUGNUMBER + ": " + summary);
-
-  // FIXME -- Bug 1068028: Amend to check for correctness of NaN border cases once the semantics are defined.
-
-  var a = float32x4(-20, 10, 30, 0.5);
-  var lower = float32x4(2, 1, 50, 0);
-  var upper = float32x4(2.5, 5, 55, 1);
-  var c = SIMD.float32x4.clamp(a, lower, upper);
-  assertEq(c.x, 2);
-  assertEq(c.y, 5);
-  assertEq(c.z, 50);
-  assertEq(c.w, 0.5);
-
-  var d = float32x4(-13.37, 10.46, 31.79, 0.54);
-  var lower1 = float32x4(2.1, 1.1, 50.13, 0.0);
-  var upper1 = float32x4(2.56, 5.55, 55.93, 1.1);
-  var f = SIMD.float32x4.clamp(d, lower1, upper1);
-  assertEq(f.x, Math.fround(2.1));
-  assertEq(f.y, Math.fround(5.55));
-  assertEq(f.z, Math.fround(50.13));
-  assertEq(f.w, Math.fround(0.54));
-
-  var g = float32x4(4, -Infinity, 10, -10);
-  var lower2 = float32x4(5, -Infinity, -Infinity, -Infinity);
-  var upper2 = float32x4(Infinity, 5, Infinity, Infinity);
-  var i = SIMD.float32x4.clamp(g, lower2, upper2);
-  assertEq(i.x, 5);
-  assertEq(i.y, -Infinity);
-  assertEq(i.z, 10);
-  assertEq(i.w, -10);
-
-  if (typeof reportCompare === "function")
-    reportCompare(true, true);
-}
-
-test();
-
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/SIMD/float32x4fromfloat64x2.js
@@ -0,0 +1,60 @@
+// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
+var BUGNUMBER = 1031203;
+var float32x4 = SIMD.float32x4;
+var float64x2 = SIMD.float64x2;
+
+var summary = 'float32x4 fromFloat64x2';
+
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function test() {
+  print(BUGNUMBER + ": " + summary);
+
+  var a = float64x2(1, 2);
+  var c = float32x4.fromFloat64x2(a);
+  assertEq(c.x, 1);
+  assertEq(c.y, 2);
+  assertEq(c.z, 0);
+  assertEq(c.w, 0);
+
+  var d = float64x2(-0, NaN);
+  var f = float32x4.fromFloat64x2(d);
+  assertEq(f.x, -0);
+  assertEq(f.y, NaN);
+  assertEq(f.z, 0);
+  assertEq(f.w, 0);
+
+  var g = float64x2(Infinity, -Infinity);
+  var i = float32x4.fromFloat64x2(g);
+  assertEq(i.x, Infinity);
+  assertEq(i.y, -Infinity);
+  assertEq(i.z, 0);
+  assertEq(i.w, 0);
+
+  var j = Math.pow(2, 25) - 1;
+  var k = -Math.pow(2, 25);
+  var l = float64x2(j, k);
+  var m = float32x4.fromFloat64x2(l);
+  assertEq(m.x, Math.fround(j));
+  assertEq(m.y, Math.fround(k));
+  assertEq(m.z, 0);
+  assertEq(m.w, 0);
+
+  var o = Math.pow(2, 1000);
+  var p = Math.pow(2, -1000);
+  var q = float64x2(o, p);
+  var r = float32x4.fromFloat64x2(q);
+  assertEq(r.x, Math.fround(o));
+  assertEq(r.y, Math.fround(p));
+  assertEq(r.z, 0);
+  assertEq(r.w, 0);
+
+  if (typeof reportCompare === "function")
+    reportCompare(true, true);
+}
+
+test();
+
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/SIMD/float32x4fromfloat64x2bits.js
@@ -0,0 +1,43 @@
+// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
+var BUGNUMBER = 1031203;
+var float32x4 = SIMD.float32x4;
+var float64x2 = SIMD.float64x2;
+var int32x4 = SIMD.int32x4;
+
+var summary = 'float32x4 fromFloat64x2Bits';
+
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function test() {
+  print(BUGNUMBER + ": " + summary);
+
+  var a = float64x2(2.000000473111868, 512.0001225471497);
+  var b = float32x4.fromFloat64x2Bits(a);
+  assertEq(b.x, 1.0);
+  assertEq(b.y, 2.0);
+  assertEq(b.z, 3.0);
+  assertEq(b.w, 4.0);
+
+  var c = float64x2(-0, NaN);
+  var d = float32x4.fromFloat64x2Bits(c);
+  assertEq(d.x, 0);
+  assertEq(d.y, -0);
+  assertEq(d.z, 0);
+  assertEq(d.w, NaN);
+
+  var e = float64x2(Infinity, -Infinity);
+  var f = float32x4.fromFloat64x2Bits(e);
+  assertEq(f.x, 0);
+  assertEq(f.y, NaN);
+  assertEq(f.z, 0);
+  assertEq(f.w, NaN);
+
+  if (typeof reportCompare === "function")
+    reportCompare(true, true);
+}
+
+test();
+
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/SIMD/float64x2-arithmetic.js
@@ -0,0 +1,77 @@
+// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
+var BUGNUMBER = 1031203;
+var float64x2 = SIMD.float64x2;
+
+var summary = 'float64x2 arithmetic';
+
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function add(a, b) { return a + b; }
+function sub(a, b) { return a - b; }
+function mul(a, b) { return a * b; }
+function div(a, b) { return a / b; }
+function neg(a) { return -a; }
+function reciprocal(a) { return 1 / a; }
+function reciprocalSqrt(a) { return 1 / Math.sqrt(a); }
+
+function testAdd(v, w) {
+    return testBinaryFunc(v, w, float64x2.add, add);
+}
+function testSub(v, w) {
+    return testBinaryFunc(v, w, float64x2.sub, sub);
+}
+function testMul(v, w) {
+    return testBinaryFunc(v, w, float64x2.mul, mul);
+}
+function testDiv(v, w) {
+    return testBinaryFunc(v, w, float64x2.div, div);
+}
+function testAbs(v) {
+    return testUnaryFunc(v, float64x2.abs, Math.abs);
+}
+function testNeg(v) {
+    return testUnaryFunc(v, float64x2.neg, neg);
+}
+function testReciprocal(v) {
+    return testUnaryFunc(v, float64x2.reciprocal, reciprocal);
+}
+function testReciprocalSqrt(v) {
+    return testUnaryFunc(v, float64x2.reciprocalSqrt, reciprocalSqrt);
+}
+function testSqrt(v) {
+    return testUnaryFunc(v, float64x2.sqrt, Math.sqrt);
+}
+
+function test() {
+  print(BUGNUMBER + ": " + summary);
+
+  var v, w;
+  for ([v, w] of [[float64x2(1, 2), float64x2(3, 4)],
+                  [float64x2(1.894, 2.8909), float64x2(100.764, 200.987)],
+                  [float64x2(-1, -2), float64x2(-14.54, 57)],
+                  [float64x2(+Infinity, -Infinity), float64x2(NaN, -0)]])
+  {
+      testAdd(v, w);
+      testSub(v, w);
+      testMul(v, w);
+      testDiv(v, w);
+      testAbs(v);
+      testNeg(v);
+      testReciprocal(v);
+      testSqrt(v);
+  }
+  for (v of [float64x2(1, 0.25), float64x2(3, 0.5),
+             float64x2(-0, NaN), float64x2(+Infinity, -Infinity)])
+  {
+      testReciprocalSqrt(v);
+  }
+
+  if (typeof reportCompare === "function")
+    reportCompare(true, true);
+}
+
+test();
+
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/SIMD/float64x2alignment.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
+var BUGNUMBER = 1031203;
+var float64x2 = SIMD.float64x2;
+
+var summary = 'float64x2 alignment';
+
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var StructType = TypedObject.StructType;
+var uint8 = TypedObject.uint8;
+
+function test() {
+  print(BUGNUMBER + ": " + summary);
+
+  assertEq(float64x2.byteLength, 16);
+  assertEq(float64x2.byteAlignment, 16);
+
+  var Compound = new StructType({c: uint8, d: uint8, f: float64x2});
+  assertEq(Compound.fieldOffsets["c"], 0);
+  assertEq(Compound.fieldOffsets["d"], 1);
+  assertEq(Compound.fieldOffsets["f"], 16);
+
+  if (typeof reportCompare === "function")
+    reportCompare(true, true);
+}
+
+test();
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/SIMD/float64x2fromfloat32x4.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
+var BUGNUMBER = 1031203;
+var float32x4 = SIMD.float32x4;
+var float64x2 = SIMD.float64x2;
+
+var summary = 'float64x2 fromFloat32x4';
+
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function test() {
+  print(BUGNUMBER + ": " + summary);
+
+  var a = float32x4(100, 200, 300, 400);
+  var c = float64x2.fromFloat32x4(a);
+  assertEq(c.x, 100);
+  assertEq(c.y, 200);
+
+  var d = float32x4(NaN, -0, NaN, -0);
+  var f = float64x2.fromFloat32x4(d);
+  assertEq(f.x, NaN);
+  assertEq(f.y, -0);
+
+  var g = float32x4(Infinity, -Infinity, Infinity, -Infinity);
+  var i = float64x2.fromFloat32x4(g);
+  assertEq(i.x, Infinity);
+  assertEq(i.y, -Infinity);
+
+  var j = float32x4(13.37, 12.853, 49.97, 53.124);
+  var l = float64x2.fromFloat32x4(j);
+  assertEq(l.x, Math.fround(13.37));
+  assertEq(l.y, Math.fround(12.853));
+
+  if (typeof reportCompare === "function")
+    reportCompare(true, true);
+}
+
+test();
+
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/SIMD/float64x2fromfloat32x4bits.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
+var BUGNUMBER = 1031203;
+var float32x4 = SIMD.float32x4;
+var float64x2 = SIMD.float64x2;
+var int32x4 = SIMD.int32x4;
+
+var summary = 'float64x2 fromFloat32x4Bits';
+
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function test() {
+  print(BUGNUMBER + ": " + summary);
+
+  var a = float32x4(0, 1.875, 0, 2);
+  var c = float64x2.fromFloat32x4Bits(a);
+  assertEq(c.x, 1.0);
+  assertEq(c.y, 2.0);
+
+  var d = float32x4(NaN, -0, Infinity, -Infinity);
+  var f = float64x2.fromFloat32x4Bits(d);
+  assertEq(f.x, -1.058925634e-314);
+  assertEq(f.y, -1.404448428688076e+306);
+
+  if (typeof reportCompare === "function")
+    reportCompare(true, true);
+}
+
+test();
+
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/SIMD/float64x2fromint32x4.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
+var BUGNUMBER = 1031203;
+var float64x2 = SIMD.float64x2;
+var int32x4 = SIMD.int32x4;
+
+var summary = 'float64x2 fromInt32x4';
+
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function test() {
+  print(BUGNUMBER + ": " + summary);
+
+  var a = int32x4(1, 2, 3, 4);
+  var c = float64x2.fromInt32x4(a);
+  assertEq(c.x, 1);
+  assertEq(c.y, 2);
+
+  var d = int32x4(INT32_MAX, INT32_MIN, 0, 0);
+  var f = float64x2.fromInt32x4(d);
+  assertEq(f.x, INT32_MAX);
+  assertEq(f.y, INT32_MIN);
+
+  if (typeof reportCompare === "function")
+    reportCompare(true, true);
+}
+
+test();
+
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/SIMD/float64x2fromint32x4bits.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
+var BUGNUMBER = 1031203;
+var float64x2 = SIMD.float64x2;
+var int32x4 = SIMD.int32x4;
+
+var summary = 'float64x2 fromInt32x4Bits';
+
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function test() {
+  print(BUGNUMBER + ": " + summary);
+
+  var a = int32x4(0x00000000, 0x3ff00000, 0x0000000, 0x40000000);
+  var c = float64x2.fromInt32x4Bits(a);
+  assertEq(c.x, 1.0);
+  assertEq(c.y, 2.0);
+
+  var d = int32x4(0xabcdef12, 0x3ff00000, 0x21fedcba, 0x40000000);
+  var f = float64x2.fromInt32x4Bits(d);
+  assertEq(f.x, 1.0000006400213732);
+  assertEq(f.y, 2.0000002532866263);
+
+  if (typeof reportCompare === "function")
+    reportCompare(true, true);
+}
+
+test();
+
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/SIMD/float64x2getters.js
@@ -0,0 +1,47 @@
+// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
+var BUGNUMBER = 1031203;
+var float64x2 = SIMD.float64x2;
+var int32x4 = SIMD.int32x4;
+
+var summary = 'float64x2 getters';
+
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function test() {
+  print(BUGNUMBER + ": " + summary);
+
+  // Create a float64x2 and check that the getters work:
+  var f = float64x2(11, 22);
+  assertEq(f.x, 11);
+  assertEq(f.y, 22);
+
+  // Test that the getters work when called reflectively:
+  var g = f.__lookupGetter__("x");
+  assertEq(g.call(f), 11);
+
+  // Test that getters cannot be applied to various incorrect things:
+  assertThrowsInstanceOf(function() {
+    g.call({})
+  }, TypeError, "Getter applicable to random objects");
+  assertThrowsInstanceOf(function() {
+    g.call(0xDEADBEEF)
+  }, TypeError, "Getter applicable to integers");
+  assertThrowsInstanceOf(function() {
+    var T = new TypedObject.StructType({x: TypedObject.float64,
+                                        y: TypedObject.float64});
+    var v = new T({x: 11, y: 22});
+    g.call(v)
+  }, TypeError, "Getter applicable to structs");
+  assertThrowsInstanceOf(function() {
+    var t = new int32x4(1, 2, 3, 4);
+    g.call(t)
+  }, TypeError, "Getter applicable to int32x4");
+
+  if (typeof reportCompare === "function")
+    reportCompare(true, true);
+}
+
+test();
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/SIMD/float64x2handle.js
@@ -0,0 +1,51 @@
+// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
+var BUGNUMBER = 1031203;
+var float64x2 = SIMD.float64x2;
+var int32x4 = SIMD.int32x4;
+
+var summary = 'float64x2 handles';
+
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var ArrayType = TypedObject.ArrayType;
+
+var float64 = TypedObject.float64;
+var Handle = TypedObject.Handle;
+
+function test() {
+  print(BUGNUMBER + ": " + summary);
+
+  var Array = float64x2.array(3);
+  var array = new Array([float64x2(1, 2),
+                         float64x2(3, 4),
+                         float64x2(5, 6)]);
+
+  // Test that trying to create handle into the interior of a
+  // float64x2 fails.
+
+  assertThrowsInstanceOf(function() {
+    var h = float64.handle(array, 1, "w");
+  }, TypeError, "Creating a float64 handle to prop via ctor");
+
+  assertThrowsInstanceOf(function() {
+    var h = float64.handle();
+    Handle.move(h, array, 1, "w");
+  }, TypeError, "Creating a float64 handle to prop via move");
+
+  assertThrowsInstanceOf(function() {
+    var h = float64.handle(array, 1, 0);
+  }, TypeError, "Creating a float64 handle to elem via ctor");
+
+  assertThrowsInstanceOf(function() {
+    var h = float64.handle();
+    Handle.move(h, array, 1, 0);
+  }, TypeError, "Creating a float64 handle to elem via move");
+
+  if (typeof reportCompare === "function")
+    reportCompare(true, true);
+}
+
+test();
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/SIMD/float64x2reify.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
+var BUGNUMBER = 1031203;
+var float64x2 = SIMD.float64x2;
+
+var summary = 'float64x2 reify';
+
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var ArrayType = TypedObject.ArrayType;
+
+function test() {
+  print(BUGNUMBER + ": " + summary);
+
+  var Array = float64x2.array(3);
+  var array = new Array([float64x2(1, 2),
+                         float64x2(3, 4),
+                         float64x2(5, 6)]);
+
+  // Test that reading array[1] produces a *copy* of float64x2, not an
+  // alias into the array.
+
+  var f = array[1];
+  assertEq(f.y, 4);
+  assertEq(array[1].y, 4);
+  array[1] = float64x2(7, 8);
+  assertEq(f.y, 4);
+  assertEq(array[1].y, 8);
+
+  if (typeof reportCompare === "function")
+    reportCompare(true, true);
+}
+
+test();
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/SIMD/float64x2setter.js
@@ -0,0 +1,45 @@
+// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
+var BUGNUMBER = 1031203;
+var float64x2 = SIMD.float64x2;
+
+var summary = 'float64x2 setter';
+
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var ArrayType = TypedObject.ArrayType;
+
+function test() {
+  print(BUGNUMBER + ": " + summary);
+
+  var Array = float64x2.array(3);
+  var array = new Array([float64x2(1, 2),
+                         float64x2(3, 4),
+                         float64x2(5, 6)]);
+  assertEq(array[1].y, 4);
+
+  // Test that we are allowed to write float64x2 values into array,
+  // but not other things.
+
+  array[1] = float64x2(7, 8);
+  assertEq(array[1].y, 8);
+
+  assertThrowsInstanceOf(function() {
+    array[1] = {x: 7, y: 8 };
+  }, TypeError, "Setting float64x2 from an object");
+
+  assertThrowsInstanceOf(function() {
+    array[1] = [ 7, 8 ];
+  }, TypeError, "Setting float64x2 from an array");
+
+  assertThrowsInstanceOf(function() {
+    array[1] = 9;
+  }, TypeError, "Setting float64x2 from a number");
+
+  if (typeof reportCompare === "function")
+    reportCompare(true, true);
+}
+
+test();
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/SIMD/float64x2with.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
+var BUGNUMBER = 1031203;
+var float64x2 = SIMD.float64x2;
+
+var summary = 'float64x2 with';
+
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function test() {
+  print(BUGNUMBER + ": " + summary);
+
+  var a = float64x2(1, 2);
+  var x = float64x2.withX(a, 5);
+  var y = float64x2.withY(a, 5);
+  assertEq(x.x, 5);
+  assertEq(x.y, 2);
+  assertEq(y.x, 1);
+  assertEq(y.y, 5);
+
+  var b = float64x2(NaN, -0);
+  var x1 = float64x2.withX(b, Infinity);
+  var y1 = float64x2.withY(b, -Infinity);
+  assertEq(x1.x, Infinity);
+  assertEq(x1.y, -0);
+  assertEq(y1.x, NaN);
+  assertEq(y1.y, -Infinity);
+
+  if (typeof reportCompare === "function")
+    reportCompare(true, true);
+}
+
+test();
+
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/SIMD/int32x4fromfloat64x2.js
@@ -0,0 +1,51 @@
+// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
+var BUGNUMBER = 1031203;
+var float64x2 = SIMD.float64x2;
+var int32x4 = SIMD.int32x4;
+
+var summary = 'int32x4 fromFloat64x2';
+
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function test() {
+  print(BUGNUMBER + ": " + summary);
+
+  var a = float64x2(1, 2.2);
+  var c = int32x4.fromFloat64x2(a);
+  assertEq(c.x, 1);
+  assertEq(c.y, 2);
+  assertEq(c.z, 0);
+  assertEq(c.w, 0);
+
+  var d = float64x2(NaN, -0);
+  var f = int32x4.fromFloat64x2(d);
+  assertEq(f.x, 0);
+  assertEq(f.y, 0);
+  assertEq(f.z, 0);
+  assertEq(f.w, 0);
+
+  var g = float64x2(Infinity, -Infinity);
+  var i = int32x4.fromFloat64x2(g);
+  assertEq(i.x, 0);
+  assertEq(i.y, 0);
+  assertEq(i.z, 0);
+  assertEq(i.w, 0);
+
+  var j = Math.pow(2, 31);
+  var k = -Math.pow(2, 31) - 1;
+  var m = float64x2(j, k);
+  var l = int32x4.fromFloat64x2(m);
+  assertEq(l.x, INT32_MIN);
+  assertEq(l.y, INT32_MAX);
+  assertEq(l.z, 0);
+  assertEq(l.w, 0);
+
+  if (typeof reportCompare === "function")
+    reportCompare(true, true);
+}
+
+test();
+
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/SIMD/int32x4fromfloat64x2bits.js
@@ -0,0 +1,49 @@
+// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
+var BUGNUMBER = 1031203;
+var float64x2 = SIMD.float64x2;
+var int32x4 = SIMD.int32x4;
+
+var summary = 'int32x4 fromFloat64x2Bits';
+
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function test() {
+  print(BUGNUMBER + ": " + summary);
+
+  var a = float64x2(1.0, 2.0);
+  var c = int32x4.fromFloat64x2Bits(a);
+  assertEq(c.x, 0x00000000);
+  assertEq(c.y, 0x3FF00000);
+  assertEq(c.z, 0x00000000);
+  assertEq(c.w, 0x40000000);
+
+  var d = float64x2(+Infinity, -Infinity);
+  var f = int32x4.fromFloat64x2Bits(d);
+  assertEq(f.x, 0x00000000);
+  assertEq(f.y, 0x7ff00000);
+  assertEq(f.z, 0x00000000);
+  assertEq(f.w, -0x100000);
+
+  var g = float64x2(-0, NaN);
+  var i = int32x4.fromFloat64x2Bits(g);
+  assertEq(i.x, 0x00000000);
+  assertEq(i.y, -0x80000000);
+  assertEq(i.z, 0x00000000);
+  assertEq(i.w, 0x7ff80000);
+
+  var j = float64x2(1.0000006400213732, 2.0000002532866263);
+  var l = int32x4.fromFloat64x2Bits(j);
+  assertEq(l.x, -0x543210ee);
+  assertEq(l.y, 0x3ff00000);
+  assertEq(l.z, 0x21fedcba);
+  assertEq(l.w, 0x40000000);
+
+  if (typeof reportCompare === "function")
+    reportCompare(true, true);
+}
+
+test();
+
--- a/js/src/tests/ecma_7/SIMD/load.js
+++ b/js/src/tests/ecma_7/SIMD/load.js
@@ -1,20 +1,20 @@
 // |reftest| skip-if(!this.hasOwnProperty("SIMD"))
 
 /*
  * Any copyright is dedicated to the Public Domain.
  * https://creativecommons.org/publicdomain/zero/1.0/
  */
 
-// Our float32array will have 16 elements
-const SIZE_ARRAY = 16;
+// Our array for int32x4 and float32x4 will have 16 elements
+const SIZE_32_ARRAY = 16;
+const SIZE_64_ARRAY = 8;
 
-// 1 float32 == 4 bytes
-const SIZE_BYTES = SIZE_ARRAY * 4;
+const SIZE_BYTES = SIZE_32_ARRAY * 4;
 
 function MakeComparator(kind, arr) {
     var bpe = arr.BYTES_PER_ELEMENT;
     var uint8 = (bpe != 1) ? new Uint8Array(arr.buffer) : arr;
 
     // Size in bytes of a single element in the SIMD vector.
     var sizeOfLaneElem;
     // Typed array constructor corresponding to the SIMD kind.
@@ -23,20 +23,24 @@ function MakeComparator(kind, arr) {
       case 'int32x4':
         sizeOfLaneElem = 4;
         typedArrayCtor = Int32Array;
         break;
       case 'float32x4':
         sizeOfLaneElem = 4;
         typedArrayCtor = Float32Array;
         break;
+      case 'float64x2':
+        sizeOfLaneElem = 8;
+        typedArrayCtor = Float64Array;
+        break;
       default:
         assertEq(true, false, "unknown SIMD kind");
     }
-
+    var lanes = 16 / sizeOfLaneElem;
     // Reads (numElemToRead * sizeOfLaneElem) bytes in arr, and reinterprets
     // these bytes as a typed array equivalent to the typed SIMD vector.
     var slice = function(start, numElemToRead) {
         // Read enough bytes
         var startBytes = start * bpe;
         var endBytes = startBytes + numElemToRead * sizeOfLaneElem;
         var asArray = Array.prototype.slice.call(uint8, startBytes, endBytes);
 
@@ -44,41 +48,48 @@ function MakeComparator(kind, arr) {
         // This is needed for loadX, loadXY, loadXYZ which do only partial
         // reads.
         for (var i = asArray.length; i < SIZE_BYTES; i++) asArray[i] = 0;
         assertEq(asArray.length, SIZE_BYTES);
 
         return new typedArrayCtor(new Uint8Array(asArray).buffer);
     }
 
+    var assertFunc = (lanes == 2) ? assertEqX2 : assertEqX4;
+    var type = SIMD[kind];
     return {
         loadX: function(index) {
-            var v = SIMD[kind].loadX(arr, index);
-            assertEqX4(v, slice(index, 1));
+            var v = type.loadX(arr, index);
+            assertFunc(v, slice(index, 1));
         },
 
         loadXY: function(index) {
-            var v = SIMD[kind].loadXY(arr, index);
-            assertEqX4(v, slice(index, 2));
+            if (lanes < 4)
+                return;
+            var v = type.loadXY(arr, index);
+            assertFunc(v, slice(index, 2));
         },
 
-        loadXYZ: function(index) {
-            var v = SIMD[kind].loadXYZ(arr, index);
-            assertEqX4(v, slice(index, 3));
+       loadXYZ: function(index) {
+           if (lanes < 4)
+               return;
+           var v = type.loadXYZ(arr, index);
+           assertFunc(v, slice(index, 3));
         },
 
         load: function(index) {
-            var v = SIMD[kind].load(arr, index);
-            assertEqX4(v, slice(index, 4));
+           var v = type.load(arr, index);
+           assertFunc(v, slice(index, 4));
         }
     }
 }
 
 function testLoad(kind, TA) {
-    for (var i = SIZE_ARRAY; i--;)
+    var lanes = TA.length / 4;
+    for (var i = TA.length; i--;)
         TA[i] = i;
 
     for (var ta of [
                     new Uint8Array(TA.buffer),
                     new Int8Array(TA.buffer),
                     new Uint16Array(TA.buffer),
                     new Int16Array(TA.buffer),
                     new Uint32Array(TA.buffer),
@@ -92,17 +103,17 @@ function testLoad(kind, TA) {
         assertThrowsInstanceOf(() => SIMD[kind].load(ta), TypeError);
         assertThrowsInstanceOf(() => SIMD[kind].load("hello", 0), TypeError);
         assertThrowsInstanceOf(() => SIMD[kind].load(ta, -1), RangeError);
 
         // Valid and invalid reads
         var C = MakeComparator(kind, ta);
         var bpe = ta.BYTES_PER_ELEMENT;
 
-        var lastValidArgLoadX   = (SIZE_BYTES - 4)  / bpe | 0;
+        var lastValidArgLoadX   = (SIZE_BYTES - (lanes == 4 ? 4 : 8))  / bpe | 0;
         var lastValidArgLoadXY  = (SIZE_BYTES - 8)  / bpe | 0;
         var lastValidArgLoadXYZ = (SIZE_BYTES - 12) / bpe | 0;
         var lastValidArgLoad    = (SIZE_BYTES - 16) / bpe | 0;
 
         C.load(0);
         C.load(1);
         C.load(2);
         C.load(3);
@@ -116,39 +127,45 @@ function testLoad(kind, TA) {
         C.loadX(lastValidArgLoadX);
         assertThrowsInstanceOf(() => SIMD[kind].loadX(ta, lastValidArgLoadX + 1), RangeError);
 
         C.loadXY(0);
         C.loadXY(1);
         C.loadXY(2);
         C.loadXY(3);
         C.loadXY(lastValidArgLoadXY);
-        assertThrowsInstanceOf(() => SIMD[kind].loadXY(ta, lastValidArgLoadXY + 1), RangeError);
 
         C.loadXYZ(0);
         C.loadXYZ(1);
         C.loadXYZ(2);
         C.loadXYZ(3);
         C.loadXYZ(lastValidArgLoadXYZ);
-        assertThrowsInstanceOf(() => SIMD[kind].loadXYZ(ta, lastValidArgLoadXYZ + 1), RangeError);
+
+        if (lanes >= 4) {
+            assertThrowsInstanceOf(() => SIMD[kind].loadXY(ta, lastValidArgLoadXY + 1), RangeError);
+            assertThrowsInstanceOf(() => SIMD[kind].loadXYZ(ta, lastValidArgLoadXYZ + 1), RangeError);
+        }
     }
 
-    // Test ToInt32 behavior
-    var v = SIMD[kind].load(TA, 12.5);
-    assertEqX4(v, [12, 13, 14, 15]);
+    if (lanes == 4) {
+        // Test ToInt32 behavior
+        var v = SIMD[kind].load(TA, 12.5);
+        assertEqX4(v, [12, 13, 14, 15]);
 
-    var obj = {
-        valueOf: function() { return 12 }
+        var obj = {
+            valueOf: function() { return 12 }
+        }
+        var v = SIMD[kind].load(TA, obj);
+        assertEqX4(v, [12, 13, 14, 15]);
     }
-    var v = SIMD[kind].load(TA, obj);
-    assertEqX4(v, [12, 13, 14, 15]);
 
     var obj = {
         valueOf: function() { throw new TypeError("i ain't a number"); }
     }
     assertThrowsInstanceOf(() => SIMD[kind].load(TA, obj), TypeError);
 }
 
-testLoad('float32x4', new Float32Array(SIZE_ARRAY));
-testLoad('int32x4', new Int32Array(SIZE_ARRAY));
+testLoad('float32x4', new Float32Array(SIZE_32_ARRAY));
+testLoad('float64x2', new Float64Array(SIZE_64_ARRAY));
+testLoad('int32x4', new Int32Array(SIZE_32_ARRAY));
 
 if (typeof reportCompare === "function")
     reportCompare(true, true);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/SIMD/minmax.js
@@ -0,0 +1,85 @@
+// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
+
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var float32x4 = SIMD.float32x4;
+var float64x2 = SIMD.float64x2;
+
+function testMaxFloat32(v, w) {
+    return testBinaryFunc(v, w, float32x4.max, (x, y) => Math.fround(Math.max(x, y)), 4);
+}
+function testMinFloat32(v, w) {
+    return testBinaryFunc(v, w, float32x4.min, (x, y) => Math.fround(Math.min(x, y)), 4);
+}
+
+function testMaxFloat64(v, w) {
+    return testBinaryFunc(v, w, float64x2.max, (x, y) => Math.max(x, y), 2);
+}
+function testMinFloat64(v, w) {
+    return testBinaryFunc(v, w, float64x2.min, (x, y) => Math.min(x, y), 2);
+}
+
+function maxNum(x, y) {
+    if (x != x)
+        return y;
+    if (y != y)
+        return x;
+    return Math.max(x, y);
+}
+
+function minNum(x, y) {
+    if (x != x)
+        return y;
+    if (y != y)
+        return x;
+    return Math.min(x, y);
+}
+
+function testMaxNumFloat32(v, w) {
+    return testBinaryFunc(v, w, float32x4.maxNum, maxNum, 4);
+}
+function testMinNumFloat32(v, w) {
+    return testBinaryFunc(v, w, float32x4.minNum, minNum, 4);
+}
+
+function testMaxNumFloat64(v, w) {
+    return testBinaryFunc(v, w, float64x2.maxNum, maxNum, 2);
+}
+function testMinNumFloat64(v, w) {
+    return testBinaryFunc(v, w, float64x2.minNum, minNum, 2);
+}
+
+function test() {
+  var v, w;
+  for ([v, w] of [[float32x4(1, 20, 30, 4), float32x4(10, 2, 3, 40)],
+                  [float32x4(9.999, 2.1234, 30.4443, 4), float32x4(10, 2.1233, 30.4444, 4.0001)],
+                  [float32x4(NaN, -Infinity, +Infinity, -0), float32x4(13.37, 42.42, NaN, 0)]])
+  {
+      testMinFloat32(v, w);
+      testMaxFloat32(v, w);
+      testMinNumFloat32(v, w);
+      testMaxNumFloat32(v, w);
+  }
+
+  for ([v, w] of [[float64x2(1, 20), float64x2(10, 2)],
+                  [float64x2(30, 4), float64x2(3, 40)],
+                  [float64x2(9.999, 2.1234), float64x2(10, 2.1233)],
+                  [float64x2(30.4443, 4), float64x2(30.4444, 4.0001)],
+                  [float64x2(NaN, -Infinity), float64x2(13.37, 42.42)],
+                  [float64x2(+Infinity, -0), float64x2(NaN, 0)]])
+  {
+      testMinFloat64(v, w);
+      testMaxFloat64(v, w);
+      testMinNumFloat64(v, w);
+      testMaxNumFloat64(v, w);
+  }
+
+  if (typeof reportCompare === "function")
+    reportCompare(true, true);
+}
+
+test();
+
--- a/js/src/tests/ecma_7/SIMD/select-bitselect.js
+++ b/js/src/tests/ecma_7/SIMD/select-bitselect.js
@@ -1,16 +1,17 @@
 // |reftest| skip-if(!this.hasOwnProperty("SIMD"))
 
 /*
  * Any copyright is dedicated to the Public Domain.
  * https://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var float32x4 = SIMD.float32x4;
+var float64x2 = SIMD.float64x2;
 var int32x4 = SIMD.int32x4;
 
 function select(mask, ifTrue, ifFalse) {
     var m = simdToArray(mask);
     var tv = simdToArray(ifTrue);
     var fv = simdToArray(ifFalse);
     return m.map(function(v, i) {
         return (v < 0 ? tv : fv)[i];
@@ -22,63 +23,63 @@ function select(mask, ifTrue, ifFalse) {
  * has 4 lanes and 2 possible values (true or false), there are 16 possible
  * masks.
  */
 function testSelect(type, inputs) {
     var x, y;
     for (var i = 0; i < 16; i++) {
         var mask = int32x4.bool(!!(i & 1), !!((i >> 1) & 1), !!((i >> 2) & 1), !!((i >> 3) & 1));
         for ([x, y] of inputs)
-            assertEqX4(type.select(mask, x, y), select(mask, x, y));
+            assertEqVec(type.select(mask, x, y), select(mask, x, y));
     }
 }
 
-function bitselect(ScalarTypedArray, mask, ifTrue, ifFalse) {
-    var m = simdToArray(mask);
-
-    var tv = new ScalarTypedArray(simdToArray(ifTrue));
-    var fv = new ScalarTypedArray(simdToArray(ifFalse));
-
-    tv = new Int32Array(tv.buffer);
-    fv = new Int32Array(fv.buffer);
-    var res = new Int32Array(4);
+function int32x4FromTypeBits(type, vec) {
+    switch (type) {
+      case float32x4:
+          return int32x4.fromFloat32x4Bits(vec);
+      case float64x2:
+          return int32x4.fromFloat64x2Bits(vec);
+      case int32x4:
+          return vec;
+      default:
+          throw new TypeError("Unknown SIMD type.");
+    }
+}
 
-    for (var i = 0; i < 4; i++) {
-        var t = 0;
-        for (var bit = 0; bit < 32; bit++) {
-            var readVal = (m[i] >> bit) & 1 ? tv[i] : fv[i];
-            var readBit = (readVal >> bit) & 1;
-            t |= readBit << bit;
-        }
-        res[i] = t;
-    }
-
-    res = new ScalarTypedArray(res.buffer);
-    return Array.prototype.map.call(res, x => x);
+function bitselect(type, mask, ifTrue, ifFalse) {
+    var tv = int32x4FromTypeBits(type, ifTrue);
+    var fv = int32x4FromTypeBits(type, ifFalse);
+    var tr = int32x4.and(mask, tv);
+    var fr = int32x4.and(int32x4.not(mask), fv);
+    var orApplied = int32x4.or(tr, fr);
+    var converted = type == int32x4 ? orApplied : type.fromInt32x4Bits(orApplied);
+    return simdToArray(converted);
 }
 
 function findCorrespondingScalarTypedArray(type) {
     switch (type) {
         case int32x4: return Int32Array;
         case float32x4: return Float32Array;
+        case float64x2: return Float64Array;
         default: throw new Error("undefined scalar typed array");
     }
 }
 
 /**
  * This tests type.bitselect on all boolean masks, as in select. For these,
  *          bitselect(mask, x, y) === select(mask, x, y)
  */
 function testBitSelectSimple(type, inputs) {
     var x, y;
     var ScalarTypedArray = findCorrespondingScalarTypedArray(type);
     for (var i = 0; i < 16; i++) {
         var mask = int32x4.bool(!!(i & 1), !!((i >> 1) & 1), !!((i >> 2) & 1), !!((i >> 3) & 1));
         for ([x, y] of inputs)
-            assertEqX4(type.bitselect(mask, x, y), bitselect(ScalarTypedArray, mask, x, y));
+            assertEqVec(type.bitselect(mask, x, y), bitselect(type, mask, x, y));
     }
 }
 
 /**
  * This tests type.bitselect on a few hand-defined masks. For these,
  *          bitselect(mask, x, y) !== select(mask, x, y)
  */
 function testBitSelectComplex(type, inputs) {
@@ -86,17 +87,17 @@ function testBitSelectComplex(type, inpu
     var masks = [
         int32x4(1337, 0x1337, 0x42, 42),
         int32x4(0x00FF1CE, 0xBAADF00D, 0xDEADBEEF, 0xCAFED00D),
         int32x4(0xD15EA5E, 0xDEADC0DE, 0xFACEB00C, 0x4B1D4B1D)
     ];
     var ScalarTypedArray = findCorrespondingScalarTypedArray(type);
     for (var mask of masks) {
         for ([x, y] of inputs)
-            assertEqX4(type.bitselect(mask, x, y), bitselect(ScalarTypedArray, mask, x, y));
+            assertEqVec(type.bitselect(mask, x, y), bitselect(type, mask, x, y));
     }
 }
 
 function test() {
     var inputs = [
         [int32x4(0,4,9,16), int32x4(1,2,3,4)],
         [int32x4(-1, 2, INT32_MAX, INT32_MIN), int32x4(INT32_MAX, -4, INT32_MIN, 42)]
     ];
@@ -110,13 +111,26 @@ function test() {
         [float32x4(-1.5,-0,NaN,-Infinity), float32x4(1,-2,13.37,3.13)],
         [float32x4(1.5,2.75,NaN,Infinity), float32x4(-NaN,-Infinity,9.75,16.125)]
     ];
 
     testSelect(float32x4, inputs);
     testBitSelectSimple(float32x4, inputs);
     testBitSelectComplex(float32x4, inputs);
 
+    inputs = [
+        [float64x2(0.125,4.25), float64x2(9.75,16.125)],
+        [float64x2(1.5,2.75), float64x2(3.25,4.5)],
+        [float64x2(-1.5,-0), float64x2(NaN,-Infinity)],
+        [float64x2(1,-2), float64x2(13.37,3.13)],
+        [float64x2(1.5,2.75), float64x2(NaN,Infinity)],
+        [float64x2(-NaN,-Infinity), float64x2(9.75,16.125)]
+    ];
+
+    testSelect(float64x2, inputs);
+    testBitSelectSimple(float64x2, inputs);
+    testBitSelectComplex(float64x2, inputs);
+
     if (typeof reportCompare === "function")
         reportCompare(true, true);
 }
 
 test();
--- a/js/src/tests/ecma_7/SIMD/shell.js
+++ b/js/src/tests/ecma_7/SIMD/shell.js
@@ -1,39 +1,99 @@
+function assertEqX2(v, arr) {
+    try {
+        assertEq(v.x, arr[0]);
+        assertEq(v.y, arr[1]);
+    } catch (e) {
+        print("stack trace:", e.stack);
+        throw e;
+    }
+}
+
 function assertEqX4(v, arr) {
     try {
         assertEq(v.x, arr[0]);
         assertEq(v.y, arr[1]);
         assertEq(v.z, arr[2]);
         assertEq(v.w, arr[3]);
     } catch (e) {
         print("stack trace:", e.stack);
         throw e;
     }
 }
 
+function simdLength(v) {
+    var pt = Object.getPrototypeOf(v);
+    if (pt === SIMD.int32x4.prototype || pt === SIMD.float32x4.prototype) {
+        return 4;
+    } else if (pt === SIMD.float64x2.prototype) {
+        return 2;
+    } else {
+        throw new TypeError("Unknown SIMD kind.");
+    }
+}
+
+function assertEqVec(v, arr) {
+    var lanes = simdLength(v);
+    if (lanes == 4)
+        assertEqX4(v, arr);
+    else if (lanes == 2)
+        assertEqX2(v, arr);
+    else
+        throw new TypeError("Unknown SIMD kind.");
+}
+
 function simdToArray(v) {
-    return [v.x, v.y, v.z, v.w];
+    var lanes = simdLength(v);
+    if (lanes == 4)
+        return [v.x, v.y, v.z, v.w];
+    else if (lanes == 2)
+        return [v.x, v.y];
+    else
+        throw new TypeError("Unknown SIMD kind.");
 }
 
 const INT32_MAX = Math.pow(2, 31) - 1;
 const INT32_MIN = -Math.pow(2, 31);
 assertEq(INT32_MAX + 1 | 0, INT32_MIN);
 
+function testUnaryFunc(v, simdFunc, func) {
+    var varr = simdToArray(v);
+
+    var observed = simdToArray(simdFunc(v));
+    var expected = varr.map(function(v, i) { return func(varr[i]); });
+
+    for (var i = 0; i < observed.length; i++)
+        assertEq(observed[i], expected[i]);
+}
+
 function testBinaryFunc(v, w, simdFunc, func) {
     var varr = simdToArray(v);
     var warr = simdToArray(w);
 
     var observed = simdToArray(simdFunc(v, w));
     var expected = varr.map(function(v, i) { return func(varr[i], warr[i]); });
 
     for (var i = 0; i < observed.length; i++)
         assertEq(observed[i], expected[i]);
 }
 
+function testBinaryCompare(v, w, simdFunc, func) {
+    var varr = simdToArray(v);
+    var warr = simdToArray(w);
+
+    var inLanes = simdLength(v);
+    var observed = simdToArray(simdFunc(v, w));
+    assertEq(observed.length, 4);
+    for (var i = 0; i < 4; i++) {
+        var j = ((i * inLanes) / 4) | 0;
+        assertEq(observed[i], func(varr[j], warr[j]));
+    }
+}
+
 function testBinaryScalarFunc(v, scalar, simdFunc, func) {
     var varr = simdToArray(v);
 
     var observed = simdToArray(simdFunc(v, scalar));
     var expected = varr.map(function(v, i) { return func(varr[i], scalar); });
 
     for (var i = 0; i < observed.length; i++)
         assertEq(observed[i], expected[i]);
--- a/js/src/tests/ecma_7/SIMD/store.js
+++ b/js/src/tests/ecma_7/SIMD/store.js
@@ -27,26 +27,28 @@ function assertChanged(ta, from, expecte
 }
 
 function testStore(ta, kind, i, v) {
     reset(ta);
     SIMD[kind].storeX(ta, i, v);
     assertChanged(ta, i, [v.x]);
 
     reset(ta);
-    SIMD[kind].storeXY(ta, i, v);
-    assertChanged(ta, i, [v.x, v.y]);
+    SIMD[kind].store(ta, i, v);
+    assertChanged(ta, i, simdToArray(v));
 
-    reset(ta);
-    SIMD[kind].storeXYZ(ta, i, v);
-    assertChanged(ta, i, [v.x, v.y, v.z]);
+    if (simdLength(v) > 2) {
+        reset(ta);
+        SIMD[kind].storeXY(ta, i, v);
+        assertChanged(ta, i, [v.x, v.y]);
 
-    reset(ta);
-    SIMD[kind].store(ta, i, v);
-    assertChanged(ta, i, [v.x, v.y, v.z, v.w]);
+        reset(ta);
+        SIMD[kind].storeXYZ(ta, i, v);
+        assertChanged(ta, i, [v.x, v.y, v.z]);
+    }
 }
 
 function testStoreInt32x4() {
     var I32 = new Int32Array(16);
 
     var v = SIMD.int32x4(0, 1, Math.pow(2,31) - 1, -Math.pow(2, 31));
     testStore(I32, 'int32x4', 0, v);
     testStore(I32, 'int32x4', 1, v);
@@ -73,13 +75,37 @@ function testStoreFloat32x4() {
     testStore(F32, 'float32x4', 2, v);
     testStore(F32, 'float32x4', 12, v);
 
     assertThrowsInstanceOf(() => SIMD.float32x4.store(F32), TypeError);
     assertThrowsInstanceOf(() => SIMD.float32x4.store(F32, 0), TypeError);
     assertThrowsInstanceOf(() => SIMD.int32x4.store(F32, 0, v), TypeError);
 }
 
+function testStoreFloat64x2() {
+    var F64 = new Float64Array(16);
+
+    var v = SIMD.float64x2(1, 2);
+    testStore(F64, 'float64x2', 0, v);
+    testStore(F64, 'float64x2', 1, v);
+    testStore(F64, 'float64x2', 14, v);
+
+    var v = SIMD.float64x2(NaN, -0);
+    testStore(F64, 'float64x2', 0, v);
+    testStore(F64, 'float64x2', 1, v);
+    testStore(F64, 'float64x2', 14, v);
+
+    var v = SIMD.float64x2(-Infinity, +Infinity);
+    testStore(F64, 'float64x2', 0, v);
+    testStore(F64, 'float64x2', 1, v);
+    testStore(F64, 'float64x2', 14, v);
+
+    assertThrowsInstanceOf(() => SIMD.float64x2.store(F64), TypeError);
+    assertThrowsInstanceOf(() => SIMD.float64x2.store(F64, 0), TypeError);
+    assertThrowsInstanceOf(() => SIMD.float32x4.store(F64, 0, v), TypeError);
+}
+
 testStoreInt32x4();
 testStoreFloat32x4();
+testStoreFloat64x2();
 
 if (typeof reportCompare === "function")
     reportCompare(true, true);
--- a/js/src/tests/ecma_7/SIMD/swizzle-shuffle.js
+++ b/js/src/tests/ecma_7/SIMD/swizzle-shuffle.js
@@ -1,47 +1,85 @@
 // |reftest| skip-if(!this.hasOwnProperty("SIMD"))
 
 /*
  * Any copyright is dedicated to the Public Domain.
  * https://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var float32x4 = SIMD.float32x4;
+var float64x2 = SIMD.float64x2;
 var int32x4 = SIMD.int32x4;
 
-function swizzle(arr, x, y, z, w) {
+function swizzle2(arr, x, y) {
+    return [arr[x], arr[y]];
+}
+
+function swizzle4(arr, x, y, z, w) {
     return [arr[x], arr[y], arr[z], arr[w]];
 }
 
+function getNumberOfLanesFromType(type) {
+    switch (type) {
+      case float32x4:
+      case int32x4:
+        return 4;
+      case float64x2:
+        return 2;
+    }
+    throw new TypeError("Unknown SIMD type.");
+}
+
 function testSwizzleForType(type) {
-    var v = type(1,2,3,4);
+    var lanes = getNumberOfLanesFromType(type);
+    var v = lanes == 4 ? type(1, 2, 3, 4) : type(1, 2);
 
     assertThrowsInstanceOf(() => type.swizzle()               , TypeError);
     assertThrowsInstanceOf(() => type.swizzle(v, 0)           , TypeError);
-    assertThrowsInstanceOf(() => type.swizzle(v, 0, 1)        , TypeError);
     assertThrowsInstanceOf(() => type.swizzle(v, 0, 1, 2)     , TypeError);
     assertThrowsInstanceOf(() => type.swizzle(v, 0, 1, 2, 4)  , TypeError);
     assertThrowsInstanceOf(() => type.swizzle(v, 0, 1, 2, -1) , TypeError);
     assertThrowsInstanceOf(() => type.swizzle(0, 1, 2, 3, v)  , TypeError);
 
+    if (lanes == 2) {
+        assertThrowsInstanceOf(() => type.swizzle(v, 0, -1)   , TypeError);
+        assertThrowsInstanceOf(() => type.swizzle(v, 0, 2)    , TypeError);
+    } else {
+        assertEq(lanes, 4);
+        assertThrowsInstanceOf(() => type.swizzle(v, 0, 1), TypeError);
+    }
+
     // Test all possible swizzles.
-    var x, y, z, w;
-    for (var i = 0; i < Math.pow(4, 4); i++) {
-        [x, y, z, w] = [i & 3, (i >> 2) & 3, (i >> 4) & 3, (i >> 6) & 3];
-        assertEqX4(type.swizzle(v, x, y, z, w), swizzle(simdToArray(v), x, y, z, w));
+    if (lanes == 4) {
+        var x, y, z, w;
+        for (var i = 0; i < Math.pow(4, 4); i++) {
+            [x, y, z, w] = [i & 3, (i >> 2) & 3, (i >> 4) & 3, (i >> 6) & 3];
+            assertEqVec(type.swizzle(v, x, y, z, w), swizzle4(simdToArray(v), x, y, z, w));
+        }
+    } else {
+        assertEq(lanes, 2);
+        var x, y;
+        for (var i = 0; i < Math.pow(2, 2); i++) {
+          [x, y] = [x & 1, (y >> 1) & 1];
+          assertEqVec(type.swizzle(v, x, y), swizzle2(simdToArray(v), x, y));
+        }
     }
 
     // Test that the lane inputs are converted into an int32.
     // n.b, order of evaluation of args is left-to-right.
     var obj = {
         x: 0,
         valueOf: function() { return this.x++ }
     };
-    assertEqX4(type.swizzle(v, obj, obj, obj, obj), swizzle(simdToArray(v), 0, 1, 2, 3));
+    if (lanes == 4) {
+        assertEqVec(type.swizzle(v, obj, obj, obj, obj), swizzle4(simdToArray(v), 0, 1, 2, 3));
+    } else {
+        assertEq(lanes, 2);
+        assertEqVec(type.swizzle(v, obj, obj), swizzle2(simdToArray(v), 0, 1));
+    }
 
     // Object for which ToInt32 will fail.
     obj = {
         valueOf: function() { throw new Error; }
     };
     assertThrowsInstanceOf(() => type.swizzle(v, 0, 1, 2, obj), Error);
 }
 
@@ -60,52 +98,98 @@ function testSwizzleFloat32x4() {
 
     assertThrowsInstanceOf(function() {
         int32x4.swizzle(v, 0, 0, 0, 0);
     }, TypeError);
 
     testSwizzleForType(float32x4);
 }
 
-function shuffle(lhsa, rhsa, x, y, z, w) {
+function testSwizzleFloat64x2() {
+    var v = float64x2(1, 2);
+
+    assertThrowsInstanceOf(function() {
+        float32x4.swizzle(v, 0, 0, 0, 0);
+    }, TypeError);
+
+   testSwizzleForType(float64x2);
+}
+
+function shuffle2(lhsa, rhsa, x, y) {
+    return [(x < 2 ? lhsa : rhsa)[x % 2],
+            (y < 2 ? lhsa : rhsa)[y % 2]];
+}
+function shuffle4(lhsa, rhsa, x, y, z, w) {
     return [(x < 4 ? lhsa : rhsa)[x % 4],
             (y < 4 ? lhsa : rhsa)[y % 4],
             (z < 4 ? lhsa : rhsa)[z % 4],
             (w < 4 ? lhsa : rhsa)[w % 4]];
 }
 
 function testShuffleForType(type) {
-    var lhs = type(1,2,3,4);
-    var rhs = type(5,6,7,8);
+    var lanes = getNumberOfLanesFromType(type);
+    var lhs, rhs;
+    if (lanes == 4) {
+        lhs = type(1, 2, 3, 4);
+        rhs = type(5, 6, 7, 8);
+    } else {
+        assertEq(lanes, 2);
+        lhs = type(1, 2);
+        rhs = type(3, 4);
+    }
 
     assertThrowsInstanceOf(() => type.shuffle(lhs)                   , TypeError);
     assertThrowsInstanceOf(() => type.shuffle(lhs, rhs)              , TypeError);
     assertThrowsInstanceOf(() => type.shuffle(lhs, rhs, 0)           , TypeError);
-    assertThrowsInstanceOf(() => type.shuffle(lhs, rhs, 0, 1)        , TypeError);
     assertThrowsInstanceOf(() => type.shuffle(lhs, rhs, 0, 1, 2)     , TypeError);
     assertThrowsInstanceOf(() => type.shuffle(lhs, rhs, 0, 1, 2, -1) , TypeError);
     assertThrowsInstanceOf(() => type.shuffle(lhs, rhs, 0, 1, 2, 8)  , TypeError);
     assertThrowsInstanceOf(() => type.shuffle(lhs, 0, 1, 2, 7, rhs)  , TypeError);
 
+    if (lanes == 2) {
+        assertThrowsInstanceOf(() => type.shuffle(lhs, rhs, 0, 4)    , TypeError);
+        assertThrowsInstanceOf(() => type.shuffle(lhs, rhs, 0, -1)   , TypeError);
+    } else {
+        assertEq(lanes, 4);
+        assertThrowsInstanceOf(() => type.shuffle(lhs, rhs, 0, 1) , TypeError);
+    }
+
     // Test all possible shuffles.
     var x, y, z, w;
-    for (var i = 0; i < Math.pow(8, 4); i++) {
-        [x, y, z, w] = [i & 7, (i >> 3) & 7, (i >> 6) & 7, (i >> 9) & 7];
-        assertEqX4(type.shuffle(lhs, rhs, x, y, z, w),
-                   shuffle(simdToArray(lhs), simdToArray(rhs), x, y, z, w));
+    if (lanes == 4) {
+        var x, y, z, w;
+        for (var i = 0; i < Math.pow(8, 4); i++) {
+            [x, y, z, w] = [i & 7, (i >> 3) & 7, (i >> 6) & 7, (i >> 9) & 7];
+            assertEqVec(type.shuffle(lhs, rhs, x, y, z, w),
+                        shuffle4(simdToArray(lhs), simdToArray(rhs), x, y, z, w));
+        }
+    } else {
+        assertEq(lanes, 2);
+        var x, y;
+        for (var i = 0; i < Math.pow(4, 2); i++) {
+            [x, y] = [i & 3, (i >> 3) & 3];
+            assertEqVec(type.shuffle(lhs, rhs, x, y),
+                        shuffle2(simdToArray(lhs), simdToArray(rhs), x, y));
+        }
     }
 
     // Test that the lane inputs are converted into an int32.
     // n.b, order of evaluation of args is left-to-right.
     var obj = {
         x: 0,
         valueOf: function() { return this.x++ }
     };
-    assertEqX4(type.shuffle(lhs, rhs, obj, obj, obj, obj),
-               shuffle(simdToArray(lhs),simdToArray(rhs), 0, 1, 2, 3));
+    if (lanes == 4) {
+        assertEqVec(type.shuffle(lhs, rhs, obj, obj, obj, obj),
+                    shuffle4(simdToArray(lhs), simdToArray(rhs), 0, 1, 2, 3));
+    } else {
+        assertEq(lanes, 2);
+        assertEqVec(type.shuffle(lhs, rhs, obj, obj),
+                    shuffle2(simdToArray(lhs), simdToArray(rhs), 0, 1));
+    }
 
     // Object for which ToInt32 will fail.
     obj = {
         valueOf: function() { throw new Error; }
     };
     assertThrowsInstanceOf(() => type.shuffle(lhs, rhs, 0, 1, 2, obj), Error);
 }
 
@@ -124,15 +208,27 @@ function testShuffleFloat32x4() {
 
     assertThrowsInstanceOf(function() {
         int32x4.shuffle(v, v, 0, 0, 0, 0);
     }, TypeError);
 
     testShuffleForType(float32x4);
 }
 
+function testShuffleFloat64x2() {
+    var v = float64x2(1, 2);
+
+    assertThrowsInstanceOf(function() {
+        float32x4.shuffle(v, v, 0, 0, 0, 0);
+    }, TypeError);
+
+    testShuffleForType(float64x2);
+}
+
 testSwizzleInt32x4();
 testSwizzleFloat32x4();
+testSwizzleFloat64x2();
 testShuffleInt32x4();
 testShuffleFloat32x4();
+testShuffleFloat64x2();
 
 if (typeof reportCompare === "function")
     reportCompare(true, true);
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -74,16 +74,17 @@
     macro(false, false_, "false") \
     macro(fieldOffsets, fieldOffsets, "fieldOffsets") \
     macro(fieldTypes, fieldTypes, "fieldTypes") \
     macro(fileName, fileName, "fileName") \
     macro(fix, fix, "fix") \
     macro(float32, float32, "float32") \
     macro(float32x4, float32x4, "float32x4") \
     macro(float64, float64, "float64") \
+    macro(float64x2, float64x2, "float64x2") \
     macro(forceInterpreter, forceInterpreter, "forceInterpreter") \
     macro(format, format, "format") \
     macro(frame, frame, "frame") \
     macro(from, from, "from") \
     macro(get, get, "get") \
     macro(getInternals, getInternals, "getInternals") \
     macro(getOwnPropertyDescriptor, getOwnPropertyDescriptor, "getOwnPropertyDescriptor") \
     macro(getOwnPropertyNames, getOwnPropertyNames, "getOwnPropertyNames") \
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -99,18 +99,19 @@ class GlobalObject : public NativeObject
     static const unsigned NUMBER_FORMAT_PROTO     = COLLATOR_PROTO + 1;
     static const unsigned DATE_TIME_FORMAT_PROTO  = NUMBER_FORMAT_PROTO + 1;
     static const unsigned REGEXP_STATICS          = DATE_TIME_FORMAT_PROTO + 1;
     static const unsigned WARNED_WATCH_DEPRECATED = REGEXP_STATICS + 1;
     static const unsigned WARNED_PROTO_SETTING_SLOW = WARNED_WATCH_DEPRECATED + 1;
     static const unsigned RUNTIME_CODEGEN_ENABLED = WARNED_PROTO_SETTING_SLOW + 1;
     static const unsigned DEBUGGERS               = RUNTIME_CODEGEN_ENABLED + 1;
     static const unsigned INTRINSICS              = DEBUGGERS + 1;
-    static const unsigned FLOAT32X4_TYPE_DESCR   = INTRINSICS + 1;
-    static const unsigned INT32X4_TYPE_DESCR     = FLOAT32X4_TYPE_DESCR + 1;
+    static const unsigned FLOAT32X4_TYPE_DESCR    = INTRINSICS + 1;
+    static const unsigned FLOAT64X2_TYPE_DESCR    = FLOAT32X4_TYPE_DESCR + 1;
+    static const unsigned INT32X4_TYPE_DESCR      = FLOAT64X2_TYPE_DESCR + 1;
     static const unsigned FOR_OF_PIC_CHAIN        = INT32X4_TYPE_DESCR + 1;
 
     /* Total reserved-slot count for global objects. */
     static const unsigned RESERVED_SLOTS = FOR_OF_PIC_CHAIN + 1;
 
     /*
      * The slot count must be in the public API for JSCLASS_GLOBAL_FLAGS, and
      * we won't expose GlobalObject, so just assert that the two values are
@@ -403,16 +404,26 @@ class GlobalObject : public NativeObject
         setSlot(FLOAT32X4_TYPE_DESCR, ObjectValue(obj));
     }
 
     JSObject &float32x4TypeDescr() {
         MOZ_ASSERT(getSlotRef(FLOAT32X4_TYPE_DESCR).isObject());
         return getSlotRef(FLOAT32X4_TYPE_DESCR).toObject();
     }
 
+    void setFloat64x2TypeDescr(JSObject &obj) {
+        MOZ_ASSERT(getSlotRef(FLOAT64X2_TYPE_DESCR).isUndefined());
+        setSlot(FLOAT64X2_TYPE_DESCR, ObjectValue(obj));
+    }
+
+    JSObject &float64x2TypeDescr() {
+        MOZ_ASSERT(getSlotRef(FLOAT64X2_TYPE_DESCR).isObject());
+        return getSlotRef(FLOAT64X2_TYPE_DESCR).toObject();
+    }
+
     void setInt32x4TypeDescr(JSObject &obj) {
         MOZ_ASSERT(getSlotRef(INT32X4_TYPE_DESCR).isUndefined());
         setSlot(INT32X4_TYPE_DESCR, ObjectValue(obj));
     }
 
     JSObject &int32x4TypeDescr() {
         MOZ_ASSERT(getSlotRef(INT32X4_TYPE_DESCR).isObject());
         return getSlotRef(INT32X4_TYPE_DESCR).toObject();
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -964,16 +964,17 @@ static const JSFunctionSpec intrinsic_fu
     JS_FN("TypedObjectIsAttached",          js::TypedObjectIsAttached, 1, 0),
     JS_FN("TypedObjectTypeDescr",           js::TypedObjectTypeDescr, 1, 0),
     JS_FN("ObjectIsOpaqueTypedObject",      js::ObjectIsOpaqueTypedObject, 1, 0),
     JS_FN("TypeDescrIsArrayType",           js::TypeDescrIsArrayType, 1, 0),
     JS_FN("TypeDescrIsSimpleType",          js::TypeDescrIsSimpleType, 1, 0),
     JS_FN("ClampToUint8",                   js::ClampToUint8, 1, 0),
     JS_FN("GetTypedObjectModule",           js::GetTypedObjectModule, 0, 0),
     JS_FN("GetFloat32x4TypeDescr",          js::GetFloat32x4TypeDescr, 0, 0),
+    JS_FN("GetFloat64x2TypeDescr",          js::GetFloat64x2TypeDescr, 0, 0),
     JS_FN("GetInt32x4TypeDescr",            js::GetInt32x4TypeDescr, 0, 0),
 
 #define LOAD_AND_STORE_SCALAR_FN_DECLS(_constant, _type, _name)         \
     JS_FN("Store_" #_name, js::StoreScalar##_type::Func, 3, 0),         \
     JS_FN("Load_" #_name,  js::LoadScalar##_type::Func, 3, 0),
     JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(LOAD_AND_STORE_SCALAR_FN_DECLS)
 #undef LOAD_AND_STORE_SCALAR_FN_DECLS
 
--- a/layout/base/SelectionCarets.cpp
+++ b/layout/base/SelectionCarets.cpp
@@ -579,28 +579,40 @@ SelectionCarets::SelectWord()
   if (!selectable) {
     SELECTIONCARETS_LOG(" frame %p is not selectable", ptFrame);
     return NS_ERROR_FAILURE;
   }
 
   nsPoint ptInFrame = mDownPoint;
   nsLayoutUtils::TransformPoint(rootFrame, ptFrame, ptInFrame);
 
-  // If target frame is editable, we should move focus to targe frame. If
-  // target frame isn't editable and our focus content is editable, we should
+  nsIFrame* currFrame = ptFrame;
+  nsIContent* newFocusContent = nullptr;
+  while (currFrame) {
+    int32_t tabIndexUnused = 0;
+    if (currFrame->IsFocusable(&tabIndexUnused, true)) {
+      newFocusContent = currFrame->GetContent();
+      nsCOMPtr<nsIDOMElement> domElement(do_QueryInterface(newFocusContent));
+      if (domElement)
+        break;
+    }
+    currFrame = currFrame->GetParent();
+  }
+
+
+  // If target frame is focusable, we should move focus to it. If target frame
+  // isn't focusable, and our previous focused content is editable, we should
   // clear focus.
   nsFocusManager* fm = nsFocusManager::GetFocusManager();
   nsIContent* editingHost = ptFrame->GetContent()->GetEditingHost();
-  if (editingHost) {
-    nsCOMPtr<nsIDOMElement> elt = do_QueryInterface(editingHost->GetParent());
-    if (elt) {
-      fm->SetFocus(elt, 0);
-    }
+  if (newFocusContent && currFrame) {
+    nsCOMPtr<nsIDOMElement> domElement(do_QueryInterface(newFocusContent));
+    fm->SetFocus(domElement,0);
 
-    if (!nsContentUtils::HasNonEmptyTextContent(
+    if (editingHost && !nsContentUtils::HasNonEmptyTextContent(
           editingHost, nsContentUtils::eRecurseIntoChildren)) {
       SELECTIONCARETS_LOG("Select a editable content %p with empty text",
                           editingHost);
       // Long tap on the content with empty text, no action for
       // selectioncarets but need to dispatch the touchcarettap event
       // to support the short cut mode
       DispatchCustomEvent(NS_LITERAL_STRING("touchcarettap"));
       return NS_OK;
--- a/layout/base/tests/marionette/test_selectioncarets.py
+++ b/layout/base/tests/marionette/test_selectioncarets.py
@@ -34,37 +34,53 @@ class SelectionCaretsTest(MarionetteTest
         self.marionette.navigate(test_html)
 
         self._input = self.marionette.find_element(*self._input_selector)
         self._textarea = self.marionette.find_element(*self._textarea_selector)
         self._textarea_rtl = self.marionette.find_element(*self._textarea_rtl_selector)
         self._contenteditable = self.marionette.find_element(*self._contenteditable_selector)
         self._content = self.marionette.find_element(*self._content_selector)
 
-    def _long_press_to_select_first_word(self, el, sel):
-        # Move caret inside the first word.
+    def _first_word_location(self, el):
+        '''Get the location (x, y) of the first word in el.
+
+        Note: this function has a side effect which changes focus to the
+        target element el.
+
+        '''
+        sel = SelectionManager(el)
+
+        # Move caret behind the first character to get the location of the first
+        # word.
         el.tap()
         sel.move_caret_to_front()
         sel.move_caret_by_offset(1)
-        x, y = sel.caret_location()
+
+        return sel.caret_location()
+
+    def _long_press_to_select(self, el, x, y):
+        '''Long press the location (x, y) to select a word.
 
-        # Long press the caret position. Selection carets should appear, and the
-        # first word will be selected. On Windows, those spaces after the word
-        # will also be selected.
-        long_press_without_contextmenu(self.marionette, el, self._long_press_time, x, y)
+        SelectionCarets should appear. On Windows, those spaces after the
+        word will also be selected.
+
+        '''
+        long_press_without_contextmenu(self.marionette, el, self._long_press_time,
+                                       x, y)
 
     def _test_long_press_to_select_a_word(self, el, assertFunc):
         sel = SelectionManager(el)
         original_content = sel.content
         words = original_content.split()
         self.assertTrue(len(words) >= 2, 'Expect at least two words in the content.')
         target_content = words[0]
 
         # Goal: Select the first word.
-        self._long_press_to_select_first_word(el, sel)
+        x, y = self._first_word_location(el)
+        self._long_press_to_select(el, x, y)
 
         # Ignore extra spaces selected after the word.
         assertFunc(target_content, sel.selected_content.rstrip())
 
     def _test_move_selection_carets(self, el, assertFunc):
         sel = SelectionManager(el)
         original_content = sel.content
         words = original_content.split()
@@ -74,60 +90,98 @@ class SelectionCaretsTest(MarionetteTest
         target_content = original_content[len(words[0]):]
 
         # Get the location of the selection carets at the end of the content for
         # later use.
         el.tap()
         sel.select_all()
         (_, _), (end_caret_x, end_caret_y) = sel.selection_carets_location()
 
-        self._long_press_to_select_first_word(el, sel)
+        x, y = self._first_word_location(el)
+        self._long_press_to_select(el, x, y)
 
         # Move the right caret to the end of the content.
         (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location()
         self.actions.flick(el, caret2_x, caret2_y, end_caret_x, end_caret_y).perform()
 
         # Move the left caret to the previous position of the right caret.
         self.actions.flick(el, caret1_x, caret2_y, caret2_x, caret2_y).perform()
 
         # Ignore extra spaces at the beginning of the content in comparison.
         assertFunc(target_content.lstrip(), sel.selected_content.lstrip())
 
-    def _test_minimum_select_one_character(self, el, assertFunc):
+    def _test_minimum_select_one_character(self, el, assertFunc,
+                                           x=None, y=None):
         sel = SelectionManager(el)
         original_content = sel.content
         words = original_content.split()
         self.assertTrue(len(words) >= 1, 'Expect at least one word in the content.')
 
         # Goal: Select the first character.
         target_content = original_content[0]
 
-        self._long_press_to_select_first_word(el, sel)
+        if x and y:
+            # If we got x and y from the arguments, use it as a hint of the
+            # location of the first word
+            pass
+        else:
+            x, y = self._first_word_location(el)
+        self._long_press_to_select(el, x, y)
 
         # Move the right caret to the position of the left caret.
         (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location()
         self.actions.flick(el, caret2_x, caret2_y, caret1_x, caret1_y,).perform()
 
         assertFunc(target_content, sel.selected_content)
 
+    def _test_focus_obtained_by_long_press(self, el1, el2):
+        '''Test the focus could be changed from el1 to el2 by long press.
+
+        If the focus is changed to e2 successfully, SelectionCarets should
+        appear and could be dragged.
+
+        '''
+        # Goal: Tap to focus el1, and then select the first character on
+        # el2.
+
+        # We want to collect the location of the first word in el2 here
+        # since self._first_word_location() has the side effect which would
+        # change the focus.
+        x, y = self._first_word_location(el2)
+        el1.tap()
+        self._test_minimum_select_one_character(el2, self.assertEqual,
+                                                x=x, y=y)
+
     ########################################################################
     # <input> test cases with selection carets enabled
     ########################################################################
     def test_input_long_press_to_select_a_word(self):
         self.openTestHtml(enabled=True)
         self._test_long_press_to_select_a_word(self._input, self.assertEqual)
 
     def test_input_move_selection_carets(self):
         self.openTestHtml(enabled=True)
         self._test_move_selection_carets(self._input, self.assertEqual)
 
     def test_input_minimum_select_one_caracter(self):
         self.openTestHtml(enabled=True)
         self._test_minimum_select_one_character(self._input, self.assertEqual)
 
+    def test_input_focus_obtained_by_long_press_from_textarea(self):
+        self.openTestHtml(enabled=True)
+        self._test_focus_obtained_by_long_press(self._textarea, self._input)
+
+    def test_input_focus_obtained_by_long_press_from_contenteditable(self):
+        self.openTestHtml(enabled=True)
+        self._test_focus_obtained_by_long_press(self._contenteditable, self._input)
+
+    def test_input_focus_obtained_by_long_press_from_content_non_editable(self):
+        self.openTestHtml(enabled=True)
+        self._test_focus_obtained_by_long_press(self._content, self._input)
+
     ########################################################################
     # <input> test cases with selection carets disabled
     ########################################################################
     def test_input_long_press_to_select_a_word_disabled(self):
         self.openTestHtml(enabled=False)
         self._test_long_press_to_select_a_word(self._input, self.assertNotEqual)
 
     def test_input_move_selection_carets_disabled(self):
@@ -144,16 +198,28 @@ class SelectionCaretsTest(MarionetteTest
     def test_textarea_move_selection_carets(self):
         self.openTestHtml(enabled=True)
         self._test_move_selection_carets(self._textarea, self.assertEqual)
 
     def test_textarea_minimum_select_one_caracter(self):
         self.openTestHtml(enabled=True)
         self._test_minimum_select_one_character(self._textarea, self.assertEqual)
 
+    def test_textarea_focus_obtained_by_long_press_from_input(self):
+        self.openTestHtml(enabled=True)
+        self._test_focus_obtained_by_long_press(self._input, self._textarea)
+
+    def test_textarea_focus_obtained_by_long_press_from_contenteditable(self):
+        self.openTestHtml(enabled=True)
+        self._test_focus_obtained_by_long_press(self._contenteditable, self._textarea)
+
+    def test_textarea_focus_obtained_by_long_press_from_content_non_editable(self):
+        self.openTestHtml(enabled=True)
+        self._test_focus_obtained_by_long_press(self._content, self._textarea)
+
     ########################################################################
     # <textarea> test cases with selection carets disabled
     ########################################################################
     def test_textarea_long_press_to_select_a_word_disabled(self):
         self.openTestHtml(enabled=False)
         self._test_long_press_to_select_a_word(self._textarea, self.assertNotEqual)
 
     def test_textarea_move_selection_carets_disable(self):
@@ -196,16 +262,28 @@ class SelectionCaretsTest(MarionetteTest
     def test_contenteditable_move_selection_carets(self):
         self.openTestHtml(enabled=True)
         self._test_move_selection_carets(self._contenteditable, self.assertEqual)
 
     def test_contenteditable_minimum_select_one_character(self):
         self.openTestHtml(enabled=True)
         self._test_minimum_select_one_character(self._contenteditable, self.assertEqual)
 
+    def test_contenteditable_focus_obtained_by_long_press_from_input(self):
+        self.openTestHtml(enabled=True)
+        self._test_focus_obtained_by_long_press(self._input, self._contenteditable)
+
+    def test_contenteditable_focus_obtained_by_long_press_from_textarea(self):
+        self.openTestHtml(enabled=True)
+        self._test_focus_obtained_by_long_press(self._textarea, self._contenteditable)
+
+    def test_contenteditable_focus_obtained_by_long_press_from_content_non_editable(self):
+        self.openTestHtml(enabled=True)
+        self._test_focus_obtained_by_long_press(self._content, self._contenteditable)
+
     ########################################################################
     # <div> contenteditable test cases with selection carets disabled
     ########################################################################
     def test_contenteditable_long_press_to_select_a_word_disabled(self):
         self.openTestHtml(enabled=False)
         self._test_long_press_to_select_a_word(self._contenteditable, self.assertNotEqual)
 
     def test_contenteditable_move_selection_carets_disabled(self):
@@ -213,8 +291,21 @@ class SelectionCaretsTest(MarionetteTest
         self._test_move_selection_carets(self._contenteditable, self.assertNotEqual)
 
     ########################################################################
     # <div> non-editable test cases with selection carets enabled
     ########################################################################
     def test_content_non_editable_minimum_select_one_character_by_selection(self):
         self.openTestHtml(enabled=True)
         self._test_minimum_select_one_character(self._content, self.assertEqual)
+
+    def test_content_non_editable_focus_obtained_by_long_press_from_input(self):
+        self.openTestHtml(enabled=True)
+        self._test_focus_obtained_by_long_press(self._input, self._content)
+
+    def test_content_non_editable_focus_obtained_by_long_press_from_textarea(self):
+        self.openTestHtml(enabled=True)
+        self._test_focus_obtained_by_long_press(self._textarea, self._content)
+
+    def test_content_non_editable_focus_obtained_by_long_press_from_contenteditable(self):
+        self.openTestHtml(enabled=True)
+        self._test_focus_obtained_by_long_press(self._contenteditable, self._content)
+
--- a/layout/style/nsCSSRuleProcessor.cpp
+++ b/layout/style/nsCSSRuleProcessor.cpp
@@ -2920,18 +2920,34 @@ nsCSSRuleProcessor::MediumFeaturesChange
   }
 
   return false;
 }
 
 UniquePtr<nsMediaQueryResultCacheKey>
 nsCSSRuleProcessor::CloneMQCacheKey()
 {
+  MOZ_ASSERT(!(mRuleCascades && mPreviousCacheKey));
+
   RuleCascadeData* c = mRuleCascades;
-  if (!c || !c->mCacheKey.HasFeatureConditions()) {
+  if (!c) {
+    // We might have an mPreviousCacheKey.  It already comes from a call
+    // to CloneMQCacheKey, so don't bother checking
+    // HasFeatureConditions().
+    if (mPreviousCacheKey) {
+      NS_ASSERTION(mPreviousCacheKey->HasFeatureConditions(),
+                   "we shouldn't have a previous cache key unless it has "
+                   "feature conditions");
+      return MakeUnique<nsMediaQueryResultCacheKey>(*mPreviousCacheKey);
+    }
+
+    return UniquePtr<nsMediaQueryResultCacheKey>();
+  }
+
+  if (!c->mCacheKey.HasFeatureConditions()) {
     return UniquePtr<nsMediaQueryResultCacheKey>();
   }
 
   return MakeUnique<nsMediaQueryResultCacheKey>(c->mCacheKey);
 }
 
 /* virtual */ size_t
 nsCSSRuleProcessor::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
--- a/layout/style/test/test_bug1089417.html
+++ b/layout/style/test/test_bug1089417.html
@@ -19,16 +19,23 @@ https://bugzilla.mozilla.org/show_bug.cg
     var fwin = f.contentWindow;
     var fdoc = f.contentDocument;
 
     f.height = "400";
     fdoc.getElementById("s").disabled = false;
     is(fwin.getComputedStyle(fdoc.documentElement).backgroundColor,
        "rgb(0, 128, 0)",
        "media query change should have restyled");
+
+    f.height = "200";
+    fdoc.getElementById("s").disabled = true;
+    fdoc.getElementById("s").disabled = false;
+    is(fwin.getComputedStyle(fdoc.documentElement).backgroundColor,
+       "rgb(255, 0, 0)",
+       "media query change should have restyled");
     SimpleTest.finish();
   }
 
   </script>
 </head>
 <body onload="run()">
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1089417">Mozilla Bug 1089417</a>
 <div id="display">
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -3747,23 +3747,30 @@ pref("browser.zoom.reflowZoom.reflowTime
  * but has no effect if browser.zoom.reflowOnZoom is disabled.
  *
  * Note that this should be turned off only in cases where debugging of the
  * reflow-on-zoom feature is necessary, and enabling the feature during
  * a page load inhbits this debugging.
  */
 pref("browser.zoom.reflowZoom.reflowTextOnPageLoad", true);
 
+//
 // Image-related prefs
+//
+
 // The maximum size, in bytes, of the decoded images we cache
 pref("image.cache.size", 5242880);
+
 // A weight, from 0-1000, to place on time when comparing to size.
 // Size is given a weight of 1000 - timeweight.
 pref("image.cache.timeweight", 500);
 
+// Whether we attempt to downscale images during decoding.
+pref("image.downscale-during-decode.enabled", false);
+
 // The default Accept header sent for images loaded over HTTP(S)
 pref("image.http.accept", "image/png,image/*;q=0.8,*/*;q=0.5");
 
 pref("image.high_quality_downscaling.enabled", true);
 
 // The minimum percent downscaling we'll use high-quality downscaling on,
 // interpreted as a floating-point number / 1000.
 pref("image.high_quality_downscaling.min_factor", 1000);
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -1361,16 +1361,25 @@ HttpBaseChannel::IsNoCacheResponse(bool 
     return NS_ERROR_NOT_AVAILABLE;
   *value = mResponseHead->NoCache();
   if (!*value)
     *value = mResponseHead->ExpiresInPast();
   return NS_OK;
 }
 
 NS_IMETHODIMP
+HttpBaseChannel::IsPrivateResponse(bool *value)
+{
+  if (!mResponseHead)
+    return NS_ERROR_NOT_AVAILABLE;
+  *value = mResponseHead->Private();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 HttpBaseChannel::GetResponseStatus(uint32_t *aValue)
 {
   if (!mResponseHead)
     return NS_ERROR_NOT_AVAILABLE;
   *aValue = mResponseHead->Status();
   return NS_OK;
 }
 
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -148,16 +148,17 @@ public:
   NS_IMETHOD GetAllowPipelining(bool *value) MOZ_OVERRIDE;
   NS_IMETHOD SetAllowPipelining(bool value) MOZ_OVERRIDE;
   NS_IMETHOD GetAllowSTS(bool *value) MOZ_OVERRIDE;
   NS_IMETHOD SetAllowSTS(bool value) MOZ_OVERRIDE;
   NS_IMETHOD GetRedirectionLimit(uint32_t *value) MOZ_OVERRIDE;
   NS_IMETHOD SetRedirectionLimit(uint32_t value) MOZ_OVERRIDE;
   NS_IMETHOD IsNoStoreResponse(bool *value) MOZ_OVERRIDE;
   NS_IMETHOD IsNoCacheResponse(bool *value) MOZ_OVERRIDE;
+  NS_IMETHOD IsPrivateResponse(bool *value) MOZ_OVERRIDE;
   NS_IMETHOD GetResponseStatus(uint32_t *aValue) MOZ_OVERRIDE;
   NS_IMETHOD GetResponseStatusText(nsACString& aValue) MOZ_OVERRIDE;
   NS_IMETHOD GetRequestSucceeded(bool *aValue) MOZ_OVERRIDE;
   NS_IMETHOD RedirectTo(nsIURI *newURI) MOZ_OVERRIDE;
 
   // nsIHttpChannelInternal
   NS_IMETHOD GetDocumentURI(nsIURI **aDocumentURI) MOZ_OVERRIDE;
   NS_IMETHOD SetDocumentURI(nsIURI *aDocumentURI) MOZ_OVERRIDE;
--- a/netwerk/protocol/http/NullHttpChannel.cpp
+++ b/netwerk/protocol/http/NullHttpChannel.cpp
@@ -189,16 +189,22 @@ NullHttpChannel::IsNoStoreResponse(bool 
 
 NS_IMETHODIMP
 NullHttpChannel::IsNoCacheResponse(bool *_retval)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
+NullHttpChannel::IsPrivateResponse(bool *_retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
 NullHttpChannel::RedirectTo(nsIURI *aNewURI)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 //-----------------------------------------------------------------------------
 // NullHttpChannel::nsIChannel
 //-----------------------------------------------------------------------------
--- a/netwerk/protocol/http/PHttpChannelParams.h
+++ b/netwerk/protocol/http/PHttpChannelParams.h
@@ -140,30 +140,32 @@ struct ParamTraits<mozilla::net::nsHttpR
   {
     WriteParam(aMsg, aParam.mHeaders);
     WriteParam(aMsg, aParam.mVersion);
     WriteParam(aMsg, aParam.mStatus);
     WriteParam(aMsg, aParam.mStatusText);
     WriteParam(aMsg, aParam.mContentLength);
     WriteParam(aMsg, aParam.mContentType);
     WriteParam(aMsg, aParam.mContentCharset);
+    WriteParam(aMsg, aParam.mCacheControlPrivate);
     WriteParam(aMsg, aParam.mCacheControlNoStore);
     WriteParam(aMsg, aParam.mCacheControlNoCache);
     WriteParam(aMsg, aParam.mPragmaNoCache);
   }
 
   static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
   {
     if (!ReadParam(aMsg, aIter, &aResult->mHeaders)             ||
         !ReadParam(aMsg, aIter, &aResult->mVersion)             ||
         !ReadParam(aMsg, aIter, &aResult->mStatus)              ||
         !ReadParam(aMsg, aIter, &aResult->mStatusText)          ||
         !ReadParam(aMsg, aIter, &aResult->mContentLength)       ||
         !ReadParam(aMsg, aIter, &aResult->mContentType)         ||
         !ReadParam(aMsg, aIter, &aResult->mContentCharset)      ||
+        !ReadParam(aMsg, aIter, &aResult->mCacheControlPrivate) ||
         !ReadParam(aMsg, aIter, &aResult->mCacheControlNoStore) ||
         !ReadParam(aMsg, aIter, &aResult->mCacheControlNoCache) ||
         !ReadParam(aMsg, aIter, &aResult->mPragmaNoCache))
       return false;
 
     return true;
   }
 };
--- a/netwerk/protocol/http/nsHttpResponseHead.cpp
+++ b/netwerk/protocol/http/nsHttpResponseHead.cpp
@@ -619,16 +619,17 @@ nsHttpResponseHead::Reset()
 {
     LOG(("nsHttpResponseHead::Reset\n"));
 
     ClearHeaders();
 
     mVersion = NS_HTTP_VERSION_1_1;
     mStatus = 200;
     mContentLength = UINT64_MAX;
+    mCacheControlPrivate = false;
     mCacheControlNoStore = false;
     mCacheControlNoCache = false;
     mPragmaNoCache = false;
     mStatusText.Truncate();
     mContentType.Truncate();
     mContentCharset.Truncate();
 }
 
@@ -787,21 +788,26 @@ nsHttpResponseHead::ParseVersion(const c
         mVersion = NS_HTTP_VERSION_1_0;
 }
 
 void
 nsHttpResponseHead::ParseCacheControl(const char *val)
 {
     if (!(val && *val)) {
         // clear flags
+        mCacheControlPrivate = false;
         mCacheControlNoCache = false;
         mCacheControlNoStore = false;
         return;
     }
 
+    // search header value for occurrence of "private"
+    if (nsHttp::FindToken(val, "private", HTTP_HEADER_VALUE_SEPS))
+        mCacheControlPrivate = true;
+
     // search header value for occurrence(s) of "no-cache" but ignore
     // occurrence(s) of "no-cache=blah"
     if (nsHttp::FindToken(val, "no-cache", HTTP_HEADER_VALUE_SEPS))
         mCacheControlNoCache = true;
 
     // search header value for occurrence of "no-store"
     if (nsHttp::FindToken(val, "no-store", HTTP_HEADER_VALUE_SEPS))
         mCacheControlNoStore = true;
--- a/netwerk/protocol/http/nsHttpResponseHead.h
+++ b/netwerk/protocol/http/nsHttpResponseHead.h
@@ -18,30 +18,32 @@ namespace mozilla { namespace net {
 //-----------------------------------------------------------------------------
 
 class nsHttpResponseHead
 {
 public:
     nsHttpResponseHead() : mVersion(NS_HTTP_VERSION_1_1)
                          , mStatus(200)
                          , mContentLength(UINT64_MAX)
+                         , mCacheControlPrivate(false)
                          , mCacheControlNoStore(false)
                          , mCacheControlNoCache(false)
                          , mPragmaNoCache(false) {}
 
     const nsHttpHeaderArray & Headers()   const { return mHeaders; }
     nsHttpHeaderArray    &Headers()             { return mHeaders; }
     nsHttpVersion         Version()       const { return mVersion; }
 // X11's Xlib.h #defines 'Status' to 'int' on some systems!
 #undef Status
     uint16_t              Status()        const { return mStatus; }
     const nsAFlatCString &StatusText()    const { return mStatusText; }
     int64_t               ContentLength() const { return mContentLength; }
     const nsAFlatCString &ContentType()   const { return mContentType; }
     const nsAFlatCString &ContentCharset() const { return mContentCharset; }
+    bool                  Private() const { return mCacheControlPrivate; }
     bool                  NoStore() const { return mCacheControlNoStore; }
     bool                  NoCache() const { return (mCacheControlNoCache || mPragmaNoCache); }
     /**
      * Full length of the entity. For byte-range requests, this may be larger
      * than ContentLength(), which will only represent the requested part of the
      * entity.
      */
     int64_t               TotalEntitySize() const;
@@ -123,16 +125,17 @@ private:
     // All members must be copy-constructable and assignable
     nsHttpHeaderArray mHeaders;
     nsHttpVersion     mVersion;
     uint16_t          mStatus;
     nsCString         mStatusText;
     int64_t           mContentLength;
     nsCString         mContentType;
     nsCString         mContentCharset;
+    bool              mCacheControlPrivate;
     bool              mCacheControlNoStore;
     bool              mCacheControlNoCache;
     bool              mPragmaNoCache;
 
     friend struct IPC::ParamTraits<nsHttpResponseHead>;
 };
 }} // namespace mozilla::net
 
--- a/netwerk/protocol/http/nsIHttpChannel.idl
+++ b/netwerk/protocol/http/nsIHttpChannel.idl
@@ -9,17 +9,17 @@ interface nsIHttpHeaderVisitor;
 
 /**
  * nsIHttpChannel
  *
  * This interface allows for the modification of HTTP request parameters and
  * the inspection of the resulting HTTP response status and headers when they
  * become available.
  */
-[scriptable, uuid(82083578-fb78-4f9a-953c-cecbae500697)]
+[scriptable, uuid(a8bed710-653c-4ea4-9747-a629cc482cf8)]
 interface nsIHttpChannel : nsIChannel
 {
     /**************************************************************************
      * REQUEST CONFIGURATION
      *
      * Modifying request parameters after asyncOpen has been called is an error.
      */
 
@@ -295,16 +295,25 @@ interface nsIHttpChannel : nsIChannel
      * in the past relative to the value of the "Date" header.
      *
      * @throws NS_ERROR_NOT_AVAILABLE if called before the response
      *         has been received (before onStartRequest).
      */
     boolean isNoCacheResponse();
 
     /**
+     * Returns true if the server sent a "Cache-Control: private" response
+     * header.
+     *
+     * @throws NS_ERROR_NOT_AVAILABLE if called before the response
+     *         has been received (before onStartRequest).
+     */
+    boolean isPrivateResponse();
+
+    /**
      * Instructs the channel to immediately redirect to a new destination.
      * Can only be called on channels not yet opened.
      *
      * This method provides no explicit conflict resolution. The last
      * caller to call it wins.
      *
      * @throws NS_ERROR_ALREADY_OPENED if called after the channel
      *         has been opened.
--- a/netwerk/protocol/viewsource/nsViewSourceChannel.cpp
+++ b/netwerk/protocol/viewsource/nsViewSourceChannel.cpp
@@ -764,14 +764,21 @@ nsViewSourceChannel::IsNoStoreResponse(b
 NS_IMETHODIMP
 nsViewSourceChannel::IsNoCacheResponse(bool *_retval)
 {
     return !mHttpChannel ? NS_ERROR_NULL_POINTER :
         mHttpChannel->IsNoCacheResponse(_retval);
 }
 
 NS_IMETHODIMP
+nsViewSourceChannel::IsPrivateResponse(bool *_retval)
+{
+    return !mHttpChannel ? NS_ERROR_NULL_POINTER :
+        mHttpChannel->IsPrivateResponse(_retval);
+}
+
+NS_IMETHODIMP
 nsViewSourceChannel::RedirectTo(nsIURI *uri)
 {
     return !mHttpChannel ? NS_ERROR_NULL_POINTER :
         mHttpChannel->RedirectTo(uri);
 }
 
--- a/netwerk/test/TestHttp.cpp
+++ b/netwerk/test/TestHttp.cpp
@@ -166,17 +166,17 @@ int main(int argc, char **argv)
         RETURN_IF_FAILED(rv, "NS_NewURI");
 
         rv = NS_NewChannel(getter_AddRefs(chan),
                            uri,
                            nsContentUtils::GetSystemPrincipal(),
                            nsILoadInfo::SEC_NORMAL,
                            nsIContentPolicy::TYPE_OTHER);
 
-        RETURN_IF_FAILED(rv, "NS_OpenURI");
+        RETURN_IF_FAILED(rv, "NS_NewChannel");
 
         rv = chan->AsyncOpen(listener, nullptr);
         RETURN_IF_FAILED(rv, "AsyncOpen");
 
         while (gKeepRunning)
             gEventQ->ProcessPendingEvents();
 
         printf(">>> done\n");
deleted file mode 100644
--- a/netwerk/test/TestPageLoad.cpp
+++ /dev/null
@@ -1,398 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "TestCommon.h"
-#include "nsNetUtil.h"
-#include "nsIServiceManager.h"
-#include "nsIInterfaceRequestor.h"
-#include "nsIInterfaceRequestorUtils.h"
-#include "nsIProgressEventSink.h"
-#include "nsIComponentManager.h"
-#include "prprf.h"
-#include "nsXPCOM.h"
-#include "nsISupportsPrimitives.h"
-#include "plstr.h"
-#include "nsCOMArray.h"
-#include "nsIComponentRegistrar.h"
-#include <algorithm>
-#include "nsIScriptSecurityManager.h"
-
-namespace TestPageLoad {
-
-int getStrLine(const char *src, char *str, int ind, int max);
-nsresult auxLoad(char *uriBuf);
-//----------------------------------------------------------------------
-
-
-#define RETURN_IF_FAILED(rv, ret, step) \
-    PR_BEGIN_MACRO \
-    if (NS_FAILED(rv)) { \
-        printf(">>> %s failed: rv=%x\n", step, static_cast<uint32_t>(rv)); \
-        return ret;\
-    } \
-    PR_END_MACRO
-
-static nsCString globalStream;
-//static char urlBuf[256];
-static nsCOMPtr<nsIURI> baseURI;
-static nsCOMArray<nsIURI> uriList;
-
-//Temp, should remove:
-static int numStart=0;
-static int numFound=0;
-
-static int32_t gKeepRunning = 0;
-
-
-//--------writer fun----------------------
-
-static NS_METHOD streamParse (nsIInputStream* in,
-                              void* closure,
-                              const char* fromRawSegment,
-                              uint32_t toOffset,
-                              uint32_t count,
-                              uint32_t *writeCount) {
-
-  char parseBuf[2048], loc[2048], lineBuf[2048];
-  char *loc_t, *loc_t2;
-  int i = 0;
-  const char *tmp;
-
-  if(!globalStream.IsEmpty()) {
-    globalStream.Append(fromRawSegment);
-    tmp = globalStream.get();
-    //printf("\n>>NOW:\n^^^^^\n%s\n^^^^^^^^^^^^^^", tmp);
-  } else {
-    tmp = fromRawSegment;
-  }
-
-  while(i < (int)count) {
-    i = getStrLine(tmp, lineBuf, i, count);
-    if(i < 0) {
-      *writeCount = count;
-      return NS_OK;
-    }
-    parseBuf[0]='\0';
-    if((loc_t=PL_strcasestr(lineBuf, "img"))!= nullptr 
-       || (loc_t=PL_strcasestr(lineBuf, "script"))!=nullptr) {
-      loc_t2=PL_strcasestr(loc_t, "src");
-      if(loc_t2!=nullptr) {
-        loc_t2+=3;
-        strcpy(loc, loc_t2);
-        sscanf(loc, "=\"%[^\"]", parseBuf);
-        if(parseBuf[0]=='\0')
-          sscanf(loc, "=%s", parseBuf);         
-        if(parseBuf[0]!='\0'){
-          numFound++;
-          auxLoad(parseBuf);
-        }     
-      }
-    }
-
-    /***NEED BETTER CHECK FOR STYLESHEETS
-    if((loc_t=PL_strcasestr(lineBuf, "link"))!= nullptr) { 
-       loc_t2=PL_strcasestr(loc_t, "href");
-      if(loc_t2!=nullptr) {
-        loc_t2+=4;
-        strcpy(loc, loc_t2);
-        //printf("%s\n", loc);
-        sscanf(loc, "=\"%[^\"]", parseBuf);
-        if(parseBuf[0]!='\0'){
-          //printf("%s\n", parseBuf);
-          numFound++;
-          auxLoad(parseBuf);
-        }     
-      }
-    }
-    */
-    if((loc_t=PL_strcasestr(lineBuf, "background"))!=nullptr) {
-      loc_t+=10;
-      strcpy(loc, loc_t);
-      sscanf(loc, "=\"%[^\"]", parseBuf);
-      if(parseBuf[0]!='\0') {
-        numFound++;
-        auxLoad(parseBuf);
-      }
-    }
-    i++;
-
-  }
-  *writeCount = count;
-  return NS_OK;
-}
-
-//-----------------------------------------------------------------------------
-// nsIStreamListener implementation
-//-----------------------------------------------------------------------------
-
-class MyListener : public nsIStreamListener
-{
-    virtual ~MyListener() {}
-
-public:
-    NS_DECL_ISUPPORTS
-    NS_DECL_NSIREQUESTOBSERVER
-    NS_DECL_NSISTREAMLISTENER
-
-    MyListener() { }
-};
-
-NS_IMPL_ISUPPORTS(MyListener,
-                  nsIRequestObserver,
-                  nsIStreamListener)
-
-NS_IMETHODIMP
-MyListener::OnStartRequest(nsIRequest *req, nsISupports *ctxt)
-{
-  //printf(">>> OnStartRequest\n");
-    numStart++;
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-MyListener::OnStopRequest(nsIRequest *req, nsISupports *ctxt, nsresult status)
-{
-    //printf(">>> OnStopRequest status=%x\n", status);
-    if (--gKeepRunning == 0)
-      QuitPumpingEvents();
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-MyListener::OnDataAvailable(nsIRequest *req, nsISupports *ctxt,
-                            nsIInputStream *stream,
-                            uint64_t offset, uint32_t count)
-{
-    //printf(">>> OnDataAvailable [count=%u]\n", count);
-    nsresult rv = NS_ERROR_FAILURE;
-    uint32_t bytesRead=0;
-    char buf[1024];
-
-    if(ctxt == nullptr) {
-      bytesRead=0;
-      rv = stream->ReadSegments(streamParse, nullptr, count, &bytesRead);
-    } else {
-      while (count) {
-        uint32_t amount = std::min<uint32_t>(count, sizeof(buf));
-        rv = stream->Read(buf, amount, &bytesRead);  
-        count -= bytesRead;
-      }
-    }
-
-    if (NS_FAILED(rv)) {
-      printf(">>> stream->Read failed with rv=%x\n",
-             static_cast<uint32_t>(rv));
-      return rv;
-    }
-
-    return NS_OK;
-}
-
-//-----------------------------------------------------------------------------
-// NotificationCallbacks implementation
-//-----------------------------------------------------------------------------
-
-class MyNotifications : public nsIInterfaceRequestor
-                      , public nsIProgressEventSink
-{
-    virtual ~MyNotifications() {}
-
-public:
-    NS_DECL_THREADSAFE_ISUPPORTS
-    NS_DECL_NSIINTERFACEREQUESTOR
-    NS_DECL_NSIPROGRESSEVENTSINK
-
-    MyNotifications() { }
-};
-
-NS_IMPL_ISUPPORTS(MyNotifications,
-                  nsIInterfaceRequestor,
-                  nsIProgressEventSink)
-
-NS_IMETHODIMP
-MyNotifications::GetInterface(const nsIID &iid, void **result)
-{
-    return QueryInterface(iid, result);
-}
-
-NS_IMETHODIMP
-MyNotifications::OnStatus(nsIRequest *req, nsISupports *ctx,
-                          nsresult status, const char16_t *statusText)
-{
-    //printf("status: %x\n", status);
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-MyNotifications::OnProgress(nsIRequest *req, nsISupports *ctx,
-                            uint64_t progress, uint64_t progressMax)
-{
-    // char buf[100];
-    // PR_snprintf(buf, sizeof(buf), "%llu/%llu\n", progress, progressMax);
-    // printf("%s", buf);
-    return NS_OK;
-}
-
-//-----------------------------------------------------------------------------
-// main, etc..
-//-----------------------------------------------------------------------------
-
-//---------getStrLine Helper function---------------
-//Finds a newline in src starting at ind. Puts the
-//line in str (must be big enough). Returns the index
-//of the newline, or -1 if at end of string. If reaches 
-//end of string ('\0'), then will copy contents to 
-//globalStream. 
-int getStrLine(const char *src, char *str, int ind, int max) {
-  char c = src[ind];
-  int i=0;
-  globalStream.Assign('\0');
-  while(c!='\n' && c!='\0' && i<max) {
-    str[i] = src[ind];
-    i++; ind++;
-    c = src[ind];
-  }
-  str[i]='\0';
-  if(i==max || c=='\0') {
-    globalStream.Assign(str);
-    //printf("\nCarryover (%d|%d):\n------------\n%s\n-------\n",i,max,str);
-    return -1;
-  }
-  return ind;
-}
-
-//----------AUX LOAD-----------
-nsresult auxLoad(char *uriBuf)
-{
-    nsresult rv;
-
-    nsCOMPtr<nsISupportsPRBool> myBool = do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID);
-
-    nsCOMPtr<nsIURI> uri;
-    nsCOMPtr<nsIChannel> chan;
-    nsCOMPtr<nsIStreamListener> listener = new MyListener();
-    nsCOMPtr<nsIInterfaceRequestor> callbacks = new MyNotifications();
-
-    printf("Getting: %s", uriBuf);
-
-    //If relative link
-    if(strncmp(uriBuf, "http:", 5)) {
-      //Relative link
-      rv = NS_NewURI(getter_AddRefs(uri), uriBuf, baseURI);
-      if (NS_FAILED(rv)) return(rv);
-    } else {
-      //Absolute link, no base needed
-      rv = NS_NewURI(getter_AddRefs(uri), uriBuf);
-      if (NS_FAILED(rv)) return(rv);
-    }
-
-    //Compare to see if exists
-    bool equal;
-    for(int32_t i = 0; i < uriList.Count(); i++) {
-      uri->Equals(uriList[i], &equal);
-      if(equal) {
-        printf("(duplicate, canceling) %s\n",uriBuf); 
-        return NS_OK;
-      }
-    }
-    printf("\n");
-    uriList.AppendObject(uri);
-
-    nsCOMPtr<nsIScriptSecurityManager> secman =
-      do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
-    RETURN_IF_FAILED(rv, rv, "Couldn't get script security manager!");
-       nsCOMPtr<nsIPrincipal> systemPrincipal;
-    rv = secman->GetSystemPrincipal(getter_AddRefs(systemPrincipal));
-    RETURN_IF_FAILED(rv, rv, "Couldn't get system principal!");
-
-    rv = NS_NewChannel(getter_AddRefs(chan),
-                       uri,
-                       systemPrincipal,
-                       nsILoadInfo::SEC_NORMAL,
-                       nsIContentPolicy::TYPE_OTHER,
-                       nullptr,   // loadGroup
-                       callbacks);
-
-    RETURN_IF_FAILED(rv, rv, "NS_NewChannel");
-
-    gKeepRunning++;
-    rv = chan->AsyncOpen(listener, myBool);
-    RETURN_IF_FAILED(rv, rv, "AsyncOpen");
-
-    return NS_OK;
-
-}
-
-//---------Buffer writer fun---------
-
-} // namespace
-
-using namespace TestPageLoad;
-
-//---------MAIN-----------
-
-int main(int argc, char **argv)
-{ 
-    if (test_common_init(&argc, &argv) != 0)
-        return -1;
-
-    nsresult rv;
-
-    if (argc == 1) {
-        printf("usage: TestPageLoad <url>\n");
-        return -1;
-    }
-    {
-        nsCOMPtr<nsIServiceManager> servMan;
-        NS_InitXPCOM2(getter_AddRefs(servMan), nullptr, nullptr);
-
-        PRTime start, finish;
-
-        printf("Loading necko ... \n");
-        nsCOMPtr<nsIChannel> chan;
-        nsCOMPtr<nsIStreamListener> listener = new MyListener();
-        nsCOMPtr<nsIInterfaceRequestor> callbacks = new MyNotifications();
-
-        rv = NS_NewURI(getter_AddRefs(baseURI), argv[1]);
-        RETURN_IF_FAILED(rv, -1, "NS_NewURI");
-
-        nsCOMPtr<nsIScriptSecurityManager> secman =
-          do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
-        RETURN_IF_FAILED(rv, -1, "Couldn't get script security manager!");
-           nsCOMPtr<nsIPrincipal> systemPrincipal;
-        rv = secman->GetSystemPrincipal(getter_AddRefs(systemPrincipal));
-        RETURN_IF_FAILED(rv, -1, "Couldn't get system principal!");
-
-        rv = NS_NewChannel(getter_AddRefs(chan),
-                           baseURI,
-                           systemPrincipal,
-                           nsILoadInfo::SEC_NORMAL,
-                           nsIContentPolicy::TYPE_OTHER,
-                           nullptr,   // loadGroup
-                           callbacks);
-
-        RETURN_IF_FAILED(rv, -1, "NS_OpenURI");
-        gKeepRunning++;
-
-        //TIMER STARTED-----------------------
-        printf("Starting clock ... \n");
-        start = PR_Now();
-        rv = chan->AsyncOpen(listener, nullptr);
-        RETURN_IF_FAILED(rv, -1, "AsyncOpen");
-
-        PumpEvents();
-
-        finish = PR_Now();
-        uint32_t totalTime32 = uint32_t(finish - start);
-
-        printf("\n\n--------------------\nAll done:\nnum found:%d\nnum start:%d\n", numFound, numStart);
-
-        printf("\n\n>>PageLoadTime>>%u>>\n\n", totalTime32);
-    } // this scopes the nsCOMPtrs
-    // no nsCOMPtrs are allowed to be alive when you call NS_ShutdownXPCOM
-    rv = NS_ShutdownXPCOM(nullptr);
-    NS_ASSERTION(NS_SUCCEEDED(rv), "NS_ShutdownXPCOM failed");
-    return 0;
-}
deleted file mode 100644
--- a/netwerk/test/TestPerf.cpp
+++ /dev/null
@@ -1,273 +0,0 @@
-#include "TestCommon.h"
-#include <stdio.h>
-#include "nsCRT.h" /* should be "plstr.h"? */
-#include "nsNetUtil.h"
-#include "nsIServiceManager.h"
-#include "nsIComponentRegistrar.h"
-#include "nsISupportsArray.h"
-#include "nsContentUtils.h"
-#include <algorithm>
-
-namespace TestPerf {
-
-static nsIIOService *gIOService = nullptr;
-
-//-----------------------------------------------------------------------------
-
-static bool
-load_sync_1(nsISupports *element, void *data)
-{
-    nsCOMPtr<nsIInputStream> stream;
-    nsCOMPtr<nsIURI> uri( do_QueryInterface(element) );
-    nsAutoCString spec;
-    nsresult rv;
-
-    rv = NS_OpenURI(getter_AddRefs(stream),
-                    uri,
-                    nsContentUtils::GetSystemPrincipal(),
-                    nsILoadInfo::SEC_NORMAL,
-                    nsIContentPolicy::TYPE_OTHER,
-                    nullptr, // aLoadGroup
-                    nullptr, // aCallbacks
-                    LOAD_NORMAL,
-                    gIOService);
-
-    if (NS_FAILED(rv)) {
-        uri->GetAsciiSpec(spec);
-        fprintf(stderr, "*** failed opening %s [rv=%x]\n", spec.get(), rv);
-        return true;
-    }
-
-    char buf[4096];
-    uint32_t bytesRead;
-
-    while (1) {
-        rv = stream->Read(buf, sizeof(buf), &bytesRead);
-        if (NS_FAILED(rv) || bytesRead == 0) {
-            if (NS_FAILED(rv)) {
-                uri->GetAsciiSpec(spec);
-                fprintf(stderr, "*** failed reading %s [rv=%x]\n", spec.get(), rv);
-            }
-            break;
-        }
-    }
-
-    return true;
-}
-
-static nsresult
-load_sync(nsISupportsArray *urls)
-{
-    urls->EnumerateForwards(load_sync_1, nullptr);
-    return NS_OK;
-}
-
-//-----------------------------------------------------------------------------
-
-static int gRequestCount = 0;
-
-class MyListener : public nsIStreamListener
-{
-public:
-    NS_DECL_ISUPPORTS
-    NS_DECL_NSIREQUESTOBSERVER
-    NS_DECL_NSISTREAMLISTENER
-
-    MyListener() { }
-    virtual ~MyListener() {}
-};
-
-NS_IMPL_ISUPPORTS(MyListener, nsIStreamListener, nsIRequestObserver)
-
-NS_IMETHODIMP
-MyListener::OnStartRequest(nsIRequest *req, nsISupports *ctx)
-{
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-MyListener::OnDataAvailable(nsIRequest *req, nsISupports *ctx,
-                            nsIInputStream *stream,
-                            uint64_t offset, uint32_t count)
-{
-    nsresult rv;
-    char buf[4096];
-    uint32_t n, bytesRead;
-    while (count) {
-        n = std::min<uint32_t>(count, sizeof(buf));
-        rv = stream->Read(buf, n, &bytesRead);
-        if (NS_FAILED(rv))
-            break;
-        count -= bytesRead;
-        if (bytesRead == 0)
-            break;
-    }
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-MyListener::OnStopRequest(nsIRequest *req, nsISupports *ctx, nsresult status)
-{
-    if (NS_FAILED(status)) {
-        nsAutoCString spec;
-        req->GetName(spec);
-        fprintf(stderr, "*** failed loading %s [reason=%x]\n", spec.get(), status);
-    }
-    if (--gRequestCount == 0) {
-        // post shutdown event
-        QuitPumpingEvents();
-    }
-    return NS_OK;
-}
-
-static bool
-load_async_1(nsISupports *element, void *data)
-{
-    nsCOMPtr<nsIURI> uri( do_QueryInterface(element) );
-    if (!uri)
-        return true;
-
-    MyListener *listener = new MyListener();
-    if (!listener)
-        return true;
-    NS_ADDREF(listener);
-
-    nsresult rv = NS_OpenURI(listener,
-                             nullptr,   // aContext
-                             uri,
-                             nsContentUtils::GetSystemPrincipal(),
-                             nsILoadInfo::SEC_NORMAL,
-                             nsIContentPolicy::TYPE_OTHER,
-                             nullptr,   // aLoadGroup
-                             nullptr,   // aCallbacks
-                             gIOService);
-
-    NS_RELEASE(listener);
-    if (NS_SUCCEEDED(rv))
-        gRequestCount++;
-    else 
-        printf(">> NS_OpenURI failed [rv=%x]\n", rv);
-    return true;
-}
-
-static nsresult
-load_async(nsISupportsArray *urls)
-{
-    urls->EnumerateForwards(load_async_1, nullptr);
-
-    PumpEvents();
-    return NS_OK;
-}
-
-//-----------------------------------------------------------------------------
-
-static nsresult
-read_file(const char *fname, nsISupportsArray *urls)
-{
-    FILE *fp = fopen(fname, "r");
-    if (!fp) {
-        printf("failed opening file: %s\n", fname);
-        return NS_ERROR_FAILURE;
-    }
-
-    nsCOMPtr<nsIURI> uri;
-    nsresult rv;
-    char buf[512];
-    while (fgets(buf, sizeof(buf), fp)) {
-        // remove trailing newline
-        buf[strlen(buf) - 1] = 0;
-        rv = NS_NewURI(getter_AddRefs(uri), buf, nullptr, gIOService); 
-        if (NS_FAILED(rv))
-            printf("*** ignoring malformed uri: %s\n", buf);
-        else {
-            //nsXPIDLCString spec;
-            //uri->GetSpec(getter_Copies(spec));
-            //printf("read url: %s\n", spec.get());
-            urls->AppendElement(uri);
-        }
-    }
-
-    fclose(fp);
-    return NS_OK;
-}
-
-//-----------------------------------------------------------------------------
-
-static void
-print_usage()
-{
-    printf("usage: TestPerf [-sync|-async] <file-of-urls>\n");
-}
-
-} // namespace
-
-using namespace TestPerf;
-
-int
-main(int argc, char **argv)
-{
-    if (test_common_init(&argc, &argv) != 0)
-        return -1;
-
-    nsresult rv;
-    bool sync;
-
-    if (argc < 3) {
-        print_usage();
-        return -1;
-    }
-
-    if (PL_strcasecmp(argv[1], "-sync") == 0)
-        sync = true;
-    else if (PL_strcasecmp(argv[1], "-async") == 0)
-        sync = false;
-    else {
-        print_usage();
-        return -1;
-    }
-
-    nsCOMPtr<nsIServiceManager> servMan;
-    NS_InitXPCOM2(getter_AddRefs(servMan), nullptr, nullptr);
-    nsCOMPtr<nsIComponentRegistrar> registrar = do_QueryInterface(servMan);
-    NS_ASSERTION(registrar, "Null nsIComponentRegistrar");
-    registrar->AutoRegister(nullptr);
-
-    // cache the io service
-    {
-        nsCOMPtr<nsIIOService> ioserv( do_GetIOService() );
-        NS_ADDREF(gIOService = ioserv);
-    }
-
-    nsCOMPtr<nsISupportsArray> urls;
-    rv = NS_NewISupportsArray(getter_AddRefs(urls));
-    if (NS_FAILED(rv)) return -1;
-
-    rv = read_file(argv[2], urls);
-    if (NS_FAILED(rv)) {
-        printf("failed reading file-of-urls\n");
-        return -1;
-    }
-
-    uint32_t urlCount;
-    urls->Count(&urlCount);
-
-    PRIntervalTime start = PR_IntervalNow();
-
-    if (sync)
-        rv = load_sync(urls);
-    else
-        rv = load_async(urls);
-
-    if (NS_FAILED(rv)) {
-        printf("load failed\n");
-        return -1;
-    }
-
-    PRIntervalTime end = PR_IntervalNow();
-    fprintf(stderr, "read: %u urls; total time: %u milliseconds\n",
-            urlCount,
-            PR_IntervalToMilliseconds(end - start));
-
-    NS_RELEASE(gIOService);
-    return 0;
-}
--- a/netwerk/test/TestProtocols.cpp
+++ b/netwerk/test/TestProtocols.cpp
@@ -644,17 +644,17 @@ nsresult StartLoadingURL(const char* aUr
                            nsIContentPolicy::TYPE_OTHER,
                            nullptr,  // loadGroup
                            callbacks,
                            nsIRequest::LOAD_NORMAL,
                            pService);
 
         NS_RELEASE(callbacks);
         if (NS_FAILED(rv)) {
-            LOG(("ERROR: NS_OpenURI failed for %s [rv=%x]\n", aUrlString, rv));
+            LOG(("ERROR: NS_NewChannel failed for %s [rv=%x]\n", aUrlString, rv));
             return rv;
         }
 
         nsCOMPtr<nsITimedChannel> timed(do_QueryInterface(pChannel));
         if (timed)
             timed->SetTimingEnabled(true);
 
         nsCOMPtr<nsIWritablePropertyBag2> props = do_QueryInterface(pChannel);
deleted file mode 100644
--- a/netwerk/test/TestSyncHTTP.cpp
+++ /dev/null
@@ -1,111 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include <nsCOMPtr.h>
-#include <nsString.h>
-#include <nsIURI.h>
-#include <nsIChannel.h>
-#include <nsIHTTPChannel.h>
-#include <nsIInputStream.h>
-#include "nsContentUtils.h"
-#include <nsNetUtil.h>
-
-/*
- * Test synchronous HTTP.
- */
-
-#define RETURN_IF_FAILED(rv, what) \
-    PR_BEGIN_MACRO \
-    if (NS_FAILED(rv)) { \
-        printf(what ": failed - %08x\n", rv); \
-        return -1; \
-    } \
-    PR_END_MACRO
-
-struct TestContext {
-    nsCOMPtr<nsIURI> uri;
-    nsCOMPtr<nsIChannel> channel;
-    nsCOMPtr<nsIInputStream> inputStream;
-    PRTime t1, t2;
-    uint32_t bytesRead, totalRead;
-
-    TestContext()
-        : t1(0), t2(0), bytesRead(0), totalRead(0)
-        { printf("TestContext [this=%p]\n", (void*)this); }
-   ~TestContext()
-        { printf("~TestContext [this=%p]\n", (void*)this); }
-};
-
-int
-main(int argc, char **argv)
-{
-    nsresult rv;
-    TestContext *c;
-    int i, nc=0, npending=0;
-    char buf[256];
-
-    if (argc < 2) {
-        printf("Usage: TestSyncHTTP <url-list>\n");
-        return -1;
-    }
-
-    c = new TestContext[argc-1];
-
-    for (i=0; i<(argc-1); ++i, ++nc) {
-        rv = NS_NewURI(getter_AddRefs(c[i].uri), argv[i+1]);
-        RETURN_IF_FAILED(rv, "NS_NewURI");
-
-        rv = NS_OpenURI(getter_AddRefs(c[i].channel,
-                        c[i].uri,
-                        nsContentUtils::GetSystemPrincipal(),
-                        nsILoadInfo::SEC_NORMAL,
-                        nsIContentPolicy::TYPE_OTHER);
-
-        RETURN_IF_FAILED(rv, "NS_OpenURI");
-
-        nsCOMPtr<nsIHTTPChannel> httpChannel = do_QueryInterface(c[i].channel);
-        if (httpChannel)
-            httpChannel->SetOpenHasEventQueue(false);
-
-        // initialize these fields for reading
-        c[i].bytesRead = 1;
-        c[i].totalRead = 0;
-    }
-
-    for (i=0; i<nc; ++i) {
-        c[i].t1 = PR_Now();
-
-        rv = c[i].channel->Open(getter_AddRefs(c[i].inputStream));
-        RETURN_IF_FAILED(rv, "nsIChannel::OpenInputStream");
-    }
-
-    npending = nc;
-    while (npending) {
-        for (i=0; i<nc; ++i) {
-            //
-            // read the response content...
-            //
-            if (c[i].bytesRead > 0) {
-                rv = c[i].inputStream->Read(buf, sizeof buf, &c[i].bytesRead);
-                RETURN_IF_FAILED(rv, "nsIInputStream::Read");
-                c[i].totalRead += c[i].bytesRead;
-
-                if (c[i].bytesRead == 0) {
-                    c[i].t2 = PR_Now();
-                    printf("finished GET of: %s\n", argv[i+1]);
-                    printf("total read: %u bytes\n", c[i].totalRead);
-                    printf("total read time: %0.3f\n",
-                            ((double) (c[i].t2 - c[i].t1))/1000000.0);
-                    npending--;
-                }
-            }
-        }
-    }
-
-    delete[] c;
-
-    NS_ShutdownXPCOM(nullptr);
-    return 0;
-}
deleted file mode 100644
--- a/netwerk/test/TestThreadedIO.cpp
+++ /dev/null
@@ -1,290 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-#include <stdio.h>
-#include "nsCOMPtr.h"
-#include "nsIEventQueueService.h"
-#include "nsIServiceManager.h"
-#include "nsIStreamListener.h"
-#include "nsIURI.h"
-#include "nsNetUtil.h"
-#include "nsContentUtils.h"
-#include <algorithm>
-//#include "prthread.h"
-
-// This test attempts to load a URL on a separate thread.  It is currently
-// designed simply to expose the problems inherent in such an ambitous task
-// (i.e., it don't work).
-
-// Utility functions...
-
-// Create event queue for current thread.
-static nsCOMPtr<nsIEventQueue>
-createEventQueue() {
-    nsCOMPtr<nsIEventQueue> result;
-    // Get event queue service.
-    nsresult rv = NS_OK;
-    nsCOMPtr<nsIEventQueueService> eqs = 
-             do_GetService(NS_EVENTQUEUESERVICE_CONTRACTID, &rv);
-    if ( NS_SUCCEEDED( rv ) ) {
-            eqs->GetThreadEventQueue(NS_CURRENT_THREAD, getter_AddRefs(result));
-    } else {
-        printf( "%s %d: NS_WITH_SERVICE(nsIEventQueueService) failed, rv=0x%08X\n",
-                (char*)__FILE__, (int)__LINE__, (int)rv );
-    }
-    return result;
-}
-
-// Create channel for requested URL.
-static nsCOMPtr<nsIChannel>
-createChannel( const char *url ) {
-    nsCOMPtr<nsIInputStream> result;
-
-    nsCOMPtr<nsIURI> uri;
-    printf( "Calling NS_NewURI for %s...\n", url );
-    nsresult rv = NS_NewURI( getter_AddRefs( uri ), url );
-
-    if ( NS_SUCCEEDED( rv ) ) {
-        printf( "...NS_NewURI completed OK\n" );
-
-        // Allocate a new input channel on this thread.
-        printf( "Calling NS_OpenURI...\n" );
-
-        nsresult rv = NS_OpenURI(getter_AddRefs(result),
-                                 uri,
-                                 nsContentUtils::GetSystemPrincipal(),
-                                 nsILoadInfo::SEC_NORMAL,
-                                 nsIContentPolicy::TYPE_OTHER);
-
-        if ( NS_SUCCEEDED( rv ) ) {
-            printf( "...NS_OpenURI completed OK\n" );
-        } else {
-            printf( "%s %d: NS_OpenURI failed, rv=0x%08X\n",
-                    (char*)__FILE__, (int)__LINE__, (int)rv );
-        }
-    } else {
-        printf( "%s %d: NS_NewURI failed, rv=0x%08X\n",
-                (char*)__FILE__, (int)__LINE__, (int)rv );
-    }
-    return result;
-}
-
-// Test listener.  It basically dumps incoming data to console.
-class TestListener : public nsIStreamListener {
-public:
-    NS_DECL_ISUPPORTS
-    NS_DECL_NSISTREAMLISTENER
-    NS_DECL_NSISTREAMOBSERVER
-
-    TestListener();
-    ~TestListener();
-    static void IOThread( void *p );
-
-private:
-    bool mDone;
-    int    mThreadNo;
-    FILE  *mFile;
-    static int threadCount;
-}; // class TestListener
-
-int TestListener::threadCount = 0;
-
-TestListener::TestListener()
-    : mDone( false ), mThreadNo( ++threadCount ) {
-    printf( "TestListener ctor called on thread %d\n", mThreadNo );
-}
-
-TestListener::~TestListener() {
-    printf( "TestListener dtor called on thread %d\n", mThreadNo );
-}
-
-NS_IMPL_ISUPPORTS( TestListener, nsIStreamListener, nsIRequestObserver )
-
-NS_IMETHODIMP
-TestListener::OnStartRequest( nsIChannel *aChannel, nsISupports *aContext ) {
-    nsresult rv = NS_OK;
-
-    printf( "TestListener::OnStartRequest called on thread %d\n", mThreadNo );
-
-    // Open output file.
-    char fileName[32];
-    sprintf( fileName, "%s%d", "thread", mThreadNo );
-    mFile = fopen( fileName, "wb" );
-    setbuf( mFile, 0 );
-
-    return rv;
-}
-
-NS_IMETHODIMP
-TestListener::OnStopRequest( nsIChannel *aChannel,
-                             nsISupports *aContext,
-                             nsresult aStatus,
-                             const char16_t *aMsg ) {
-    nsresult rv = NS_OK;
-
-    printf( "TestListener::OnStopRequest called on thread %d\n", mThreadNo );
-
-    fclose( mFile );
-    mDone = true;
-
-    return rv;
-}
-
-NS_IMETHODIMP
-TestListener::OnDataAvailable( nsIChannel *aChannel,
-                               nsISupports *aContext,
-                               nsIInputStream *aStream,
-                               uint64_t offset,
-                               uint32_t aLength ) {
-    nsresult rv = NS_OK;
-
-    printf( "TestListener::OnDataAvailable called on thread %d\n", mThreadNo );
-
-    // Write the data to the console.
-    // Read a buffer full till aLength bytes have been processed.
-    char buffer[ 8192 ];
-    unsigned long bytesRemaining = aLength;
-    while ( bytesRemaining ) {
-        unsigned int bytesRead;
-        // Read a buffer full or the number remaining (whichever is smaller).
-        rv = aStream->Read( buffer,
-                            std::min( sizeof( buffer ), bytesRemaining ),
-                            &bytesRead );
-        if ( NS_SUCCEEDED( rv ) ) {
-            // Write the bytes just read to the output file.
-            fwrite( buffer, 1, bytesRead, mFile );
-            bytesRemaining -= bytesRead;
-        } else {
-            printf( "%s %d: Read error, rv=0x%08X\n",
-                    (char*)__FILE__, (int)__LINE__, (int)rv );
-            break;
-        }
-    }
-    printf( "\n" );
-
-    return rv;
-}
-
-// IOThread: this function creates a new TestListener object (on the new
-// thread), opens a channel, and does AsyncRead to it.
-void
-TestListener::IOThread( void *p ) {
-    printf( "I/O thread (0x%08X) started...\n", (int)(void*)PR_GetCurrentThread() );
-
-    // Argument is pointer to the nsIEventQueue for the main thread.
-    nsIEventQueue *mainThreadQ = static_cast<nsIEventQueue*>(p);
-
-    // Create channel for random web page.
-    nsCOMPtr<nsIChannel> channel = createChannel( (const char*)p );
-
-    if ( channel ) {
-        // Create event queue.
-        nsCOMPtr<nsIEventQueue> ioEventQ = createEventQueue();
-
-        if ( ioEventQ ) {
-            // Create test listener.
-            TestListener *testListener = new TestListener();
-            testListener->AddRef();
-
-            // Read the channel.
-            printf( "Doing AsyncRead...\n" );
-            nsresult rv = channel->AsyncRead( testListener, 0 );
-
-            if ( NS_SUCCEEDED( rv ) ) {
-                printf( "...AsyncRead completed OK\n" );
-
-                // Process events till testListener says stop.
-                printf( "Start event loop on io thread %d...\n", testListener->mThreadNo );
-                while ( !testListener->mDone ) {
-                    PLEvent *event;
-                    ioEventQ->GetEvent( &event );
-                    ioEventQ->HandleEvent( event );
-                }
-                printf( "...io thread %d event loop exiting\n", testListener->mThreadNo );
-            } else {
-                printf( "%s %d: AsyncRead failed on thread %d, rv=0x%08X\n",
-                        (char*)__FILE__, (int)__LINE__, testListener->mThreadNo, (int)rv );
-            }
-
-            // Release the test listener.
-            testListener->Release();
-        }
-    }
-
-    printf( "...I/O thread terminating\n" );
-}
-
-static const int maxThreads = 5;
-
-int
-main( int argc, char* argv[] ) {
-    setbuf( stdout, 0 );
-    if ( argc < 2 || argc > maxThreads + 1 ) {
-        printf( "usage: testThreadedIO url1 <url2>...\n"
-                "where <url#> is a location to be loaded on a separate thread\n"
-                "limit is %d urls/threads", maxThreads );
-        return -1;
-    }
-
-    nsresult rv= (nsresult)-1;
-
-    printf( "Test starting...\n" );
-
-    // Initialize XPCOM.
-    printf( "Initializing XPCOM...\n" );
-    rv = NS_InitXPCOM2(nullptr, nullptr, nullptr);
-    if ( NS_FAILED( rv ) ) {
-        printf( "%s %d: NS_InitXPCOM failed, rv=0x%08X\n",
-                (char*)__FILE__, (int)__LINE__, (int)rv );
-        return rv;
-    }
-    printf( "...XPCOM initialized OK\n" );
-    // Create the Event Queue for this thread...
-    printf( "Creating event queue for main thread (0x%08X)...\n",
-            (int)(void*)PR_GetCurrentThread() );
-    {
-        nsCOMPtr<nsIEventQueue> mainThreadQ = createEventQueue();
-
-        if ( mainThreadQ ) {
-            printf( "...main thread's event queue created OK\n" );
-
-            // Spawn threads to do I/O.
-            int goodThreads = 0;
-            PRThread *thread[ maxThreads ];
-            for ( int threadNo = 1; threadNo < argc; threadNo++ ) {
-                printf( "Creating I/O thread %d to load %s...\n", threadNo, argv[threadNo] );
-                PRThread *ioThread = PR_CreateThread( PR_USER_THREAD,
-                                                      TestListener::IOThread,
-                                                      argv[threadNo],
-                                                      PR_PRIORITY_NORMAL,
-                                                      PR_GLOBAL_THREAD,
-                                                      PR_JOINABLE_THREAD,
-                                                      0 );
-                if ( ioThread ) {
-                    thread[ goodThreads++ ] = ioThread;
-                    printf( "...I/O thread %d (0x%08X) created OK\n",
-                            threadNo, (int)(void*)ioThread );
-                } else {
-                    printf( "%s %d: PR_CreateThread for thread %d failed\n",
-                            (char*)__FILE__, (int)__LINE__, threadNo );
-                }
-            }
-
-            // Wait for all the threads to terminate.
-            for ( int joinThread = 0; joinThread < goodThreads; joinThread++ ) {
-                printf( "Waiting for thread %d to terminate...\n", joinThread+1 );
-                PR_JoinThread( thread[ joinThread ] );
-            }
-        }
-    } // this scopes the nsCOMPtrs
-    // no nsCOMPtrs are allowed to be alive when you call NS_ShutdownXPCOM
-    // Shut down XPCOM.
-    printf( "Shutting down XPCOM...\n" );
-    NS_ShutdownXPCOM( 0 );
-    printf( "...XPCOM shutdown complete\n" );
-
-    // Exit.
-    printf( "...test complete, rv=0x%08X\n", (int)rv );
-    return rv;
-}
--- a/netwerk/test/httpserver/test/test_basic_functionality.js
+++ b/netwerk/test/httpserver/test/test_basic_functionality.js
@@ -54,16 +54,17 @@ const HEADER_COUNT = 1000;
 
 // common properties *always* appended by server
 // or invariants for every URL in paths
 function commonCheck(ch)
 {
   do_check_true(ch.contentLength > -1);
   do_check_eq(ch.getResponseHeader("connection"), "close");
   do_check_false(ch.isNoStoreResponse());
+  do_check_false(ch.isPrivateResponse());
 }
 
 function start_objHandler(ch, cx)
 {
   commonCheck(ch);
 
   do_check_eq(ch.responseStatus, 200);
   do_check_true(ch.requestSucceeded);
--- a/netwerk/test/moz.build
+++ b/netwerk/test/moz.build
@@ -19,31 +19,29 @@ XPCSHELL_TESTS_MANIFESTS += [
 GeckoSimplePrograms([
     'PropertiesTest',
     'ReadNTLM',
     'TestBlockingSocket',
     'TestCallbacks',
     'TestDNS',
     'TestIncrementalDownload',
     'TestOpen',
-    'TestPageLoad',
     'TestProtocols',
     'TestServ',
     'TestStandardURL',
     'TestStreamLoader',
     'TestUpload',
     'TestURLParser',
     'urltest',
 ])
 
 # XXX Make this work in libxul builds.
 #SIMPLE_PROGRAMS += [
 #    TestIDN',
 #    TestIOThreads',
-#    TestPerf',
 #    TestSocketTransport',
 #    TestStreamChannel',
 #    TestStreamPump',
 #    TestStreamTransport',
 #    TestUDPSocketProvider',
 #]
 
 CppUnitTests([
--- a/testing/marionette/client/marionette/selection.py
+++ b/testing/marionette/client/marionette/selection.py
@@ -100,17 +100,17 @@ class SelectionManager(object):
         considered.
 
         '''
         range_count = self.range_count();
         first_rect_list = self.selection_rect_list(0)
         last_rect_list = self.selection_rect_list(range_count - 1)
         last_list_length = last_rect_list['length']
         first_rect, last_rect = first_rect_list['0'], last_rect_list[str(last_list_length - 1)]
-        origin_x, origin_y = self.element.location['x'], self.element.location['y']
+        origin_x, origin_y = self.element.rect['x'], self.element.rect['y']
 
         if self.element.get_attribute('dir') == 'rtl':  # such as Arabic
             start_pos, end_pos = 'right', 'left'
         else:
             start_pos, end_pos = 'left', 'right'
 
         # Calculate y offset according to different needs.
         if location_type == 'center':
--- a/testing/marionette/client/marionette/tests/unit/unit-tests.ini
+++ b/testing/marionette/client/marionette/tests/unit/unit-tests.ini
@@ -12,16 +12,17 @@ b2g = true
 skip = false
 
 [test_marionette.py]
 [test_data_driven.py]
 [test_session.py]
 [test_capabilities.py]
 
 [test_accessibility.py]
+b2g = false
 
 [test_expectedfail.py]
 expected = fail
 [test_import_script.py]
 b2g = false
 [test_import_script_reuse_window.py]
 b2g = false
 [test_click.py]
--- a/xpcom/base/nsCycleCollector.cpp
+++ b/xpcom/base/nsCycleCollector.cpp
@@ -150,16 +150,17 @@
 #endif
 #endif
 
 #include "base/process_util.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/CycleCollectedJSRuntime.h"
+#include "mozilla/DebugOnly.h"
 #include "mozilla/HoldDropJSObjects.h"
 /* This must occur *after* base/process_util.h to avoid typedefs conflicts. */
 #include "mozilla/LinkedList.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/SegmentedVector.h"
 
 #include "nsCycleCollectionParticipant.h"
 #include "nsCycleCollectionNoteRootCallback.h"
@@ -1252,31 +1253,31 @@ class nsCycleCollector : public nsIMemor
 
   CycleCollectedJSRuntime* mJSRuntime;
 
   ccPhase mIncrementalPhase;
   CCGraph mGraph;
   nsAutoPtr<CCGraphBuilder> mBuilder;
   nsCOMPtr<nsICycleCollectorListener> mListener;
 
-  nsIThread* mThread;
+  DebugOnly<void*> mThread;
 
   nsCycleCollectorParams mParams;
 
   uint32_t mWhiteNodeCount;
 
   CC_BeforeUnlinkCallback mBeforeUnlinkCB;
   CC_ForgetSkippableCallback mForgetSkippableCB;
 
   nsPurpleBuffer mPurpleBuf;
 
   uint32_t mUnmergedNeeded;
   uint32_t mMergedInARow;
 
-  JSPurpleBuffer* mJSPurpleBuffer;
+  nsRefPtr<JSPurpleBuffer> mJSPurpleBuffer;
 
 private:
   virtual ~nsCycleCollector();
 
 public:
   nsCycleCollector();
 
   void RegisterJSRuntime(CycleCollectedJSRuntime* aJSRuntime);
@@ -2036,17 +2037,17 @@ private:
   CCGraph& mGraph;
   CycleCollectorResults& mResults;
   NodePool::Builder mNodeBuilder;
   EdgePool::Builder mEdgeBuilder;
   PtrInfo* mCurrPi;
   nsCycleCollectionParticipant* mJSParticipant;
   nsCycleCollectionParticipant* mJSZoneParticipant;
   nsCString mNextEdgeName;
-  nsICycleCollectorListener* mListener;
+  nsCOMPtr<nsICycleCollectorListener> mListener;
   bool mMergeZones;
   bool mRanOutOfMemory;
   nsAutoPtr<NodePool::Enumerator> mCurrNode;
 
 public:
   CCGraphBuilder(CCGraph& aGraph,
                  CycleCollectorResults& aResults,
                  CycleCollectedJSRuntime* aJSRuntime,
@@ -2573,39 +2574,37 @@ class JSPurpleBuffer
 {
   ~JSPurpleBuffer()
   {
     MOZ_ASSERT(mValues.IsEmpty());
     MOZ_ASSERT(mObjects.IsEmpty());
   }
 
 public:
-  explicit JSPurpleBuffer(JSPurpleBuffer*& aReferenceToThis)
+  explicit JSPurpleBuffer(nsRefPtr<JSPurpleBuffer>& aReferenceToThis)
     : mReferenceToThis(aReferenceToThis)
     , mValues(kSegmentSize)
     , mObjects(kSegmentSize)
   {
     mReferenceToThis = this;
-    NS_ADDREF_THIS();
     mozilla::HoldJSObjects(this);
   }
 
   void Destroy()
   {
     mReferenceToThis = nullptr;
     mValues.Clear();
     mObjects.Clear();
     mozilla::DropJSObjects(this);
-    NS_RELEASE_THIS();
   }
 
   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(JSPurpleBuffer)
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(JSPurpleBuffer)
 
-  JSPurpleBuffer*& mReferenceToThis;
+  nsRefPtr<JSPurpleBuffer>& mReferenceToThis;
 
   // These are raw pointers instead of Heap<T> because we only need Heap<T> for
   // pointers which may point into the nursery. The purple buffer never contains
   // pointers to the nursery because nursery gcthings can never be gray and only
   // gray things can be inserted into the purple buffer.
   static const size_t kSegmentSize = 512;
   SegmentedVector<JS::Value, kSegmentSize, InfallibleAllocPolicy> mValues;
   SegmentedVector<JSObject*, kSegmentSize, InfallibleAllocPolicy> mObjects;
@@ -2737,17 +2736,17 @@ public:
   }
 
   virtual void Trace(JS::Heap<JSFunction*>* aFunction, const char* aName,
                      void* aClosure) const
   {
   }
 
 private:
-  nsCycleCollector* mCollector;
+  nsRefPtr<nsCycleCollector> mCollector;
   ObjectsVector mObjects;
 };
 
 class RemoveSkippableVisitor : public SnowWhiteKiller
 {
 public:
   RemoveSkippableVisitor(nsCycleCollector* aCollector,
                          uint32_t aMaxCount, bool aRemoveChildlessNodes,
@@ -3001,17 +3000,17 @@ public:
     if (pi->mColor == black) {
       return;
     }
     FloodBlackNode(mCount, mFailed, pi);
   }
 
 private:
   CCGraph& mGraph;
-  nsICycleCollectorListener* mListener;
+  nsCOMPtr<nsICycleCollectorListener> mListener;
   uint32_t& mCount;
   bool& mFailed;
 };
 
 // Objects that have been stored somewhere since the start of incremental graph building must
 // be treated as live for this cycle collection, because we may not have accurate information
 // about who holds references to them.
 void
@@ -3395,18 +3394,17 @@ nsCycleCollector::nsCycleCollector() :
   mScanInProgress(false),
   mJSRuntime(nullptr),
   mIncrementalPhase(IdlePhase),
   mThread(NS_GetCurrentThread()),
   mWhiteNodeCount(0),
   mBeforeUnlinkCB(nullptr),
   mForgetSkippableCB(nullptr),
   mUnmergedNeeded(0),
-  mMergedInARow(0),
-  mJSPurpleBuffer(nullptr)
+  mMergedInARow(0)
 {
 }
 
 nsCycleCollector::~nsCycleCollector()
 {
   UnregisterWeakMemoryReporter(this);
 }
 
deleted file mode 100644
--- a/xpcom/tests/CvtURL.cpp
+++ /dev/null
@@ -1,95 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-#include <stdio.h>
-#include "nscore.h"
-#include "nsIConverterInputStream.h"
-#include "nsIURL.h"
-#include "nsNetUtil.h"
-#include "nsCRT.h"
-#include "nsString.h"
-#include "prprf.h"
-#include "prtime.h"
-
-static nsString* ConvertCharacterSetName(const char* aName)
-{
-  return new nsString(NS_ConvertASCIItoUTF16(aName));
-}
-
-int main(int argc, char** argv)
-{
-  if (3 != argc) {
-    printf("usage: CvtURL url utf8\n");
-    return -1;
-  }
-
-  char* characterSetName = argv[2];
-  nsString* cset = ConvertCharacterSetName(characterSetName);
-  if (NS_PTR_TO_INT32(cset) < 0) {
-    printf("illegal character set name: '%s'\n", characterSetName);
-    return -1;
-  }
-
-  // Create url object
-  char* urlName = argv[1];
-  nsIURI* url;
-  nsresult rv;
-  rv = NS_NewURI(&url, urlName);
-  if (NS_OK != rv) {
-    printf("invalid URL: '%s'\n", urlName);
-    return -1;
-  }
-
-  // Get an input stream from the url
-  nsIInputStream* in;
-  nsresult ec = NS_OpenURI(&in,
-                           url,
-                           nsContentUtils::GetSystemPrincipal(),
-                           nsILoadInfo::SEC_NORMAL,
-                           nsIContentPolicy::TYPE_OTHER);
-
-  if (nullptr == in) {
-    printf("open of url('%s') failed: error=%x\n", urlName, ec);
-    return -1;
-  }
-
-  // Translate the input using the argument character set id into
-  // unicode
-  nsCOMPtr<nsIConverterInputStream> uin =
-    do_CreateInstance("@mozilla.org/intl/converter-input-stream;1", &rv);
-  if (NS_SUCCEEDED(rv))
-    rv = uin->Init(in, cset->get(), 4096);
-  if (NS_FAILED(rv)) {
-    printf("can't create converter input stream: %d\n", rv);
-    return -1;
-  }
-
-  // Read the input and write some output
-  PRTime start = PR_Now();
-  int32_t count = 0;
-  for (;;) {
-    char16_t buf[1000];
-    uint32_t nb;
-    ec = uin->Read(buf, 0, 1000, &nb);
-    if (NS_FAILED(ec)) {
-      printf("i/o error: %d\n", ec);
-      break;
-    }
-    if (nb == 0) break; // EOF
-    count += nb;
-  }
-  PRTime end = PR_Now();
-  PRTime conversion = (end - start) / 1000;
-  char buf[500];
-  PR_snprintf(buf, sizeof(buf),
-              "converting and discarding %d bytes took %lldms",
-              count, conversion);
-  puts(buf);
-
-  // Release the objects
-  in->Release();
-  url->Release();
-
-  return 0;
-}