merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 21 Sep 2015 13:43:51 +0200
changeset 296102 7cd926385096475bda3eb601e0ff109f45c0498a
parent 296079 98231abe637676311a0df1e0a5da85c94adce36d (current diff)
parent 296101 a6904700794378c465f9136cfb1eef059af4979e (diff)
child 296103 5da1fc351aba2e08f33983e9a011306dde4a81af
push id5245
push userraliiev@mozilla.com
push dateThu, 29 Oct 2015 11:30:51 +0000
treeherdermozilla-beta@dac831dc1bd0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone43.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
--- a/dom/canvas/test/crossorigin/test_canvas2d_crossorigin.html
+++ b/dom/canvas/test/crossorigin/test_canvas2d_crossorigin.html
@@ -200,17 +200,13 @@ function beginTest() {
           }
         }
         testImage(url, attrValData[0], expected_error);
       }
     }
   }
 }
 
-var prefs = [
-  [ "canvas.capturestream.enabled", true ],
-];
-SpecialPowers.pushPrefEnv({ "set" : prefs }, beginTest);
-
+beginTest();
 </script>
 </pre>
 </body>
 </html>
--- a/dom/canvas/test/crossorigin/test_video_crossorigin.html
+++ b/dom/canvas/test/crossorigin/test_video_crossorigin.html
@@ -206,19 +206,14 @@ function beginTest() {
   try {
     document.gl = createCanvas(16, 16).getContext("experimental-webgl");
   } catch (ex) {
     // Mac OS X 10.5 doesn't support WebGL, so we won't run the WebGL tests
   }
   document.manager.runTests(corsTests, startTest);
 }
 
-var prefs = [
-  [ "canvas.capturestream.enabled", true ],
-];
-
 SimpleTest.waitForExplicitFinish();
-SpecialPowers.pushPrefEnv({ "set" : prefs }, beginTest);
-
+beginTest();
 </script>
 </pre>
 </body>
 </html>
--- a/dom/canvas/test/reftest/reftest.list
+++ b/dom/canvas/test/reftest/reftest.list
@@ -15,17 +15,17 @@ pref(webgl.force-layers-readback,true)  
 == webgl-clear-test.html?depth wrapper.html?green.png
 == webgl-clear-test.html?stencil wrapper.html?green.png
 == webgl-clear-test.html?depth&stencil wrapper.html?green.png
 
 # Check that resize works:
 == webgl-resize-test.html  wrapper.html?green.png
 
 # Check that captureStream() displays in a local video element
-pref(canvas.capturestream.enabled,true) skip-if(winWidget&&layersGPUAccelerated&&d2d) == webgl-capturestream-test.html?preserve wrapper.html?green.png
+skip-if(winWidget&&layersGPUAccelerated&&d2d) == webgl-capturestream-test.html?preserve wrapper.html?green.png
 
 # Some of the failure conditions are a little crazy. I'm (jgilbert) setting these based on
 # failures encountered when running on Try, and then targetting the Try config by
 # differences in the `sandbox` contents. That is, I'm labeling based on symptoms rather
 # than cause.
 # Lin-R-e10s: gtkWidget && browserIsRemote
 # WinXP-R: winWidget && layersGPUAccelerated && !d2d
 # Win7+-R: winWidget && layersGPUAccelerated && d2d
@@ -152,11 +152,11 @@ skip-if(!winWidget) pref(webgl.disable-a
 # Bug 815648
 == stroketext-shadow.html stroketext-shadow-ref.html
 
 # focus rings
 pref(canvas.focusring.enabled,true) skip-if(B2G) skip-if(Android&&AndroidVersion<15,8,500) skip-if(winWidget) needs-focus == drawFocusIfNeeded.html drawFocusIfNeeded-ref.html
 pref(canvas.customfocusring.enabled,true) skip-if(B2G) skip-if(Android&&AndroidVersion<15,8,500) skip-if(winWidget) needs-focus == drawCustomFocusRing.html drawCustomFocusRing-ref.html
 
 # Check that captureStream() displays in a local video element
-pref(canvas.capturestream.enabled,true) skip-if(winWidget&&layersGPUAccelerated&&d2d) == capturestream.html wrapper.html?green.png
+skip-if(winWidget&&layersGPUAccelerated&&d2d) == capturestream.html wrapper.html?green.png
 
 fuzzy-if(Android,3,40) fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu),1,1) == 1177726-text-stroke-bounds.html 1177726-text-stroke-bounds-ref.html
--- a/dom/canvas/test/test_capture.html
+++ b/dom/canvas/test/test_capture.html
@@ -102,14 +102,11 @@ function beginTest() {
     .then(checkRequestFrameOrderGuarantee)
     .then(checkDrawColorGreen) // Restore video elements to green.
     .then(checkDrawImageNotCleanRed)
     .then(finish);
 }
 
 SimpleTest.waitForExplicitFinish();
 
-var prefs = [
-  [ "canvas.capturestream.enabled", true ],
-];
-SpecialPowers.pushPrefEnv({ "set" : prefs }, beginTest);
+beginTest();
 </script>
 
--- a/dom/canvas/test/webgl-mochitest/test_capture.html
+++ b/dom/canvas/test/webgl-mochitest/test_capture.html
@@ -166,14 +166,11 @@ function beginTest() {
     .then(checkDrawColorGreen)
     .then(checkClearColorRed)
     .then(checkRequestFrameOrderGuarantee)
     .then(finish);
 }
 
 SimpleTest.waitForExplicitFinish();
 
-var prefs = [
-  [ "canvas.capturestream.enabled", true ],
-];
-SpecialPowers.pushPrefEnv({ "set" : prefs }, beginTest);
+beginTest();
 </script>
 
--- a/dom/media/DOMMediaStream.cpp
+++ b/dom/media/DOMMediaStream.cpp
@@ -353,16 +353,24 @@ DOMMediaStream::SetTrackEnabled(TrackID 
 void
 DOMMediaStream::StopTrack(TrackID aTrackID)
 {
   if (mStream && mStream->AsSourceStream()) {
     mStream->AsSourceStream()->EndTrack(aTrackID);
   }
 }
 
+already_AddRefed<Promise>
+DOMMediaStream::ApplyConstraintsToTrack(TrackID aTrackID,
+                                        const MediaTrackConstraints& aConstraints,
+                                        ErrorResult &aRv)
+{
+  return nullptr;
+}
+
 bool
 DOMMediaStream::CombineWithPrincipal(nsIPrincipal* aPrincipal)
 {
   bool changed =
     nsContentUtils::CombineResourcePrincipals(&mPrincipal, aPrincipal);
   if (changed) {
     NotifyPrincipalChanged();
   }
--- a/dom/media/DOMMediaStream.h
+++ b/dom/media/DOMMediaStream.h
@@ -43,16 +43,17 @@ class HTMLCanvasElement;
 class MediaStreamTrack;
 class AudioStreamTrack;
 class VideoStreamTrack;
 class AudioTrack;
 class VideoTrack;
 class AudioTrackList;
 class VideoTrackList;
 class MediaTrackListListener;
+struct MediaTrackConstraints;
 } // namespace dom
 
 namespace layers {
 class ImageContainer;
 class OverlayImage;
 } // namespace layers
 
 class MediaStreamDirectListener;
@@ -72,16 +73,17 @@ class DOMMediaStream : public DOMEventTa
   typedef dom::VideoStreamTrack VideoStreamTrack;
   typedef dom::AudioTrack AudioTrack;
   typedef dom::VideoTrack VideoTrack;
   typedef dom::AudioTrackList AudioTrackList;
   typedef dom::VideoTrackList VideoTrackList;
   typedef dom::MediaTrackListListener MediaTrackListListener;
 
 public:
+  typedef dom::MediaTrackConstraints MediaTrackConstraints;
   typedef uint8_t TrackTypeHints;
 
   DOMMediaStream();
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_REALLY_FORWARD_NSIDOMEVENTTARGET(DOMEventTargetHelper)
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(DOMMediaStream,
                                            DOMEventTargetHelper)
@@ -116,16 +118,21 @@ public:
   /**
    * Overridden in DOMLocalMediaStreams to allow getUserMedia to disable
    * media at the SourceMediaStream.
    */
   virtual void SetTrackEnabled(TrackID aTrackID, bool aEnabled);
 
   virtual void StopTrack(TrackID aTrackID);
 
+  virtual already_AddRefed<dom::Promise>
+  ApplyConstraintsToTrack(TrackID aTrackID,
+                          const MediaTrackConstraints& aConstraints,
+                          ErrorResult &aRv);
+
   virtual DOMLocalMediaStream* AsDOMLocalMediaStream() { return nullptr; }
   virtual DOMHwMediaStream* AsDOMHwMediaStream() { return nullptr; }
 
   bool IsFinished();
   /**
    * Returns a principal indicating who may access this stream. The stream contents
    * can only be accessed by principals subsuming this principal.
    */
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -74,18 +74,16 @@
 
 #if defined(XP_MACOSX)
 #include "nsCocoaFeatures.h"
 #endif
 #if defined (XP_WIN)
 #include "mozilla/WindowsVersion.h"
 #endif
 
-#include <map>
-
 // GetCurrentTime is defined in winbase.h as zero argument macro forwarding to
 // GetTickCount() and conflicts with MediaStream::GetCurrentTime.
 #ifdef GetCurrentTime
 #undef GetCurrentTime
 #endif
 
 // XXX Workaround for bug 986974 to maintain the existing broken semantics
 template<>
@@ -114,16 +112,17 @@ GetMediaManagerLog()
   return sLog;
 }
 #define LOG(msg) MOZ_LOG(GetMediaManagerLog(), mozilla::LogLevel::Debug, msg)
 
 using dom::File;
 using dom::MediaStreamConstraints;
 using dom::MediaTrackConstraintSet;
 using dom::MediaTrackConstraints;
+using dom::MediaStreamTrack;
 using dom::MediaStreamError;
 using dom::GetUserMediaRequest;
 using dom::Sequence;
 using dom::OwningBooleanOrMediaTrackConstraints;
 using media::Pledge;
 using media::NewRunnableFrom;
 using media::NewTaskFrom;
 
@@ -209,16 +208,164 @@ HostHasPermission(nsIURI &docURI)
     }
 
     begin = end + 1;
   } while (end < domainWhiteList.Length());
 
   return false;
 }
 
+// Generic class for running long media operations like Start off the main
+// thread, and then (because nsDOMMediaStreams aren't threadsafe),
+// ProxyReleases mStream since it's cycle collected.
+class MediaOperationTask : public Task
+{
+public:
+  // so we can send Stop without AddRef()ing from the MSG thread
+  MediaOperationTask(MediaOperation aType,
+    GetUserMediaCallbackMediaStreamListener* aListener,
+    DOMMediaStream* aStream,
+    DOMMediaStream::OnTracksAvailableCallback* aOnTracksAvailableCallback,
+    AudioDevice* aAudioDevice,
+    VideoDevice* aVideoDevice,
+    bool aBool,
+    uint64_t aWindowID,
+    already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
+    const dom::MediaTrackConstraints& aConstraints = dom::MediaTrackConstraints())
+    : mType(aType)
+    , mStream(aStream)
+    , mOnTracksAvailableCallback(aOnTracksAvailableCallback)
+    , mAudioDevice(aAudioDevice)
+    , mVideoDevice(aVideoDevice)
+    , mListener(aListener)
+    , mBool(aBool)
+    , mWindowID(aWindowID)
+    , mOnFailure(aError)
+    , mConstraints(aConstraints)
+  {}
+
+  ~MediaOperationTask()
+  {
+    // MediaStreams can be released on any thread.
+  }
+
+  void
+  ReturnCallbackError(nsresult rv, const char* errorLog);
+
+  void
+  Run()
+  {
+    SourceMediaStream *source = mListener->GetSourceStream();
+    // No locking between these is required as all the callbacks for the
+    // same MediaStream will occur on the same thread.
+    if (!source) // means the stream was never Activated()
+      return;
+
+    switch (mType) {
+      case MEDIA_START:
+        {
+          NS_ASSERTION(!NS_IsMainThread(), "Never call on main thread");
+          nsresult rv;
+
+          if (mAudioDevice) {
+            rv = mAudioDevice->GetSource()->Start(source, kAudioTrack);
+            if (NS_FAILED(rv)) {
+              ReturnCallbackError(rv, "Starting audio failed");
+              return;
+            }
+          }
+          if (mVideoDevice) {
+            rv = mVideoDevice->GetSource()->Start(source, kVideoTrack);
+            if (NS_FAILED(rv)) {
+              ReturnCallbackError(rv, "Starting video failed");
+              return;
+            }
+          }
+          // Start() queued the tracks to be added synchronously to avoid races
+          source->FinishAddTracks();
+
+          source->SetPullEnabled(true);
+          source->AdvanceKnownTracksTime(STREAM_TIME_MAX);
+
+          MM_LOG(("started all sources"));
+          // Forward mOnTracksAvailableCallback to GetUserMediaNotificationEvent,
+          // because mOnTracksAvailableCallback needs to be added to mStream
+          // on the main thread.
+          nsIRunnable *event =
+            new GetUserMediaNotificationEvent(GetUserMediaNotificationEvent::STARTING,
+                                              mStream.forget(),
+                                              mOnTracksAvailableCallback.forget(),
+                                              mAudioDevice != nullptr,
+                                              mVideoDevice != nullptr,
+                                              mWindowID, mOnFailure.forget());
+          // event must always be released on mainthread due to the JS callbacks
+          // in the TracksAvailableCallback
+          NS_DispatchToMainThread(event);
+        }
+        break;
+
+      case MEDIA_STOP:
+      case MEDIA_STOP_TRACK:
+        {
+          NS_ASSERTION(!NS_IsMainThread(), "Never call on main thread");
+          if (mAudioDevice) {
+            mAudioDevice->GetSource()->Stop(source, kAudioTrack);
+            mAudioDevice->GetSource()->Deallocate();
+          }
+          if (mVideoDevice) {
+            mVideoDevice->GetSource()->Stop(source, kVideoTrack);
+            mVideoDevice->GetSource()->Deallocate();
+          }
+          // Do this after stopping all tracks with EndTrack()
+          if (mBool) {
+            source->Finish();
+          }
+
+          nsIRunnable *event =
+            new GetUserMediaNotificationEvent(mListener,
+                                              mType == MEDIA_STOP ?
+                                              GetUserMediaNotificationEvent::STOPPING :
+                                              GetUserMediaNotificationEvent::STOPPED_TRACK,
+                                              mAudioDevice != nullptr,
+                                              mVideoDevice != nullptr,
+                                              mWindowID);
+          // event must always be released on mainthread due to the JS callbacks
+          // in the TracksAvailableCallback
+          NS_DispatchToMainThread(event);
+        }
+        break;
+
+      case MEDIA_DIRECT_LISTENERS:
+        {
+          NS_ASSERTION(!NS_IsMainThread(), "Never call on main thread");
+          if (mVideoDevice) {
+            mVideoDevice->GetSource()->SetDirectListeners(mBool);
+          }
+        }
+        break;
+
+      default:
+        MOZ_ASSERT(false,"invalid MediaManager operation");
+        break;
+    }
+  }
+
+private:
+  MediaOperation mType;
+  nsRefPtr<DOMMediaStream> mStream;
+  nsAutoPtr<DOMMediaStream::OnTracksAvailableCallback> mOnTracksAvailableCallback;
+  nsRefPtr<AudioDevice> mAudioDevice; // threadsafe
+  nsRefPtr<VideoDevice> mVideoDevice; // threadsafe
+  nsRefPtr<GetUserMediaCallbackMediaStreamListener> mListener; // threadsafe
+  bool mBool;
+  uint64_t mWindowID;
+  nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mOnFailure;
+  dom::MediaTrackConstraints mConstraints;
+};
+
 /**
  * Send an error back to content.
  * Do this only on the main thread. The onSuccess callback is also passed here
  * so it can be released correctly.
  */
 template<class SuccessCallbackType>
 class ErrorCallbackRunnable : public nsRunnable
 {
@@ -465,43 +612,53 @@ nsresult VideoDevice::Allocate(const dom
   return GetSource()->Allocate(aConstraints, aPrefs, mID);
 }
 
 nsresult AudioDevice::Allocate(const dom::MediaTrackConstraints &aConstraints,
                                const MediaEnginePrefs &aPrefs) {
   return GetSource()->Allocate(aConstraints, aPrefs, mID);
 }
 
+nsresult VideoDevice::Restart(const dom::MediaTrackConstraints &aConstraints,
+                              const MediaEnginePrefs &aPrefs) {
+  return GetSource()->Restart(aConstraints, aPrefs, mID);
+}
+
+nsresult AudioDevice::Restart(const dom::MediaTrackConstraints &aConstraints,
+                              const MediaEnginePrefs &aPrefs) {
+  return GetSource()->Restart(aConstraints, aPrefs, mID);
+}
+
 /**
  * A subclass that we only use to stash internal pointers to MediaStreamGraph objects
  * that need to be cleaned up.
  */
 class nsDOMUserMediaStream : public DOMLocalMediaStream
 {
 public:
   static already_AddRefed<nsDOMUserMediaStream>
   CreateTrackUnionStream(nsIDOMWindow* aWindow,
                          GetUserMediaCallbackMediaStreamListener* aListener,
-                         MediaEngineSource* aAudioSource,
-                         MediaEngineSource* aVideoSource,
+                         AudioDevice* aAudioDevice,
+                         VideoDevice* aVideoDevice,
                          MediaStreamGraph* aMSG)
   {
     nsRefPtr<nsDOMUserMediaStream> stream = new nsDOMUserMediaStream(aListener,
-                                                                     aAudioSource,
-                                                                     aVideoSource);
+                                                                     aAudioDevice,
+                                                                     aVideoDevice);
     stream->InitTrackUnionStream(aWindow, aMSG);
     return stream.forget();
   }
 
   nsDOMUserMediaStream(GetUserMediaCallbackMediaStreamListener* aListener,
-                       MediaEngineSource *aAudioSource,
-                       MediaEngineSource *aVideoSource) :
+                       AudioDevice *aAudioDevice,
+                       VideoDevice *aVideoDevice) :
     mListener(aListener),
-    mAudioSource(aAudioSource),
-    mVideoSource(aVideoSource),
+    mAudioDevice(aAudioDevice),
+    mVideoDevice(aVideoDevice),
     mEchoOn(true),
     mAgcOn(false),
     mNoiseOn(true),
 #ifdef MOZ_WEBRTC
     mEcho(webrtc::kEcDefault),
     mAgc(webrtc::kAgcDefault),
     mNoise(webrtc::kNsDefault),
 #else
@@ -538,23 +695,70 @@ public:
   virtual void StopTrack(TrackID aTrackID) override
   {
     if (mSourceStream) {
       mSourceStream->EndTrack(aTrackID);
       // We could override NotifyMediaStreamTrackEnded(), and maybe should, but it's
       // risky to do late in a release since that will affect all track ends, and not
       // just StopTrack()s.
       if (GetDOMTrackFor(aTrackID)) {
-        mListener->StopTrack(aTrackID, !!GetDOMTrackFor(aTrackID)->AsAudioStreamTrack());
+        mListener->StopTrack(aTrackID,
+                             !!GetDOMTrackFor(aTrackID)->AsAudioStreamTrack());
       } else {
-        LOG(("StopTrack(%d) on non-existant track", aTrackID));
+        LOG(("StopTrack(%d) on non-existent track", aTrackID));
       }
     }
   }
 
+  virtual already_AddRefed<Promise>
+  ApplyConstraintsToTrack(TrackID aTrackID,
+                          const MediaTrackConstraints& aConstraints,
+                          ErrorResult &aRv) override
+  {
+    nsPIDOMWindow* window = static_cast<nsPIDOMWindow*>(mWindow.get());
+    nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(window);
+    nsRefPtr<Promise> promise = Promise::Create(go, aRv);
+
+    if (sInShutdown) {
+      nsRefPtr<MediaStreamError> error = new MediaStreamError(window,
+          NS_LITERAL_STRING("AbortError"),
+          NS_LITERAL_STRING("In shutdown"));
+      promise->MaybeReject(error);
+      return promise.forget();
+    }
+    if (!mSourceStream) {
+      nsRefPtr<MediaStreamError> error = new MediaStreamError(window,
+          NS_LITERAL_STRING("InternalError"),
+          NS_LITERAL_STRING("No stream."));
+      promise->MaybeReject(error);
+      return promise.forget();
+    }
+
+    nsRefPtr<dom::MediaStreamTrack> track = GetDOMTrackFor(aTrackID);
+    if (!track) {
+      LOG(("ApplyConstraintsToTrack(%d) on non-existent track", aTrackID));
+      nsRefPtr<MediaStreamError> error = new MediaStreamError(window,
+          NS_LITERAL_STRING("InternalError"),
+          NS_LITERAL_STRING("No track."));
+      promise->MaybeReject(error);
+      return promise.forget();
+    }
+
+    typedef media::Pledge<bool, MediaStreamError*> PledgeVoid;
+
+    nsRefPtr<PledgeVoid> p = mListener->ApplyConstraintsToTrack(window,
+        aTrackID, !!track->AsAudioStreamTrack(), aConstraints);
+    p->Then([promise](bool& aDummy) mutable {
+      promise->MaybeResolve(false);
+    }, [promise](MediaStreamError*& reason) mutable {
+      promise->MaybeReject(reason);
+    });
+    return promise.forget();
+  }
+
 #if 0
   virtual void NotifyMediaStreamTrackEnded(dom::MediaStreamTrack* aTrack)
   {
     TrackID trackID = aTrack->GetTrackID();
     // We override this so we can also tell the backend to stop capturing if the track ends
     LOG(("track %d ending, type = %s",
          trackID, aTrack->AsAudioStreamTrack() ? "audio" : "video"));
     MOZ_ASSERT(aTrack->AsVideoStreamTrack() || aTrack->AsAudioStreamTrack());
@@ -593,52 +797,52 @@ public:
   virtual void RemoveDirectListener(MediaStreamDirectListener *aListener) override
   {
     if (mSourceStream) {
       mSourceStream->RemoveDirectListener(aListener);
     }
   }
 
   // let us intervene for direct listeners when someone does track.enabled = false
-  virtual void SetTrackEnabled(TrackID aID, bool aEnabled) override
+  virtual void SetTrackEnabled(TrackID aTrackID, bool aEnabled) override
   {
     // We encapsulate the SourceMediaStream and TrackUnion into one entity, so
     // we can handle the disabling at the SourceMediaStream
 
-    // We need to find the input track ID for output ID aID, so we let the TrackUnion
+    // We need to find the input track ID for output ID aTrackID, so we let the TrackUnion
     // forward the request to the source and translate the ID
-    GetStream()->AsProcessedStream()->ForwardTrackEnabled(aID, aEnabled);
+    GetStream()->AsProcessedStream()->ForwardTrackEnabled(aTrackID, aEnabled);
   }
 
   virtual DOMLocalMediaStream* AsDOMLocalMediaStream() override
   {
     return this;
   }
 
   virtual MediaEngineSource* GetMediaEngine(TrackID aTrackID) override
   {
     // MediaEngine supports only one video and on video track now and TrackID is
     // fixed in MediaEngine.
     if (aTrackID == kVideoTrack) {
-      return mVideoSource;
+      return mVideoDevice ? mVideoDevice->GetSource() : nullptr;
     }
     else if (aTrackID == kAudioTrack) {
-      return mAudioSource;
+      return mAudioDevice ? mAudioDevice->GetSource() : nullptr;
     }
 
     return nullptr;
   }
 
   // The actual MediaStream is a TrackUnionStream. But these resources need to be
   // explicitly destroyed too.
   nsRefPtr<SourceMediaStream> mSourceStream;
   nsRefPtr<MediaInputPort> mPort;
   nsRefPtr<GetUserMediaCallbackMediaStreamListener> mListener;
-  nsRefPtr<MediaEngineSource> mAudioSource; // so we can turn on AEC
-  nsRefPtr<MediaEngineSource> mVideoSource;
+  nsRefPtr<AudioDevice> mAudioDevice; // so we can turn on AEC
+  nsRefPtr<VideoDevice> mVideoDevice;
   bool mEchoOn;
   bool mAgcOn;
   bool mNoiseOn;
   uint32_t mEcho;
   uint32_t mAgc;
   uint32_t mNoise;
   uint32_t mPlayoutDelay;
 };
@@ -681,21 +885,21 @@ class GetUserMediaStreamRunnable : publi
 {
 public:
   GetUserMediaStreamRunnable(
     nsCOMPtr<nsIDOMGetUserMediaSuccessCallback>& aOnSuccess,
     nsCOMPtr<nsIDOMGetUserMediaErrorCallback>& aOnFailure,
     uint64_t aWindowID,
     GetUserMediaCallbackMediaStreamListener* aListener,
     const nsCString& aOrigin,
-    MediaEngineSource* aAudioSource,
-    MediaEngineSource* aVideoSource,
+    AudioDevice* aAudioDevice,
+    VideoDevice* aVideoDevice,
     PeerIdentity* aPeerIdentity)
-    : mAudioSource(aAudioSource)
-    , mVideoSource(aVideoSource)
+    : mAudioDevice(aAudioDevice)
+    , mVideoDevice(aVideoDevice)
     , mWindowID(aWindowID)
     , mListener(aListener)
     , mOrigin(aOrigin)
     , mPeerIdentity(aPeerIdentity)
     , mManager(MediaManager::GetInstance())
   {
     mOnSuccess.swap(aOnSuccess);
     mOnFailure.swap(aOnFailure);
@@ -782,44 +986,44 @@ public:
         branch->GetBoolPref("media.getusermedia.noise_enabled", &noise_on);
         branch->GetIntPref("media.getusermedia.noise", &noise);
         branch->GetIntPref("media.getusermedia.playout_delay", &playout_delay);
       }
     }
 #endif
 
     MediaStreamGraph::GraphDriverType graphDriverType =
-      mAudioSource ? MediaStreamGraph::AUDIO_THREAD_DRIVER
+      mAudioDevice ? MediaStreamGraph::AUDIO_THREAD_DRIVER
                    : MediaStreamGraph::SYSTEM_THREAD_DRIVER;
     MediaStreamGraph* msg =
       MediaStreamGraph::GetInstance(graphDriverType,
                                     dom::AudioChannel::Normal);
 
     nsRefPtr<SourceMediaStream> stream = msg->CreateSourceStream(nullptr);
 
     nsRefPtr<DOMLocalMediaStream> domStream;
     // AudioCapture is a special case, here, in the sense that we're not really
     // using the audio source and the SourceMediaStream, which acts as
     // placeholders. We re-route a number of stream internaly in the MSG and mix
     // them down instead.
-    if (mAudioSource &&
-        mAudioSource->GetMediaSource() == dom::MediaSourceEnum::AudioCapture) {
+    if (mAudioDevice &&
+        mAudioDevice->GetMediaSource() == dom::MediaSourceEnum::AudioCapture) {
       domStream = DOMLocalMediaStream::CreateAudioCaptureStream(window, msg);
       // It should be possible to pipe the capture stream to anything. CORS is
       // not a problem here, we got explicit user content.
       domStream->SetPrincipal(window->GetExtantDoc()->NodePrincipal());
       msg->RegisterCaptureStreamForWindow(
             mWindowID, domStream->GetStream()->AsProcessedStream());
       window->SetAudioCapture(true);
     } else {
       // Normal case, connect the source stream to the track union stream to
       // avoid us blocking
       nsRefPtr<nsDOMUserMediaStream> trackunion =
         nsDOMUserMediaStream::CreateTrackUnionStream(window, mListener,
-                                                     mAudioSource, mVideoSource,
+                                                     mAudioDevice, mVideoDevice,
                                                      msg);
       trackunion->GetStream()->AsProcessedStream()->SetAutofinish(true);
       nsRefPtr<MediaInputPort> port = trackunion->GetStream()->AsProcessedStream()->
         AllocateInputPort(stream);
       trackunion->mSourceStream = stream;
       trackunion->mPort = port.forget();
       // Log the relationship between SourceMediaStream and TrackUnion stream
       // Make sure logger starts before capture
@@ -854,52 +1058,52 @@ public:
       }
       return NS_OK;
     }
 
     // The listener was added at the beginning in an inactive state.
     // Activate our listener. We'll call Start() on the source when get a callback
     // that the MediaStream has started consuming. The listener is freed
     // when the page is invalidated (on navigation or close).
-    mListener->Activate(stream.forget(), mAudioSource, mVideoSource);
+    mListener->Activate(stream.forget(), mAudioDevice, mVideoDevice);
 
     // Note: includes JS callbacks; must be released on MainThread
     TracksAvailableCallback* tracksAvailableCallback =
       new TracksAvailableCallback(mManager, mOnSuccess, mWindowID, domStream);
 
     mListener->AudioConfig(aec_on, (uint32_t) aec,
                            agc_on, (uint32_t) agc,
                            noise_on, (uint32_t) noise,
                            playout_delay);
 
     // Dispatch to the media thread to ask it to start the sources,
     // because that can take a while.
     // Pass ownership of trackunion to the MediaOperationTask
     // to ensure it's kept alive until the MediaOperationTask runs (at least).
-    MediaManager::PostTask(
-      FROM_HERE, new MediaOperationTask(MEDIA_START, mListener, domStream,
-                                        tracksAvailableCallback, mAudioSource,
-                                        mVideoSource, false, mWindowID,
-                                        mOnFailure.forget()));
+    MediaManager::PostTask(FROM_HERE,
+        new MediaOperationTask(MEDIA_START, mListener, domStream,
+                               tracksAvailableCallback,
+                               mAudioDevice, mVideoDevice,
+                               false, mWindowID, mOnFailure.forget()));
     // We won't need mOnFailure now.
     mOnFailure = nullptr;
 
     if (!MediaManager::IsPrivateBrowsing(window)) {
       // Call GetOriginKey again, this time w/persist = true, to promote
       // deviceIds to persistent, in case they're not already. Fire'n'forget.
       nsRefPtr<Pledge<nsCString>> p = media::GetOriginKey(mOrigin, false, true);
     }
     return NS_OK;
   }
 
 private:
   nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> mOnSuccess;
   nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mOnFailure;
-  nsRefPtr<MediaEngineSource> mAudioSource;
-  nsRefPtr<MediaEngineSource> mVideoSource;
+  nsRefPtr<AudioDevice> mAudioDevice;
+  nsRefPtr<VideoDevice> mVideoDevice;
   uint64_t mWindowID;
   nsRefPtr<GetUserMediaCallbackMediaStreamListener> mListener;
   nsCString mOrigin;
   nsAutoPtr<PeerIdentity> mPeerIdentity;
   nsRefPtr<MediaManager> mManager; // get ref to this when creating the runnable
 };
 
 static bool
@@ -944,137 +1148,16 @@ GetSources(MediaEngine *engine, dom::Med
     }
   } else {
     for (auto& source : sources) {
       aResult.AppendElement(new DeviceType(source));
     }
   }
 }
 
-template<class DeviceType>
-static bool
-AreUnfitSettings(const MediaTrackConstraints &aConstraints,
-                 nsTArray<nsRefPtr<DeviceType>>& aSources)
-{
-  nsTArray<const MediaTrackConstraintSet*> aggregateConstraints;
-  aggregateConstraints.AppendElement(&aConstraints);
-
-  for (auto& source : aSources) {
-    if (source->GetBestFitnessDistance(aggregateConstraints) != UINT32_MAX) {
-      return false;
-    }
-  }
-  return true;
-}
-
-// Apply constrains to a supplied list of sources (removes items from the list)
-
-template<class DeviceType>
-static const char*
-SelectSettings(const MediaTrackConstraints &aConstraints,
-               nsTArray<nsRefPtr<DeviceType>>& aSources)
-{
-  auto& c = aConstraints;
-
-  // First apply top-level constraints.
-
-  // Stack constraintSets that pass, starting with the required one, because the
-  // whole stack must be re-satisfied each time a capability-set is ruled out
-  // (this avoids storing state or pushing algorithm into the lower-level code).
-  nsTArray<nsRefPtr<DeviceType>> unsatisfactory;
-  nsTArray<const MediaTrackConstraintSet*> aggregateConstraints;
-  aggregateConstraints.AppendElement(&c);
-
-  std::multimap<uint32_t, nsRefPtr<DeviceType>> ordered;
-
-  for (uint32_t i = 0; i < aSources.Length();) {
-    uint32_t distance = aSources[i]->GetBestFitnessDistance(aggregateConstraints);
-    if (distance == UINT32_MAX) {
-      unsatisfactory.AppendElement(aSources[i]);
-      aSources.RemoveElementAt(i);
-    } else {
-      ordered.insert(std::pair<uint32_t, nsRefPtr<DeviceType>>(distance,
-                                                               aSources[i]));
-      ++i;
-    }
-  }
-  if (!aSources.Length()) {
-    // None selected. The spec says to report a constraint that satisfies NONE
-    // of the sources. Unfortunately, this is a bit laborious to find out, and
-    // requires updating as new constraints are added!
-
-    if (c.mDeviceId.IsConstrainDOMStringParameters()) {
-      MediaTrackConstraints fresh;
-      fresh.mDeviceId = c.mDeviceId;
-      if (AreUnfitSettings(fresh, unsatisfactory)) {
-        return "deviceId";
-      }
-    }
-    if (c.mWidth.IsConstrainLongRange()) {
-      MediaTrackConstraints fresh;
-      fresh.mWidth = c.mWidth;
-      if (AreUnfitSettings(fresh, unsatisfactory)) {
-        return "width";
-      }
-    }
-    if (c.mHeight.IsConstrainLongRange()) {
-      MediaTrackConstraints fresh;
-      fresh.mHeight = c.mHeight;
-      if (AreUnfitSettings(fresh, unsatisfactory)) {
-        return "height";
-      }
-    }
-    if (c.mFrameRate.IsConstrainDoubleRange()) {
-      MediaTrackConstraints fresh;
-      fresh.mFrameRate = c.mFrameRate;
-      if (AreUnfitSettings(fresh, unsatisfactory)) {
-        return "frameRate";
-      }
-    }
-    if (c.mFacingMode.IsConstrainDOMStringParameters()) {
-      MediaTrackConstraints fresh;
-      fresh.mFacingMode = c.mFacingMode;
-      if (AreUnfitSettings(fresh, unsatisfactory)) {
-        return "facingMode";
-      }
-    }
-    return "";
-  }
-
-  // Order devices by shortest distance
-  for (auto& ordinal : ordered) {
-    aSources.RemoveElement(ordinal.second);
-    aSources.AppendElement(ordinal.second);
-  }
-
-  // Then apply advanced constraints.
-
-  if (c.mAdvanced.WasPassed()) {
-    auto &array = c.mAdvanced.Value();
-
-    for (int i = 0; i < int(array.Length()); i++) {
-      aggregateConstraints.AppendElement(&array[i]);
-      nsTArray<nsRefPtr<DeviceType>> rejects;
-      for (uint32_t j = 0; j < aSources.Length();) {
-        if (aSources[j]->GetBestFitnessDistance(aggregateConstraints) == UINT32_MAX) {
-          rejects.AppendElement(aSources[j]);
-          aSources.RemoveElementAt(j);
-        } else {
-          ++j;
-        }
-      }
-      if (!aSources.Length()) {
-        aSources.AppendElements(Move(rejects));
-        aggregateConstraints.RemoveElementAt(aggregateConstraints.Length() - 1);
-      }
-    }
-  }
-  return nullptr;
-}
-
 static const char*
 SelectSettings(MediaStreamConstraints &aConstraints,
                nsTArray<nsRefPtr<MediaDevice>>& aSources)
 {
   // Since the advanced part of the constraints algorithm needs to know when
   // a candidate set is overconstrained (zero members), we must split up the
   // list into videos and audios, and put it back together again at the end.
 
@@ -1091,23 +1174,25 @@ SelectSettings(MediaStreamConstraints &a
     }
   }
   aSources.Clear();
   MOZ_ASSERT(!aSources.Length());
 
   const char* badConstraint = nullptr;
 
   if (IsOn(aConstraints.mVideo)) {
-    badConstraint = SelectSettings(GetInvariant(aConstraints.mVideo), videos);
+    badConstraint = MediaConstraintsHelper::SelectSettings(
+        GetInvariant(aConstraints.mVideo), videos);
     for (auto& video : videos) {
       aSources.AppendElement(video);
     }
   }
   if (audios.Length() && IsOn(aConstraints.mAudio)) {
-    badConstraint = SelectSettings(GetInvariant(aConstraints.mAudio), audios);
+    badConstraint = MediaConstraintsHelper::SelectSettings(
+        GetInvariant(aConstraints.mAudio), audios);
     for (auto& audio : audios) {
       aSources.AppendElement(audio);
     }
   }
   return badConstraint;
 }
 
 /**
@@ -1195,23 +1280,20 @@ public:
         return;
       }
     }
     PeerIdentity* peerIdentity = nullptr;
     if (!mConstraints.mPeerIdentity.IsEmpty()) {
       peerIdentity = new PeerIdentity(mConstraints.mPeerIdentity);
     }
 
-    NS_DispatchToMainThread(do_AddRef(new GetUserMediaStreamRunnable(
-      mOnSuccess, mOnFailure, mWindowID, mListener, mOrigin,
-      (mAudioDevice? mAudioDevice->GetSource() : nullptr),
-      (mVideoDevice? mVideoDevice->GetSource() : nullptr),
-      peerIdentity
-    )));
-
+    NS_DispatchToMainThread(do_AddRef(
+        new GetUserMediaStreamRunnable(mOnSuccess, mOnFailure, mWindowID,
+                                       mListener, mOrigin, mAudioDevice,
+                                       mVideoDevice, peerIdentity)));
     MOZ_ASSERT(!mOnSuccess);
     MOZ_ASSERT(!mOnFailure);
   }
 
   nsresult
   Denied(const nsAString& aName,
          const nsAString& aMessage = EmptyString())
   {
@@ -2026,18 +2108,18 @@ MediaManager::GetUserMedia(nsPIDOMWindow
       nsRefPtr<GetUserMediaRequest> req =
           new GetUserMediaRequest(window, callID, c, isHTTPS);
       obs->NotifyObservers(req, "getUserMedia:request", nullptr);
     }
 
 #ifdef MOZ_WEBRTC
     EnableWebRtcLog();
 #endif
-  }, [onFailure](MediaStreamError& reason) mutable {
-    onFailure->OnError(&reason);
+  }, [onFailure](MediaStreamError*& reason) mutable {
+    onFailure->OnError(reason);
   });
   return NS_OK;
 }
 
 /* static */ void
 MediaManager::AnonymizeDevices(SourceSet& aDevices, const nsACString& aOriginKey)
 {
   if (!aOriginKey.IsEmpty()) {
@@ -2207,20 +2289,20 @@ MediaManager::EnumerateDevices(nsPIDOMWi
                                                      dom::MediaSourceEnum::Microphone,
                                                      fake);
   p->Then([onSuccess, windowId, listener](SourceSet*& aDevices) mutable {
     ScopedDeletePtr<SourceSet> devices(aDevices); // grab result
     nsRefPtr<MediaManager> mgr = MediaManager_GetInstance();
     mgr->RemoveFromWindowList(windowId, listener);
     nsCOMPtr<nsIWritableVariant> array = MediaManager_ToJSArray(*devices);
     onSuccess->OnSuccess(array);
-  }, [onFailure, windowId, listener](MediaStreamError& reason) mutable {
+  }, [onFailure, windowId, listener](MediaStreamError*& reason) mutable {
     nsRefPtr<MediaManager> mgr = MediaManager_GetInstance();
     mgr->RemoveFromWindowList(windowId, listener);
-    onFailure->OnError(&reason);
+    onFailure->OnError(reason);
   });
   return NS_OK;
 }
 
 /*
  * GetUserMediaDevices - called by the UI-part of getUserMedia from chrome JS.
  */
 
@@ -2922,20 +3004,20 @@ MediaManager::IsActivelyCapturingOrHasAP
 
 void
 GetUserMediaCallbackMediaStreamListener::AudioConfig(bool aEchoOn,
               uint32_t aEcho,
               bool aAgcOn, uint32_t aAGC,
               bool aNoiseOn, uint32_t aNoise,
               int32_t aPlayoutDelay)
 {
-  if (mAudioSource) {
+  if (mAudioDevice) {
 #ifdef MOZ_WEBRTC
     MediaManager::PostTask(FROM_HERE,
-      NewRunnableMethod(mAudioSource.get(), &MediaEngineSource::Config,
+      NewRunnableMethod(mAudioDevice->GetSource(), &MediaEngineSource::Config,
                         aEchoOn, aEcho, aAgcOn, aAGC, aNoiseOn,
                         aNoise, aPlayoutDelay));
 #endif
   }
 }
 
 // Can be invoked from EITHER MainThread or MSG thread
 void
@@ -2943,68 +3025,162 @@ GetUserMediaCallbackMediaStreamListener:
 {
   // We can't take a chance on blocking here, so proxy this to another
   // thread.
   // Pass a ref to us (which is threadsafe) so it can query us for the
   // source stream info.
   MediaManager::PostTask(FROM_HERE,
     new MediaOperationTask(MEDIA_STOP,
                            this, nullptr, nullptr,
-                           mAudioSource, mVideoSource,
+                           mAudioDevice, mVideoDevice,
                            mFinished, mWindowID, nullptr));
 }
 
 // Doesn't kill audio
 // XXX refactor to combine with Invalidate()?
 void
 GetUserMediaCallbackMediaStreamListener::StopSharing()
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
-  if (mVideoSource && !mStopped &&
-      (mVideoSource->GetMediaSource() == dom::MediaSourceEnum::Screen ||
-       mVideoSource->GetMediaSource() == dom::MediaSourceEnum::Application ||
-       mVideoSource->GetMediaSource() == dom::MediaSourceEnum::Window)) {
+  if (mVideoDevice && !mStopped &&
+      (mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Screen ||
+       mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Application ||
+       mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Window)) {
     // Stop the whole stream if there's no audio; just the video track if we have both
     MediaManager::PostTask(FROM_HERE,
-      new MediaOperationTask(mAudioSource ? MEDIA_STOP_TRACK : MEDIA_STOP,
+      new MediaOperationTask(mAudioDevice ? MEDIA_STOP_TRACK : MEDIA_STOP,
                              this, nullptr, nullptr,
-                             nullptr, mVideoSource,
+                             nullptr, mVideoDevice,
                              mFinished, mWindowID, nullptr));
-  } else if (mAudioSource &&
-             mAudioSource->GetMediaSource() == dom::MediaSourceEnum::AudioCapture) {
+  } else if (mAudioDevice &&
+             mAudioDevice->GetMediaSource() == dom::MediaSourceEnum::AudioCapture) {
     nsCOMPtr<nsPIDOMWindow> window = nsGlobalWindow::GetInnerWindowWithId(mWindowID);
     MOZ_ASSERT(window);
     window->SetAudioCapture(false);
     MediaStreamGraph* graph =
       MediaStreamGraph::GetInstance(MediaStreamGraph::AUDIO_THREAD_DRIVER,
                                     dom::AudioChannel::Normal);
     graph->UnregisterCaptureStreamForWindow(mWindowID);
     mStream->Destroy();
   }
 }
 
+// ApplyConstraints for track
+
+already_AddRefed<GetUserMediaCallbackMediaStreamListener::PledgeVoid>
+GetUserMediaCallbackMediaStreamListener::ApplyConstraintsToTrack(
+    nsPIDOMWindow* aWindow,
+    TrackID aTrackID,
+    bool aIsAudio,
+    const MediaTrackConstraints& aConstraints)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  nsRefPtr<PledgeVoid> p = new PledgeVoid();
+
+  if (!(((aIsAudio && mAudioDevice) ||
+         (!aIsAudio && mVideoDevice)) && !mStopped))
+  {
+    LOG(("gUM track %d applyConstraints, but we don't have type %s",
+         aTrackID, aIsAudio ? "audio" : "video"));
+    p->Resolve(false);
+    return p.forget();
+  }
+
+  // XXX to support multiple tracks of a type in a stream, this should key off
+  // the TrackID and not just the type
+  nsRefPtr<AudioDevice> audioDevice = aIsAudio ? mAudioDevice.get() : nullptr;
+  nsRefPtr<VideoDevice> videoDevice = !aIsAudio ? mVideoDevice.get() : nullptr;
+
+  nsRefPtr<MediaManager> mgr = MediaManager::GetInstance();
+  uint32_t id = mgr->mOutstandingVoidPledges.Append(*p);
+  uint64_t windowId = aWindow->WindowID();
+
+  MediaManager::PostTask(FROM_HERE, NewTaskFrom([id, windowId,
+                                                 audioDevice, videoDevice,
+                                                 aConstraints]() mutable {
+    MOZ_ASSERT(MediaManager::IsInMediaThread());
+    nsRefPtr<MediaManager> mgr = MediaManager::GetInstance();
+    const char* badConstraint = nullptr;
+    nsresult rv = NS_OK;
+
+    if (audioDevice) {
+      rv = audioDevice->Restart(aConstraints, mgr->mPrefs);
+      if (rv == NS_ERROR_NOT_AVAILABLE) {
+        nsTArray<nsRefPtr<AudioDevice>> audios;
+        audios.AppendElement(audioDevice);
+        badConstraint = MediaConstraintsHelper::SelectSettings(aConstraints,
+                                                               audios);
+      }
+    } else {
+      rv = videoDevice->Restart(aConstraints, mgr->mPrefs);
+      if (rv == NS_ERROR_NOT_AVAILABLE) {
+        nsTArray<nsRefPtr<VideoDevice>> videos;
+        videos.AppendElement(videoDevice);
+        badConstraint = MediaConstraintsHelper::SelectSettings(aConstraints,
+                                                               videos);
+      }
+    }
+    NS_DispatchToMainThread(do_AddRef(NewRunnableFrom([id, windowId, rv,
+                                                       badConstraint]() mutable {
+      MOZ_ASSERT(NS_IsMainThread());
+      nsRefPtr<MediaManager> mgr = MediaManager_GetInstance();
+      if (!mgr) {
+        return NS_OK;
+      }
+      nsRefPtr<PledgeVoid> p = mgr->mOutstandingVoidPledges.Remove(id);
+      if (p) {
+        if (NS_SUCCEEDED(rv)) {
+          p->Resolve(false);
+        } else {
+          nsPIDOMWindow *window = static_cast<nsPIDOMWindow*>
+              (nsGlobalWindow::GetInnerWindowWithId(windowId));
+          if (window) {
+            if (rv == NS_ERROR_NOT_AVAILABLE) {
+              nsString constraint;
+              constraint.AssignASCII(badConstraint);
+              nsRefPtr<MediaStreamError> error =
+                  new MediaStreamError(window,
+                                       NS_LITERAL_STRING("OverconstrainedError"),
+                                       NS_LITERAL_STRING(""),
+                                       constraint);
+              p->Reject(error);
+            } else {
+              nsRefPtr<MediaStreamError> error =
+                  new MediaStreamError(window,
+                                       NS_LITERAL_STRING("InternalError"));
+              p->Reject(error);
+            }
+          }
+        }
+      }
+      return NS_OK;
+    })));
+  }));
+  return p.forget();
+}
+
 // Stop backend for track
 
 void
-GetUserMediaCallbackMediaStreamListener::StopTrack(TrackID aID, bool aIsAudio)
+GetUserMediaCallbackMediaStreamListener::StopTrack(TrackID aTrackID, bool aIsAudio)
 {
-  if (((aIsAudio && mAudioSource) ||
-       (!aIsAudio && mVideoSource)) && !mStopped)
+  if (((aIsAudio && mAudioDevice) ||
+       (!aIsAudio && mVideoDevice)) && !mStopped)
   {
     // XXX to support multiple tracks of a type in a stream, this should key off
     // the TrackID and not just the type
     MediaManager::PostTask(FROM_HERE,
       new MediaOperationTask(MEDIA_STOP_TRACK,
                              this, nullptr, nullptr,
-                             aIsAudio  ? mAudioSource.get() : nullptr,
-                             !aIsAudio ? mVideoSource.get() : nullptr,
+                             aIsAudio  ? mAudioDevice.get() : nullptr,
+                             !aIsAudio ? mVideoDevice.get() : nullptr,
                              mFinished, mWindowID, nullptr));
   } else {
     LOG(("gUM track %d ended, but we don't have type %s",
-         aID, aIsAudio ? "audio" : "video"));
+         aTrackID, aIsAudio ? "audio" : "video"));
   }
 }
 
 // Called from the MediaStreamGraph thread
 void
 GetUserMediaCallbackMediaStreamListener::NotifyFinished(MediaStreamGraph* aGraph)
 {
   mFinished = true;
@@ -3015,17 +3191,17 @@ GetUserMediaCallbackMediaStreamListener:
 // Called from the MediaStreamGraph thread
 void
 GetUserMediaCallbackMediaStreamListener::NotifyDirectListeners(MediaStreamGraph* aGraph,
                                                                bool aHasListeners)
 {
   MediaManager::PostTask(FROM_HERE,
     new MediaOperationTask(MEDIA_DIRECT_LISTENERS,
                            this, nullptr, nullptr,
-                           mAudioSource, mVideoSource,
+                           mAudioDevice, mVideoDevice,
                            aHasListeners, mWindowID, nullptr));
 }
 
 // Called from the MediaStreamGraph thread
 // this can be in response to our own RemoveListener() (via ::Remove()), or
 // because the DOM GC'd the DOMLocalMediaStream/etc we're attached to.
 void
 GetUserMediaCallbackMediaStreamListener::NotifyRemoved(MediaStreamGraph* aGraph)
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -42,22 +42,82 @@
 
 #ifdef MOZ_WIDGET_GONK
 #include "DOMCameraManager.h"
 #endif
 
 namespace mozilla {
 namespace dom {
 struct MediaStreamConstraints;
+struct MediaTrackConstraints;
 struct MediaTrackConstraintSet;
 } // namespace dom
 
 extern PRLogModuleInfo* GetMediaManagerLog();
 #define MM_LOG(msg) MOZ_LOG(GetMediaManagerLog(), mozilla::LogLevel::Debug, msg)
 
+class MediaDevice : public nsIMediaDevice
+{
+public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSIMEDIADEVICE
+
+  void SetId(const nsAString& aID);
+  virtual uint32_t GetBestFitnessDistance(
+      const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets);
+protected:
+  virtual ~MediaDevice() {}
+  explicit MediaDevice(MediaEngineSource* aSource, bool aIsVideo);
+  static uint32_t FitnessDistance(nsString aN,
+    const dom::OwningStringOrStringSequenceOrConstrainDOMStringParameters& aConstraint);
+private:
+  static bool StringsContain(const dom::OwningStringOrStringSequence& aStrings,
+                             nsString aN);
+  static uint32_t FitnessDistance(nsString aN,
+      const dom::ConstrainDOMStringParameters& aParams);
+protected:
+  nsString mName;
+  nsString mID;
+  dom::MediaSourceEnum mMediaSource;
+  nsRefPtr<MediaEngineSource> mSource;
+public:
+  dom::MediaSourceEnum GetMediaSource() {
+    return mMediaSource;
+  }
+  bool mIsVideo;
+};
+
+class VideoDevice : public MediaDevice
+{
+public:
+  typedef MediaEngineVideoSource Source;
+
+  explicit VideoDevice(Source* aSource);
+  NS_IMETHOD GetType(nsAString& aType);
+  Source* GetSource();
+  nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
+                    const MediaEnginePrefs &aPrefs);
+  nsresult Restart(const dom::MediaTrackConstraints &aConstraints,
+                   const MediaEnginePrefs &aPrefs);
+};
+
+class AudioDevice : public MediaDevice
+{
+public:
+  typedef MediaEngineAudioSource Source;
+
+  explicit AudioDevice(Source* aSource);
+  NS_IMETHOD GetType(nsAString& aType);
+  Source* GetSource();
+  nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
+                    const MediaEnginePrefs &aPrefs);
+  nsresult Restart(const dom::MediaTrackConstraints &aConstraints,
+                   const MediaEnginePrefs &aPrefs);
+};
+
 /**
  * This class is an implementation of MediaStreamListener. This is used
  * to Start() and Stop() the underlying MediaEngineSource when MediaStreams
  * are assigned and deassigned in content.
  */
 class GetUserMediaCallbackMediaStreamListener : public MediaStreamListener
 {
 public:
@@ -74,23 +134,23 @@ public:
   ~GetUserMediaCallbackMediaStreamListener()
   {
     unused << mMediaThread;
     // It's OK to release mStream on any thread; they have thread-safe
     // refcounts.
   }
 
   void Activate(already_AddRefed<SourceMediaStream> aStream,
-    MediaEngineSource* aAudioSource,
-    MediaEngineSource* aVideoSource)
+                AudioDevice* aAudioDevice,
+                VideoDevice* aVideoDevice)
   {
     NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
     mStream = aStream;
-    mAudioSource = aAudioSource;
-    mVideoSource = aVideoSource;
+    mAudioDevice = aAudioDevice;
+    mVideoDevice = aVideoDevice;
 
     mStream->AddListener(this);
   }
 
   MediaStream *Stream() // Can be used to test if Activate was called
   {
     return mStream;
   }
@@ -102,56 +162,67 @@ public:
     }
     return mStream->AsSourceStream();
   }
 
   void StopSharing();
 
   void StopTrack(TrackID aID, bool aIsAudio);
 
-  // mVideo/AudioSource are set by Activate(), so we assume they're capturing
+  typedef media::Pledge<bool, dom::MediaStreamError*> PledgeVoid;
+
+  already_AddRefed<PledgeVoid>
+  ApplyConstraintsToTrack(nsPIDOMWindow* aWindow,
+                          TrackID aID, bool aIsAudio,
+                          const dom::MediaTrackConstraints& aConstraints);
+
+  // mVideo/AudioDevice are set by Activate(), so we assume they're capturing
   // if set and represent a real capture device.
   bool CapturingVideo()
   {
     NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
-    return mVideoSource && !mStopped &&
-           mVideoSource->GetMediaSource() == dom::MediaSourceEnum::Camera &&
-           (!mVideoSource->IsFake() ||
+    return mVideoDevice && !mStopped &&
+           mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Camera &&
+           (!mVideoDevice->GetSource()->IsFake() ||
             Preferences::GetBool("media.navigator.permission.fake"));
   }
   bool CapturingAudio()
   {
     NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
-    return mAudioSource && !mStopped &&
-           (!mAudioSource->IsFake() ||
+    return mAudioDevice && !mStopped &&
+           (!mAudioDevice->GetSource()->IsFake() ||
             Preferences::GetBool("media.navigator.permission.fake"));
   }
   bool CapturingScreen()
   {
     NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
-    return mVideoSource && !mStopped && !mVideoSource->IsAvailable() &&
-           mVideoSource->GetMediaSource() == dom::MediaSourceEnum::Screen;
+    return mVideoDevice && !mStopped &&
+           !mVideoDevice->GetSource()->IsAvailable() &&
+           mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Screen;
   }
   bool CapturingWindow()
   {
     NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
-    return mVideoSource && !mStopped && !mVideoSource->IsAvailable() &&
-           mVideoSource->GetMediaSource() == dom::MediaSourceEnum::Window;
+    return mVideoDevice && !mStopped &&
+           !mVideoDevice->GetSource()->IsAvailable() &&
+           mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Window;
   }
   bool CapturingApplication()
   {
     NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
-    return mVideoSource && !mStopped && !mVideoSource->IsAvailable() &&
-           mVideoSource->GetMediaSource() == dom::MediaSourceEnum::Application;
+    return mVideoDevice && !mStopped &&
+           !mVideoDevice->GetSource()->IsAvailable() &&
+           mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Application;
   }
   bool CapturingBrowser()
   {
     NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
-    return mVideoSource && !mStopped && mVideoSource->IsAvailable() &&
-           mVideoSource->GetMediaSource() == dom::MediaSourceEnum::Browser;
+    return mVideoDevice && !mStopped &&
+           mVideoDevice->GetSource()->IsAvailable() &&
+           mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Browser;
   }
 
   void SetStopped()
   {
     mStopped = true;
   }
 
   // implement in .cpp to avoid circular dependency with MediaOperationTask
@@ -182,21 +253,23 @@ public:
   }
 
   // Proxy NotifyPull() to sources
   virtual void
   NotifyPull(MediaStreamGraph* aGraph, StreamTime aDesiredTime) override
   {
     // Currently audio sources ignore NotifyPull, but they could
     // watch it especially for fake audio.
-    if (mAudioSource) {
-      mAudioSource->NotifyPull(aGraph, mStream, kAudioTrack, aDesiredTime);
+    if (mAudioDevice) {
+      mAudioDevice->GetSource()->NotifyPull(aGraph, mStream, kAudioTrack,
+                                            aDesiredTime);
     }
-    if (mVideoSource) {
-      mVideoSource->NotifyPull(aGraph, mStream, kVideoTrack, aDesiredTime);
+    if (mVideoDevice) {
+      mVideoDevice->GetSource()->NotifyPull(aGraph, mStream, kVideoTrack,
+                                            aDesiredTime);
     }
   }
 
   virtual void
   NotifyEvent(MediaStreamGraph* aGraph,
               MediaStreamListener::MediaStreamGraphEvent aEvent) override
   {
     switch (aEvent) {
@@ -232,33 +305,33 @@ private:
   uint64_t mWindowID;
 
   bool mStopped; // MainThread only
 
   // Set at Activate on MainThread
 
   // Accessed from MediaStreamGraph thread, MediaManager thread, and MainThread
   // No locking needed as they're only addrefed except on the MediaManager thread
-  nsRefPtr<MediaEngineSource> mAudioSource; // threadsafe refcnt
-  nsRefPtr<MediaEngineSource> mVideoSource; // threadsafe refcnt
+  nsRefPtr<AudioDevice> mAudioDevice; // threadsafe refcnt
+  nsRefPtr<VideoDevice> mVideoDevice; // threadsafe refcnt
   nsRefPtr<SourceMediaStream> mStream; // threadsafe refcnt
   bool mFinished;
 
   // Accessed from MainThread and MSG thread
   Mutex mLock; // protects mRemoved access from MainThread
   bool mRemoved;
 };
 
 class GetUserMediaNotificationEvent: public nsRunnable
 {
   public:
     enum GetUserMediaStatus {
       STARTING,
       STOPPING,
-      STOPPED_TRACK
+      STOPPED_TRACK,
     };
     GetUserMediaNotificationEvent(GetUserMediaCallbackMediaStreamListener* aListener,
                                   GetUserMediaStatus aStatus,
                                   bool aIsAudio, bool aIsVideo, uint64_t aWindowID)
     : mListener(aListener) , mStatus(aStatus) , mIsAudio(aIsAudio)
     , mIsVideo(aIsVideo), mWindowID(aWindowID) {}
 
     GetUserMediaNotificationEvent(GetUserMediaStatus aStatus,
@@ -286,17 +359,17 @@ class GetUserMediaNotificationEvent: pub
     uint64_t mWindowID;
     nsRefPtr<nsIDOMGetUserMediaErrorCallback> mOnFailure;
 };
 
 typedef enum {
   MEDIA_START,
   MEDIA_STOP,
   MEDIA_STOP_TRACK,
-  MEDIA_DIRECT_LISTENERS
+  MEDIA_DIRECT_LISTENERS,
 } MediaOperation;
 
 class MediaManager;
 class GetUserMediaTask;
 
 class ReleaseMediaOperationResource : public nsRunnable
 {
 public:
@@ -305,225 +378,29 @@ public:
     mStream(aStream),
     mOnTracksAvailableCallback(aOnTracksAvailableCallback) {}
   NS_IMETHOD Run() override {return NS_OK;}
 private:
   nsRefPtr<DOMMediaStream> mStream;
   nsAutoPtr<DOMMediaStream::OnTracksAvailableCallback> mOnTracksAvailableCallback;
 };
 
-// Generic class for running long media operations like Start off the main
-// thread, and then (because nsDOMMediaStreams aren't threadsafe),
-// ProxyReleases mStream since it's cycle collected.
-class MediaOperationTask : public Task
-{
-public:
-  // so we can send Stop without AddRef()ing from the MSG thread
-  MediaOperationTask(MediaOperation aType,
-    GetUserMediaCallbackMediaStreamListener* aListener,
-    DOMMediaStream* aStream,
-    DOMMediaStream::OnTracksAvailableCallback* aOnTracksAvailableCallback,
-    MediaEngineSource* aAudioSource,
-    MediaEngineSource* aVideoSource,
-    bool aBool,
-    uint64_t aWindowID,
-    already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError)
-    : mType(aType)
-    , mStream(aStream)
-    , mOnTracksAvailableCallback(aOnTracksAvailableCallback)
-    , mAudioSource(aAudioSource)
-    , mVideoSource(aVideoSource)
-    , mListener(aListener)
-    , mBool(aBool)
-    , mWindowID(aWindowID)
-    , mOnFailure(aError)
-  {}
-
-  ~MediaOperationTask()
-  {
-    // MediaStreams can be released on any thread.
-  }
-
-  void
-  ReturnCallbackError(nsresult rv, const char* errorLog);
-
-  void
-  Run()
-  {
-    SourceMediaStream *source = mListener->GetSourceStream();
-    // No locking between these is required as all the callbacks for the
-    // same MediaStream will occur on the same thread.
-    if (!source) // means the stream was never Activated()
-      return;
-
-    switch (mType) {
-      case MEDIA_START:
-        {
-          NS_ASSERTION(!NS_IsMainThread(), "Never call on main thread");
-          nsresult rv;
-
-          if (mAudioSource) {
-            rv = mAudioSource->Start(source, kAudioTrack);
-            if (NS_FAILED(rv)) {
-              ReturnCallbackError(rv, "Starting audio failed");
-              return;
-            }
-          }
-          if (mVideoSource) {
-            rv = mVideoSource->Start(source, kVideoTrack);
-            if (NS_FAILED(rv)) {
-              ReturnCallbackError(rv, "Starting video failed");
-              return;
-            }
-          }
-          // Start() queued the tracks to be added synchronously to avoid races
-          source->FinishAddTracks();
-
-          source->SetPullEnabled(true);
-          source->AdvanceKnownTracksTime(STREAM_TIME_MAX);
-
-          MM_LOG(("started all sources"));
-          // Forward mOnTracksAvailableCallback to GetUserMediaNotificationEvent,
-          // because mOnTracksAvailableCallback needs to be added to mStream
-          // on the main thread.
-          nsIRunnable *event =
-            new GetUserMediaNotificationEvent(GetUserMediaNotificationEvent::STARTING,
-                                              mStream.forget(),
-                                              mOnTracksAvailableCallback.forget(),
-                                              mAudioSource != nullptr,
-                                              mVideoSource != nullptr,
-                                              mWindowID, mOnFailure.forget());
-          // event must always be released on mainthread due to the JS callbacks
-          // in the TracksAvailableCallback
-          NS_DispatchToMainThread(event);
-        }
-        break;
-
-      case MEDIA_STOP:
-      case MEDIA_STOP_TRACK:
-        {
-          NS_ASSERTION(!NS_IsMainThread(), "Never call on main thread");
-          if (mAudioSource) {
-            mAudioSource->Stop(source, kAudioTrack);
-            mAudioSource->Deallocate();
-          }
-          if (mVideoSource) {
-            mVideoSource->Stop(source, kVideoTrack);
-            mVideoSource->Deallocate();
-          }
-          // Do this after stopping all tracks with EndTrack()
-          if (mBool) {
-            source->Finish();
-          }
-
-          nsIRunnable *event =
-            new GetUserMediaNotificationEvent(mListener,
-                                              mType == MEDIA_STOP ?
-                                              GetUserMediaNotificationEvent::STOPPING :
-                                              GetUserMediaNotificationEvent::STOPPED_TRACK,
-                                              mAudioSource != nullptr,
-                                              mVideoSource != nullptr,
-                                              mWindowID);
-          // event must always be released on mainthread due to the JS callbacks
-          // in the TracksAvailableCallback
-          NS_DispatchToMainThread(event);
-        }
-        break;
-
-      case MEDIA_DIRECT_LISTENERS:
-        {
-          NS_ASSERTION(!NS_IsMainThread(), "Never call on main thread");
-          if (mVideoSource) {
-            mVideoSource->SetDirectListeners(mBool);
-          }
-        }
-        break;
-
-      default:
-        MOZ_ASSERT(false,"invalid MediaManager operation");
-        break;
-    }
-  }
-
-private:
-  MediaOperation mType;
-  nsRefPtr<DOMMediaStream> mStream;
-  nsAutoPtr<DOMMediaStream::OnTracksAvailableCallback> mOnTracksAvailableCallback;
-  nsRefPtr<MediaEngineSource> mAudioSource; // threadsafe
-  nsRefPtr<MediaEngineSource> mVideoSource; // threadsafe
-  nsRefPtr<GetUserMediaCallbackMediaStreamListener> mListener; // threadsafe
-  bool mBool;
-  uint64_t mWindowID;
-  nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mOnFailure;
-};
-
 typedef nsTArray<nsRefPtr<GetUserMediaCallbackMediaStreamListener> > StreamListeners;
 typedef nsClassHashtable<nsUint64HashKey, StreamListeners> WindowTable;
 
-class MediaDevice : public nsIMediaDevice
-{
-public:
-  NS_DECL_THREADSAFE_ISUPPORTS
-  NS_DECL_NSIMEDIADEVICE
-
-  void SetId(const nsAString& aID);
-  virtual uint32_t GetBestFitnessDistance(
-      const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets);
-protected:
-  virtual ~MediaDevice() {}
-  explicit MediaDevice(MediaEngineSource* aSource, bool aIsVideo);
-  static uint32_t FitnessDistance(nsString aN,
-    const dom::OwningStringOrStringSequenceOrConstrainDOMStringParameters& aConstraint);
-private:
-  static bool StringsContain(const dom::OwningStringOrStringSequence& aStrings,
-                             nsString aN);
-  static uint32_t FitnessDistance(nsString aN,
-      const dom::ConstrainDOMStringParameters& aParams);
-protected:
-  nsString mName;
-  nsString mID;
-  dom::MediaSourceEnum mMediaSource;
-  nsRefPtr<MediaEngineSource> mSource;
-public:
-  bool mIsVideo;
-};
-
-class VideoDevice : public MediaDevice
-{
-public:
-  typedef MediaEngineVideoSource Source;
-
-  explicit VideoDevice(Source* aSource);
-  NS_IMETHOD GetType(nsAString& aType);
-  Source* GetSource();
-  nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
-                    const MediaEnginePrefs &aPrefs);
-};
-
-class AudioDevice : public MediaDevice
-{
-public:
-  typedef MediaEngineAudioSource Source;
-
-  explicit AudioDevice(Source* aSource);
-  NS_IMETHOD GetType(nsAString& aType);
-  Source* GetSource();
-  nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
-                    const MediaEnginePrefs &aPrefs);
-};
-
 // we could add MediaManager if needed
 typedef void (*WindowListenerCallback)(MediaManager *aThis,
                                        uint64_t aWindowID,
                                        StreamListeners *aListeners,
                                        void *aData);
 
 class MediaManager final : public nsIMediaManagerService,
                            public nsIObserver
 {
+  friend GetUserMediaCallbackMediaStreamListener;
 public:
   static already_AddRefed<MediaManager> GetInstance();
 
   // NOTE: never Dispatch(....,NS_DISPATCH_SYNC) to the MediaManager
   // thread from the MainThread, as we NS_DISPATCH_SYNC to MainThread
   // from MediaManager thread.
   static MediaManager* Get();
   static MediaManager* GetIfExists();
@@ -581,17 +458,17 @@ public:
   void OnNavigation(uint64_t aWindowID);
   bool IsActivelyCapturingOrHasAPermission(uint64_t aWindowId);
 
   MediaEnginePrefs mPrefs;
 
   typedef nsTArray<nsRefPtr<MediaDevice>> SourceSet;
   static bool IsPrivateBrowsing(nsPIDOMWindow *window);
 private:
-  typedef media::Pledge<SourceSet*, dom::MediaStreamError> PledgeSourceSet;
+  typedef media::Pledge<SourceSet*, dom::MediaStreamError*> PledgeSourceSet;
 
   static bool IsPrivileged();
   static bool IsLoop(nsIURI* aDocURI);
   static nsresult GenerateUUID(nsAString& aResult);
   static nsresult AnonymizeId(nsAString& aId, const nsACString& aOriginKey);
 public: // TODO: make private once we upgrade to GCC 4.8+ on linux.
   static void AnonymizeDevices(SourceSet& aDevices, const nsACString& aOriginKey);
   static already_AddRefed<nsIWritableVariant> ToJSArray(SourceSet& aDevices);
@@ -641,16 +518,17 @@ private:
 
   Mutex mMutex;
   // protected with mMutex:
   RefPtr<MediaEngine> mBackend;
 
   static StaticRefPtr<MediaManager> sSingleton;
 
   media::CoatCheck<PledgeSourceSet> mOutstandingPledges;
+  media::CoatCheck<GetUserMediaCallbackMediaStreamListener::PledgeVoid> mOutstandingVoidPledges;
 #if defined(MOZ_B2G_CAMERA) && defined(MOZ_WIDGET_GONK)
   nsRefPtr<nsDOMCameraManager> mCameraManager;
 #endif
 public:
   media::CoatCheck<media::Pledge<nsCString>> mGetOriginKeyPledges;
   ScopedDeletePtr<media::Parent<media::NonE10s>> mNonE10sParent;
 };
 
--- a/dom/media/MediaStreamTrack.cpp
+++ b/dom/media/MediaStreamTrack.cpp
@@ -57,10 +57,17 @@ MediaStreamTrack::SetEnabled(bool aEnabl
 }
 
 void
 MediaStreamTrack::Stop()
 {
   mStream->StopTrack(mTrackID);
 }
 
+already_AddRefed<Promise>
+MediaStreamTrack::ApplyConstraints(const MediaTrackConstraints& aConstraints,
+                                   ErrorResult &aRv)
+{
+  return mStream->ApplyConstraintsToTrack(mTrackID, aConstraints, aRv);
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/media/MediaStreamTrack.h
+++ b/dom/media/MediaStreamTrack.h
@@ -4,16 +4,17 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MEDIASTREAMTRACK_H_
 #define MEDIASTREAMTRACK_H_
 
 #include "mozilla/DOMEventTargetHelper.h"
 #include "nsID.h"
 #include "StreamBuffer.h"
+#include "MediaTrackConstraints.h"
 
 namespace mozilla {
 
 class DOMMediaStream;
 
 namespace dom {
 
 class AudioStreamTrack;
@@ -44,16 +45,18 @@ public:
 
   // WebIDL
   virtual void GetKind(nsAString& aKind) = 0;
   void GetId(nsAString& aID) const;
   void GetLabel(nsAString& aLabel) { aLabel.Truncate(); }
   bool Enabled() { return mEnabled; }
   void SetEnabled(bool aEnabled);
   void Stop();
+  already_AddRefed<Promise>
+  ApplyConstraints(const dom::MediaTrackConstraints& aConstraints, ErrorResult &aRv);
 
   // Notifications from the MediaStreamGraph
   void NotifyEnded() { mEnded = true; }
 
   // Webrtc allows the remote side to name tracks whatever it wants, and we
   // need to surface this to content.
   void AssignId(const nsAString& aID) { mID = aID; }
 
--- a/dom/media/systemservices/MediaUtils.h
+++ b/dom/media/systemservices/MediaUtils.h
@@ -58,17 +58,17 @@ class Pledge : public PledgeBase
   public:
     FunctorsBase() {}
     virtual void Succeed(ValueType& result) = 0;
     virtual void Fail(ErrorType& error) = 0;
     virtual ~FunctorsBase() {};
   };
 
 public:
-  explicit Pledge() : mDone(false), mError(nullptr) {}
+  explicit Pledge() : mDone(false), mRejected(false) {}
   Pledge(const Pledge& aOther) = delete;
   Pledge& operator = (const Pledge&) = delete;
 
   template<typename OnSuccessType>
   void Then(OnSuccessType aOnSuccess)
   {
     Then(aOnSuccess, [](ErrorType&){});
   }
@@ -92,150 +92,59 @@ public:
       };
 
       OnSuccessType mOnSuccess;
       OnFailureType mOnFailure;
     };
     mFunctors = new Functors(aOnSuccess, aOnFailure);
 
     if (mDone) {
-      if (!mError) {
-        mFunctors->Succeed(mValue);
-      } else {
-        mFunctors->Fail(*mError);
-      }
-    }
-  }
-
-  void Resolve(const ValueType& aValue)
-  {
-    mValue = aValue;
-    Resolve();
-  }
-protected:
-  void Resolve()
-  {
-    if (!mDone) {
-      mDone = true;
-      MOZ_ASSERT(!mError);
-      if (mFunctors) {
-        mFunctors->Succeed(mValue);
-      }
-    }
-  }
-
-  void Reject(ErrorType rv)
-  {
-    if (!mDone) {
-      mDone = true;
-      mError = rv;
-      if (mFunctors) {
-        mFunctors->Fail(mError);
-      }
-    }
-  }
-
-  ValueType mValue;
-private:
-  ~Pledge() {};
-  bool mDone;
-  nsRefPtr<ErrorType> mError;
-  ScopedDeletePtr<FunctorsBase> mFunctors;
-};
-
-template<typename ValueType>
-class Pledge<ValueType, nsresult>  : public PledgeBase
-{
-  // TODO: Remove workaround once mozilla allows std::function from <functional>
-  // wo/std::function support, do template + virtual trick to accept lambdas
-  class FunctorsBase
-  {
-  public:
-    FunctorsBase() {}
-    virtual void Succeed(ValueType& result) = 0;
-    virtual void Fail(nsresult error) = 0;
-    virtual ~FunctorsBase() {};
-  };
-
-public:
-  explicit Pledge() : mDone(false), mError(NS_OK) {}
-  Pledge(const Pledge& aOther) = delete;
-  Pledge& operator = (const Pledge&) = delete;
-
-  template<typename OnSuccessType>
-  void Then(OnSuccessType aOnSuccess)
-  {
-    Then(aOnSuccess, [](nsresult){});
-  }
-
-  template<typename OnSuccessType, typename OnFailureType>
-  void Then(OnSuccessType aOnSuccess, OnFailureType aOnFailure)
-  {
-    class Functors : public FunctorsBase
-    {
-    public:
-      Functors(OnSuccessType& aOnSuccess, OnFailureType& aOnFailure)
-        : mOnSuccess(aOnSuccess), mOnFailure(aOnFailure) {}
-
-      void Succeed(ValueType& result)
-      {
-        mOnSuccess(result);
-      }
-      void Fail(nsresult rv)
-      {
-        mOnFailure(rv);
-      };
-
-      OnSuccessType mOnSuccess;
-      OnFailureType mOnFailure;
-    };
-    mFunctors = new Functors(aOnSuccess, aOnFailure);
-
-    if (mDone) {
-      if (mError == NS_OK) {
+      if (!mRejected) {
         mFunctors->Succeed(mValue);
       } else {
         mFunctors->Fail(mError);
       }
     }
   }
 
   void Resolve(const ValueType& aValue)
   {
     mValue = aValue;
     Resolve();
   }
+
+  void Reject(ErrorType rv)
+  {
+    if (!mDone) {
+      mDone = mRejected = true;
+      mError = rv;
+      if (mFunctors) {
+        mFunctors->Fail(mError);
+      }
+    }
+  }
+
 protected:
   void Resolve()
   {
     if (!mDone) {
       mDone = true;
-      MOZ_ASSERT(mError == NS_OK);
+      MOZ_ASSERT(!mRejected);
       if (mFunctors) {
         mFunctors->Succeed(mValue);
       }
     }
   }
 
-  void Reject(nsresult error)
-  {
-    if (!mDone) {
-      mDone = true;
-      mError = error;
-      if (mFunctors) {
-        mFunctors->Fail(mError);
-      }
-    }
-  }
-
   ValueType mValue;
 private:
   ~Pledge() {};
   bool mDone;
-  nsresult mError;
+  bool mRejected;
+  ErrorType mError;
   ScopedDeletePtr<FunctorsBase> mFunctors;
 };
 
 /* media::NewRunnableFrom() - Create an nsRunnable from a lambda.
  * media::NewTaskFrom()     - Create a Task from a lambda.
  *
  * Passing variables (closures) to an async function is clunky with nsRunnable:
  *
--- a/dom/media/tests/mochitest/head.js
+++ b/dom/media/tests/mochitest/head.js
@@ -229,17 +229,16 @@ function setupEnvironment() {
     return;
   }
 
   // Running as a Mochitest.
   SimpleTest.requestFlakyTimeout("WebRTC inherently depends on timeouts");
   window.finish = () => SimpleTest.finish();
   SpecialPowers.pushPrefEnv({
     'set': [
-      ['canvas.capturestream.enabled', true],
       ['media.peerconnection.enabled', true],
       ['media.peerconnection.identity.enabled', true],
       ['media.peerconnection.identity.timeout', 120000],
       ['media.peerconnection.ice.stun_client_maximum_transmits', 14],
       ['media.peerconnection.ice.trickle_grace_period', 30000],
       ['media.navigator.permission.disabled', true],
       ['media.navigator.streams.fake', FAKE_ENABLED],
       ['media.getusermedia.screensharing.enabled', true],
--- a/dom/media/tests/mochitest/test_getUserMedia_constraints.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_constraints.html
@@ -112,15 +112,20 @@ runTest(function() {
   return tests.reduce((p, test) =>
     p.then(() => navigator.mediaDevices.getUserMedia(test.constraints))
     .then(() => is(null, test.error, test.message), e => {
       is(e.name, test.error, test.message + ": " + e.message);
       if (test.constraint) {
         is(e.constraint, test.constraint,
            test.message + " w/correct constraint.");
       }
-    }), p);
+    }), p)
+    .then(() => navigator.mediaDevices.getUserMedia({video: true, audio: true}))
+    .then(stream => stream.getVideoTracks()[0].applyConstraints({ width: 320 })
+      .then(() => stream.getAudioTracks()[0].applyConstraints({ })))
+    .then(() => ok(true, "applyConstraints code exercised"))
+    // TODO: Test outcome once fake devices support constraints (Bug 1088621)
 });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/webrtc/MediaEngine.h
+++ b/dom/media/webrtc/MediaEngine.h
@@ -106,16 +106,21 @@ public:
   virtual void NotifyPull(MediaStreamGraph* aGraph,
                           SourceMediaStream *aSource,
                           TrackID aId,
                           StreamTime aDesiredTime) = 0;
 
   /* Stop the device and release the corresponding MediaStream */
   virtual nsresult Stop(SourceMediaStream *aSource, TrackID aID) = 0;
 
+  /* Restart with new capability */
+  virtual nsresult Restart(const dom::MediaTrackConstraints& aConstraints,
+                           const MediaEnginePrefs &aPrefs,
+                           const nsString& aDeviceId) = 0;
+
   /* Change device configuration.  */
   virtual nsresult Config(bool aEchoOn, uint32_t aEcho,
                           bool aAgcOn, uint32_t aAGC,
                           bool aNoiseOn, uint32_t aNoise,
                           int32_t aPlayoutDelay) = 0;
 
   /* Returns true if a source represents a fake capture device and
    * false otherwise
--- a/dom/media/webrtc/MediaEngineDefault.cpp
+++ b/dom/media/webrtc/MediaEngineDefault.cpp
@@ -196,16 +196,24 @@ MediaEngineDefaultVideoSource::Stop(Sour
       aSource->EndTrack(kTrackCount + i);
     }
   }
 
   mState = kStopped;
   return NS_OK;
 }
 
+nsresult
+MediaEngineDefaultVideoSource::Restart(const dom::MediaTrackConstraints& aConstraints,
+                                       const MediaEnginePrefs &aPrefs,
+                                       const nsString& aDeviceId)
+{
+  return NS_OK;
+}
+
 NS_IMETHODIMP
 MediaEngineDefaultVideoSource::Notify(nsITimer* aTimer)
 {
   // Update the target color
   if (mCr <= 16) {
     if (mCb < 240) {
       mCb++;
     } else {
@@ -465,16 +473,24 @@ MediaEngineDefaultAudioSource::Stop(Sour
       aSource->EndTrack(kTrackCount + kFakeVideoTrackCount+i);
     }
   }
 
   mState = kStopped;
   return NS_OK;
 }
 
+nsresult
+MediaEngineDefaultAudioSource::Restart(const dom::MediaTrackConstraints& aConstraints,
+                                       const MediaEnginePrefs &aPrefs,
+                                       const nsString& aDeviceId)
+{
+  return NS_OK;
+}
+
 NS_IMETHODIMP
 MediaEngineDefaultAudioSource::Notify(nsITimer* aTimer)
 {
   AudioSegment segment;
   nsRefPtr<SharedBuffer> buffer = SharedBuffer::Create(AUDIO_FRAME_LENGTH * sizeof(int16_t));
   int16_t* dest = static_cast<int16_t*>(buffer->Data());
 
   mSineGenerator->generate(dest, AUDIO_FRAME_LENGTH);
--- a/dom/media/webrtc/MediaEngineDefault.h
+++ b/dom/media/webrtc/MediaEngineDefault.h
@@ -44,16 +44,19 @@ public:
   virtual void GetUUID(nsACString&) override;
 
   virtual nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
                             const MediaEnginePrefs &aPrefs,
                             const nsString& aDeviceId) override;
   virtual nsresult Deallocate() override;
   virtual nsresult Start(SourceMediaStream*, TrackID) override;
   virtual nsresult Stop(SourceMediaStream*, TrackID) override;
+  virtual nsresult Restart(const dom::MediaTrackConstraints& aConstraints,
+                           const MediaEnginePrefs &aPrefs,
+                           const nsString& aDeviceId) override;
   virtual void SetDirectListeners(bool aHasDirectListeners) override {};
   virtual nsresult Config(bool aEchoOn, uint32_t aEcho,
                           bool aAgcOn, uint32_t aAGC,
                           bool aNoiseOn, uint32_t aNoise,
                           int32_t aPlayoutDelay) override { return NS_OK; };
   virtual void NotifyPull(MediaStreamGraph* aGraph,
                           SourceMediaStream *aSource,
                           TrackID aId,
@@ -114,16 +117,19 @@ public:
   virtual void GetUUID(nsACString&) override;
 
   virtual nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
                             const MediaEnginePrefs &aPrefs,
                             const nsString& aDeviceId) override;
   virtual nsresult Deallocate() override;
   virtual nsresult Start(SourceMediaStream*, TrackID) override;
   virtual nsresult Stop(SourceMediaStream*, TrackID) override;
+  virtual nsresult Restart(const dom::MediaTrackConstraints& aConstraints,
+                           const MediaEnginePrefs &aPrefs,
+                           const nsString& aDeviceId) override;
   virtual void SetDirectListeners(bool aHasDirectListeners) override {};
   virtual nsresult Config(bool aEchoOn, uint32_t aEcho,
                           bool aAgcOn, uint32_t aAGC,
                           bool aNoiseOn, uint32_t aNoise,
                           int32_t aPlayoutDelay) override { return NS_OK; };
   virtual void NotifyPull(MediaStreamGraph* aGraph,
                           SourceMediaStream *aSource,
                           TrackID aId,
--- a/dom/media/webrtc/MediaEngineGonkVideoSource.cpp
+++ b/dom/media/webrtc/MediaEngineGonkVideoSource.cpp
@@ -323,16 +323,24 @@ MediaEngineGonkVideoSource::Stop(SourceM
   }
 
   NS_DispatchToMainThread(WrapRunnable(nsRefPtr<MediaEngineGonkVideoSource>(this),
                                        &MediaEngineGonkVideoSource::StopImpl));
 
   return NS_OK;
 }
 
+nsresult
+MediaEngineGonkVideoSource::Restart(const dom::MediaTrackConstraints& aConstraints,
+                                    const MediaEnginePrefs& aPrefs,
+                                    const nsString& aDeviceId)
+{
+  return NS_OK;
+}
+
 /**
 * Initialization and Shutdown functions for the video source, called by the
 * constructor and destructor respectively.
 */
 
 void
 MediaEngineGonkVideoSource::Init()
 {
--- a/dom/media/webrtc/MediaEngineGonkVideoSource.h
+++ b/dom/media/webrtc/MediaEngineGonkVideoSource.h
@@ -61,16 +61,19 @@ public:
     }
 
   virtual nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
                             const MediaEnginePrefs &aPrefs,
                             const nsString& aDeviceId) override;
   virtual nsresult Deallocate() override;
   virtual nsresult Start(SourceMediaStream* aStream, TrackID aID) override;
   virtual nsresult Stop(SourceMediaStream* aSource, TrackID aID) override;
+  virtual nsresult Restart(const dom::MediaTrackConstraints& aConstraints,
+                           const MediaEnginePrefs &aPrefs,
+                           const nsString& aDeviceId) override;
   virtual void NotifyPull(MediaStreamGraph* aGraph,
                           SourceMediaStream* aSource,
                           TrackID aId,
                           StreamTime aDesiredTime) override;
   virtual const dom::MediaSourceEnum GetMediaSource() override {
     return dom::MediaSourceEnum::Camera;
   }
 
--- a/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp
+++ b/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp
@@ -207,16 +207,41 @@ MediaEngineRemoteVideoSource::Stop(mozil
     mImage = nullptr;
   }
 
   mozilla::camera::StopCapture(mCapEngine, mCaptureIndex);
 
   return NS_OK;
 }
 
+nsresult
+MediaEngineRemoteVideoSource::Restart(const dom::MediaTrackConstraints& aConstraints,
+                                      const MediaEnginePrefs& aPrefs,
+                                      const nsString& aDeviceId)
+{
+  if (!mInitDone) {
+    LOG(("Init not done"));
+    return NS_ERROR_FAILURE;
+  }
+  if (!ChooseCapability(aConstraints, aPrefs, aDeviceId)) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+  if (mState != kStarted) {
+    return NS_OK;
+  }
+
+  mozilla::camera::StopCapture(mCapEngine, mCaptureIndex);
+  if (mozilla::camera::StartCapture(mCapEngine,
+                                    mCaptureIndex, mCapability, this)) {
+    LOG(("StartCapture failed"));
+    return NS_ERROR_FAILURE;
+  }
+  return NS_OK;
+}
+
 void
 MediaEngineRemoteVideoSource::NotifyPull(MediaStreamGraph* aGraph,
                                          SourceMediaStream* aSource,
                                          TrackID aID, StreamTime aDesiredTime)
 {
   VideoSegment segment;
 
   MonitorAutoLock lock(mMonitor);
--- a/dom/media/webrtc/MediaEngineRemoteVideoSource.h
+++ b/dom/media/webrtc/MediaEngineRemoteVideoSource.h
@@ -66,16 +66,19 @@ public:
                                const char* aMonitorName = "RemoteVideo.Monitor");
 
   virtual nsresult Allocate(const dom::MediaTrackConstraints& aConstraints,
                             const MediaEnginePrefs& aPrefs,
                             const nsString& aDeviceId) override;
   virtual nsresult Deallocate() override;;
   virtual nsresult Start(SourceMediaStream*, TrackID) override;
   virtual nsresult Stop(SourceMediaStream*, TrackID) override;
+  virtual nsresult Restart(const dom::MediaTrackConstraints& aConstraints,
+                           const MediaEnginePrefs &aPrefs,
+                           const nsString& aDeviceId) override;
   virtual void NotifyPull(MediaStreamGraph* aGraph,
                           SourceMediaStream* aSource,
                           TrackID aId,
                           StreamTime aDesiredTime) override;
   virtual const dom::MediaSourceEnum GetMediaSource() override {
     return mMediaSource;
   }
 
--- a/dom/media/webrtc/MediaEngineTabVideoSource.cpp
+++ b/dom/media/webrtc/MediaEngineTabVideoSource.cpp
@@ -287,16 +287,25 @@ MediaEngineTabVideoSource::Stop(mozilla:
   if (!mWindow)
     return NS_OK;
 
   NS_DispatchToMainThread(new StopRunnable(this));
   return NS_OK;
 }
 
 nsresult
+MediaEngineTabVideoSource::Restart(const dom::MediaTrackConstraints& aConstraints,
+                                   const mozilla::MediaEnginePrefs& aPrefs,
+                                   const nsString& aDeviceId)
+{
+  // TODO
+  return NS_OK;
+}
+
+nsresult
 MediaEngineTabVideoSource::Config(bool, uint32_t, bool, uint32_t, bool, uint32_t, int32_t)
 {
   return NS_OK;
 }
 
 bool
 MediaEngineTabVideoSource::IsFake()
 {
--- a/dom/media/webrtc/MediaEngineTabVideoSource.h
+++ b/dom/media/webrtc/MediaEngineTabVideoSource.h
@@ -24,16 +24,19 @@ class MediaEngineTabVideoSource : public
     virtual nsresult Allocate(const dom::MediaTrackConstraints &,
                               const mozilla::MediaEnginePrefs&,
                               const nsString& aDeviceId) override;
     virtual nsresult Deallocate() override;
     virtual nsresult Start(mozilla::SourceMediaStream*, mozilla::TrackID) override;
     virtual void SetDirectListeners(bool aHasDirectListeners) override {};
     virtual void NotifyPull(mozilla::MediaStreamGraph*, mozilla::SourceMediaStream*, mozilla::TrackID, mozilla::StreamTime) override;
     virtual nsresult Stop(mozilla::SourceMediaStream*, mozilla::TrackID) override;
+    virtual nsresult Restart(const dom::MediaTrackConstraints& aConstraints,
+                             const mozilla::MediaEnginePrefs& aPrefs,
+                             const nsString& aDeviceId) override;
     virtual nsresult Config(bool, uint32_t, bool, uint32_t, bool, uint32_t, int32_t) override;
     virtual bool IsFake() override;
     virtual const dom::MediaSourceEnum GetMediaSource() override {
       return dom::MediaSourceEnum::Browser;
     }
     virtual uint32_t GetBestFitnessDistance(
       const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets,
       const nsString& aDeviceId) override
--- a/dom/media/webrtc/MediaEngineWebRTC.h
+++ b/dom/media/webrtc/MediaEngineWebRTC.h
@@ -79,16 +79,19 @@ public:
     return NS_OK;
   }
   void Shutdown() override
   {
     // Nothing to do here, everything is managed in MediaManager.cpp
   }
   nsresult Start(SourceMediaStream* aMediaStream, TrackID aId) override;
   nsresult Stop(SourceMediaStream* aMediaStream, TrackID aId) override;
+  nsresult Restart(const dom::MediaTrackConstraints& aConstraints,
+                   const MediaEnginePrefs &aPrefs,
+                   const nsString& aDeviceId) override;
   void SetDirectListeners(bool aDirect) override
   {}
   nsresult Config(bool aEchoOn, uint32_t aEcho, bool aAgcOn,
                   uint32_t aAGC, bool aNoiseOn, uint32_t aNoise,
                   int32_t aPlayoutDelay) override
   {
     return NS_OK;
   }
@@ -150,16 +153,19 @@ public:
   virtual void GetUUID(nsACString& aUUID) override;
 
   virtual nsresult Allocate(const dom::MediaTrackConstraints& aConstraints,
                             const MediaEnginePrefs& aPrefs,
                             const nsString& aDeviceId) override;
   virtual nsresult Deallocate() override;
   virtual nsresult Start(SourceMediaStream* aStream, TrackID aID) override;
   virtual nsresult Stop(SourceMediaStream* aSource, TrackID aID) override;
+  virtual nsresult Restart(const dom::MediaTrackConstraints& aConstraints,
+                           const MediaEnginePrefs &aPrefs,
+                           const nsString& aDeviceId) override;
   virtual void SetDirectListeners(bool aHasDirectListeners) override {};
   virtual nsresult Config(bool aEchoOn, uint32_t aEcho,
                           bool aAgcOn, uint32_t aAGC,
                           bool aNoiseOn, uint32_t aNoise,
                           int32_t aPlayoutDelay) override;
 
   virtual void NotifyPull(MediaStreamGraph* aGraph,
                           SourceMediaStream* aSource,
--- a/dom/media/webrtc/MediaEngineWebRTCAudio.cpp
+++ b/dom/media/webrtc/MediaEngineWebRTCAudio.cpp
@@ -417,16 +417,24 @@ MediaEngineWebRTCMicrophoneSource::Stop(
     return NS_ERROR_FAILURE;
   }
   if (mVoEBase->StopReceive(mChannel)) {
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
+nsresult
+MediaEngineWebRTCMicrophoneSource::Restart(const dom::MediaTrackConstraints& aConstraints,
+                                           const MediaEnginePrefs &aPrefs,
+                                           const nsString& aDeviceId)
+{
+  return NS_OK;
+}
+
 void
 MediaEngineWebRTCMicrophoneSource::NotifyPull(MediaStreamGraph *aGraph,
                                               SourceMediaStream *aSource,
                                               TrackID aID,
                                               StreamTime aDesiredTime)
 {
   // Ignore - we push audio data
   LOG_FRAMES(("NotifyPull, desired = %ld", (int64_t) aDesiredTime));
@@ -659,16 +667,25 @@ MediaEngineWebRTCAudioCaptureSource::Sta
 nsresult
 MediaEngineWebRTCAudioCaptureSource::Stop(SourceMediaStream *aMediaStream,
                                           TrackID aId)
 {
   aMediaStream->EndAllTrackAndFinish();
   return NS_OK;
 }
 
+nsresult
+MediaEngineWebRTCAudioCaptureSource::Restart(
+    const dom::MediaTrackConstraints& aConstraints,
+    const MediaEnginePrefs &aPrefs,
+    const nsString& aDeviceId)
+{
+  return NS_OK;
+}
+
 uint32_t
 MediaEngineWebRTCAudioCaptureSource::GetBestFitnessDistance(
     const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets,
     const nsString& aDeviceId)
 {
   // There is only one way of capturing audio for now, and it's always adequate.
   return 0;
 }
--- a/dom/media/webrtc/MediaTrackConstraints.h
+++ b/dom/media/webrtc/MediaTrackConstraints.h
@@ -7,16 +7,18 @@
 #ifndef MEDIATRACKCONSTRAINTS_H_
 #define MEDIATRACKCONSTRAINTS_H_
 
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/MediaStreamTrackBinding.h"
 #include "mozilla/dom/MediaTrackConstraintSetBinding.h"
 #include "mozilla/dom/MediaTrackSupportedConstraintsBinding.h"
 
+#include <map>
+
 namespace mozilla {
 
 template<class EnumValuesStrings, class Enum>
 static const char* EnumToASCII(const EnumValuesStrings& aStrings, Enum aValue) {
   return aStrings[uint32_t(aValue)].value;
 }
 
 template<class EnumValuesStrings, class Enum>
@@ -99,13 +101,135 @@ protected:
     bool aAdvanced);
   static uint32_t FitnessDistance(nsString aN,
       const dom::ConstrainDOMStringParameters& aParams);
 
   static uint32_t
   GetMinimumFitnessDistance(const dom::MediaTrackConstraintSet &aConstraints,
                             bool aAdvanced,
                             const nsString& aDeviceId);
+
+  template<class DeviceType>
+  static bool
+  AreUnfitSettings(const dom::MediaTrackConstraints &aConstraints,
+                   nsTArray<nsRefPtr<DeviceType>>& aSources)
+  {
+    nsTArray<const dom::MediaTrackConstraintSet*> aggregateConstraints;
+    aggregateConstraints.AppendElement(&aConstraints);
+
+    for (auto& source : aSources) {
+      if (source->GetBestFitnessDistance(aggregateConstraints) != UINT32_MAX) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+public:
+  // Apply constrains to a supplied list of sources (removes items from the list)
+
+  template<class DeviceType>
+  static const char*
+  SelectSettings(const dom::MediaTrackConstraints &aConstraints,
+                 nsTArray<nsRefPtr<DeviceType>>& aSources)
+  {
+    auto& c = aConstraints;
+
+    // First apply top-level constraints.
+
+    // Stack constraintSets that pass, starting with the required one, because the
+    // whole stack must be re-satisfied each time a capability-set is ruled out
+    // (this avoids storing state or pushing algorithm into the lower-level code).
+    nsTArray<nsRefPtr<DeviceType>> unsatisfactory;
+    nsTArray<const dom::MediaTrackConstraintSet*> aggregateConstraints;
+    aggregateConstraints.AppendElement(&c);
+
+    std::multimap<uint32_t, nsRefPtr<DeviceType>> ordered;
+
+    for (uint32_t i = 0; i < aSources.Length();) {
+      uint32_t distance = aSources[i]->GetBestFitnessDistance(aggregateConstraints);
+      if (distance == UINT32_MAX) {
+        unsatisfactory.AppendElement(aSources[i]);
+        aSources.RemoveElementAt(i);
+      } else {
+        ordered.insert(std::pair<uint32_t, nsRefPtr<DeviceType>>(distance,
+                                                                 aSources[i]));
+        ++i;
+      }
+    }
+    if (!aSources.Length()) {
+      // None selected. The spec says to report a constraint that satisfies NONE
+      // of the sources. Unfortunately, this is a bit laborious to find out, and
+      // requires updating as new constraints are added!
+
+      if (c.mDeviceId.IsConstrainDOMStringParameters()) {
+        dom::MediaTrackConstraints fresh;
+        fresh.mDeviceId = c.mDeviceId;
+        if (AreUnfitSettings(fresh, unsatisfactory)) {
+          return "deviceId";
+        }
+      }
+      if (c.mWidth.IsConstrainLongRange()) {
+        dom::MediaTrackConstraints fresh;
+        fresh.mWidth = c.mWidth;
+        if (AreUnfitSettings(fresh, unsatisfactory)) {
+          return "width";
+        }
+      }
+      if (c.mHeight.IsConstrainLongRange()) {
+        dom::MediaTrackConstraints fresh;
+        fresh.mHeight = c.mHeight;
+        if (AreUnfitSettings(fresh, unsatisfactory)) {
+          return "height";
+        }
+      }
+      if (c.mFrameRate.IsConstrainDoubleRange()) {
+        dom::MediaTrackConstraints fresh;
+        fresh.mFrameRate = c.mFrameRate;
+        if (AreUnfitSettings(fresh, unsatisfactory)) {
+          return "frameRate";
+        }
+      }
+      if (c.mFacingMode.IsConstrainDOMStringParameters()) {
+        dom::MediaTrackConstraints fresh;
+        fresh.mFacingMode = c.mFacingMode;
+        if (AreUnfitSettings(fresh, unsatisfactory)) {
+          return "facingMode";
+        }
+      }
+      return "";
+    }
+
+    // Order devices by shortest distance
+    for (auto& ordinal : ordered) {
+      aSources.RemoveElement(ordinal.second);
+      aSources.AppendElement(ordinal.second);
+    }
+
+    // Then apply advanced constraints.
+
+    if (c.mAdvanced.WasPassed()) {
+      auto &array = c.mAdvanced.Value();
+
+      for (int i = 0; i < int(array.Length()); i++) {
+        aggregateConstraints.AppendElement(&array[i]);
+        nsTArray<nsRefPtr<DeviceType>> rejects;
+        for (uint32_t j = 0; j < aSources.Length();) {
+          if (aSources[j]->GetBestFitnessDistance(aggregateConstraints) == UINT32_MAX) {
+            rejects.AppendElement(aSources[j]);
+            aSources.RemoveElementAt(j);
+          } else {
+            ++j;
+          }
+        }
+        if (!aSources.Length()) {
+          aSources.AppendElements(Move(rejects));
+          aggregateConstraints.RemoveElementAt(aggregateConstraints.Length() - 1);
+        }
+      }
+    }
+    return nullptr;
+  }
 };
 
 } // namespace mozilla
 
 #endif /* MEDIATRACKCONSTRAINTS_H_ */
--- a/dom/network/PTCPSocket.ipdl
+++ b/dom/network/PTCPSocket.ipdl
@@ -36,16 +36,22 @@ protocol PTCPSocket
   manager PNecko;
 
 parent:
   // Forward calling to child's open() method to parent, expect TCPOptions
   // is expanded to |useSSL| (from TCPOptions.useSecureTransport) and
   // |binaryType| (from TCPOption.binaryType).
   Open(nsString host, uint16_t port, bool useSSL, bool useArrayBuffers);
 
+  // Ask parent to open a socket and bind the newly-opened socket to a local
+  // address specified in |localAddr| and |localPort|.
+  OpenBind(nsCString host, uint16_t port,
+           nsCString localAddr, uint16_t localPort,
+           bool useSSL, bool aUseArrayBuffers);
+
   // When child's send() is called, this message requrests parent to send
   // data and update it's trackingNumber.
   Data(SendableData data, uint32_t trackingNumber);
 
   // Forward calling to child's upgradeToSecure() method to parent.
   StartTLS();
 
   // Forward calling to child's send() method to parent.
--- a/dom/network/TCPSocket.cpp
+++ b/dom/network/TCPSocket.cpp
@@ -138,16 +138,17 @@ NS_IMPL_RELEASE_INHERITED(TCPSocket, DOM
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TCPSocket)
   NS_INTERFACE_MAP_ENTRY(nsITransportEventSink)
   NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
   NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
   NS_INTERFACE_MAP_ENTRY(nsIInputStreamCallback)
   NS_INTERFACE_MAP_ENTRY(nsIObserver)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
+  NS_INTERFACE_MAP_ENTRY(nsITCPSocketCallback)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 TCPSocket::TCPSocket(nsIGlobalObject* aGlobal, const nsAString& aHost, uint16_t aPort,
                      bool aSsl, bool aUseArrayBuffers)
   : DOMEventTargetHelper(aGlobal)
   , mReadyState(TCPReadyState::Closed)
   , mUseArrayBuffers(aUseArrayBuffers)
   , mHost(aHost)
@@ -162,22 +163,24 @@ TCPSocket::TCPSocket(nsIGlobalObject* aG
   , mWaitingForStartTLS(false)
 #ifdef MOZ_WIDGET_GONK
   , mTxBytes(0)
   , mRxBytes(0)
   , mAppId(nsIScriptSecurityManager::UNKNOWN_APP_ID)
   , mInBrowser(false)
 #endif
 {
-  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal);
-  if (window && window->IsOuterWindow()) {
-    window = window->GetCurrentInnerWindow();
-  }
-  if (window) {
-    mInnerWindowID = window->WindowID();
+  if (aGlobal) {
+    nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal);
+    if (window && window->IsOuterWindow()) {
+      window = window->GetCurrentInnerWindow();
+    }
+    if (window) {
+      mInnerWindowID = window->WindowID();
+    }
   }
 }
 
 TCPSocket::~TCPSocket()
 {
 }
 
 nsresult
@@ -230,53 +233,63 @@ TCPSocket::CreateStream()
                                     BUFFER_SIZE,
                                     false, /* close source */
                                     false); /* close sink */
   NS_ENSURE_SUCCESS(rv, rv);
   return NS_OK;
 }
 
 nsresult
+TCPSocket::InitWithUnconnectedTransport(nsISocketTransport* aTransport)
+{
+  mReadyState = TCPReadyState::Connecting;
+  mTransport = aTransport;
+
+  MOZ_ASSERT(XRE_GetProcessType() != GeckoProcessType_Content);
+
+  nsCOMPtr<nsIThread> mainThread;
+  NS_GetMainThread(getter_AddRefs(mainThread));
+  mTransport->SetEventSink(this, mainThread);
+
+  nsresult rv = CreateStream();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+nsresult
 TCPSocket::Init()
 {
   nsCOMPtr<nsIObserverService> obs = do_GetService("@mozilla.org/observer-service;1");
   if (obs) {
     obs->AddObserver(this, "inner-window-destroyed", true);
   }
 
-  mReadyState = TCPReadyState::Connecting;
-
   if (XRE_GetProcessType() == GeckoProcessType_Content) {
+    mReadyState = TCPReadyState::Connecting;
     mSocketBridgeChild = new TCPSocketChild(mHost, mPort);
     mSocketBridgeChild->SendOpen(this, mSsl, mUseArrayBuffers);
     return NS_OK;
   }
 
   nsCOMPtr<nsISocketTransportService> sts =
     do_GetService("@mozilla.org/network/socket-transport-service;1");
 
   const char* socketTypes[1];
   if (mSsl) {
     socketTypes[0] = "ssl";
   } else {
     socketTypes[0] = "starttls";
   }
+  nsCOMPtr<nsISocketTransport> transport;
   nsresult rv = sts->CreateTransport(socketTypes, 1, NS_ConvertUTF16toUTF8(mHost), mPort,
-                                     nullptr, getter_AddRefs(mTransport));
+                                     nullptr, getter_AddRefs(transport));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsCOMPtr<nsIThread> mainThread;
-  NS_GetMainThread(getter_AddRefs(mainThread));
-
-  mTransport->SetEventSink(this, mainThread);
-
-  rv = CreateStream();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
+  return InitWithUnconnectedTransport(transport);
 }
 
 void
 TCPSocket::InitWithSocketChild(TCPSocketChild* aSocketBridge)
 {
   mSocketBridgeChild = aSocketBridge;
   mReadyState = TCPReadyState::Open;
   mSocketBridgeChild->SetSocket(this);
@@ -443,68 +456,107 @@ TCPSocket::ActivateTLS()
   nsCOMPtr<nsISupports> securityInfo;
   mTransport->GetSecurityInfo(getter_AddRefs(securityInfo));
   nsCOMPtr<nsISSLSocketControl> socketControl = do_QueryInterface(securityInfo);
   if (socketControl) {
     socketControl->StartTLS();
   }
 }
 
-void
+NS_IMETHODIMP
 TCPSocket::FireErrorEvent(const nsAString& aName, const nsAString& aType)
 {
   if (mSocketBridgeParent) {
     mSocketBridgeParent->FireErrorEvent(aName, aType, mReadyState);
-    return;
+    return NS_OK;
   }
 
   TCPSocketErrorEventInit init;
   init.mBubbles = false;
   init.mCancelable = false;
   init.mName = aName;
   init.mMessage = aType;
 
   nsRefPtr<TCPSocketErrorEvent> event =
     TCPSocketErrorEvent::Constructor(this, NS_LITERAL_STRING("error"), init);
+  MOZ_ASSERT(event);
   event->SetTrusted(true);
   bool dummy;
   DispatchEvent(event, &dummy);
+  return NS_OK;
 }
 
-void
+NS_IMETHODIMP
 TCPSocket::FireEvent(const nsAString& aType)
 {
   if (mSocketBridgeParent) {
     mSocketBridgeParent->FireEvent(aType, mReadyState);
-    return;
+    return NS_OK;
   }
 
   AutoJSAPI api;
   if (NS_WARN_IF(!api.Init(GetOwner()))) {
-    return;
+    return NS_ERROR_FAILURE;
   }
   JS::Rooted<JS::Value> val(api.cx());
-  FireDataEvent(api.cx(), aType, val);
+  return FireDataEvent(api.cx(), aType, val);
 }
 
-void
+NS_IMETHODIMP
+TCPSocket::FireDataArrayEvent(const nsAString& aType,
+                              const InfallibleTArray<uint8_t>& buffer)
+{
+  AutoJSAPI api;
+  if (NS_WARN_IF(!api.Init(GetOwner()))) {
+    return NS_ERROR_FAILURE;
+  }
+  JSContext* cx = api.cx();
+  JS::Rooted<JS::Value> val(cx);
+
+  bool ok = IPC::DeserializeArrayBuffer(cx, buffer, &val);
+  if (ok) {
+    return FireDataEvent(cx, aType, val);
+  }
+  return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+TCPSocket::FireDataStringEvent(const nsAString& aType,
+                               const nsACString& aString)
+{
+  AutoJSAPI api;
+  if (NS_WARN_IF(!api.Init(GetOwner()))) {
+    return NS_ERROR_FAILURE;
+  }
+  JSContext* cx = api.cx();
+  JS::Rooted<JS::Value> val(cx);
+
+  bool ok = ToJSValue(cx, NS_ConvertASCIItoUTF16(aString), &val);
+  if (ok) {
+    return FireDataEvent(cx, aType, val);
+  }
+  return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
 TCPSocket::FireDataEvent(JSContext* aCx, const nsAString& aType, JS::Handle<JS::Value> aData)
 {
   MOZ_ASSERT(!mSocketBridgeParent);
 
   RootedDictionary<TCPSocketEventInit> init(aCx);
   init.mBubbles = false;
   init.mCancelable = false;
   init.mData = aData;
 
   nsRefPtr<TCPSocketEvent> event =
     TCPSocketEvent::Constructor(this, aType, init);
   event->SetTrusted(true);
   bool dummy;
   DispatchEvent(event, &dummy);
+  return NS_OK;
 }
 
 JSObject*
 TCPSocket::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return TCPSocketBinding::Wrap(aCx, this, aGivenProto);
 }
 
@@ -682,21 +734,20 @@ TCPSocket::MaybeReportErrorAndCloseIfOpe
           errName.AssignLiteral("NetworkInterruptError");
           break;
         default:
           errName.AssignLiteral("NetworkError");
           break;
       }
     }
 
-    FireErrorEvent(errName, errorType);
+    NS_WARN_IF(NS_FAILED(FireErrorEvent(errName, errorType)));
   }
 
-  FireEvent(NS_LITERAL_STRING("close"));
-  return NS_OK;
+  return FireEvent(NS_LITERAL_STRING("close"));
 }
 
 void
 TCPSocket::Close()
 {
   if (mReadyState == TCPReadyState::Closed || mReadyState == TCPReadyState::Closing) {
     return;
   }
@@ -1054,36 +1105,38 @@ void
 TCPSocket::SetAppIdAndBrowser(uint32_t aAppId, bool aInBrowser)
 {
 #ifdef MOZ_WIDGET_GONK
   mAppId = aAppId;
   mInBrowser = aInBrowser;
 #endif
 }
 
-void
+NS_IMETHODIMP
 TCPSocket::UpdateReadyState(uint32_t aReadyState)
 {
   MOZ_ASSERT(mSocketBridgeChild);
   mReadyState = static_cast<TCPReadyState>(aReadyState);
+  return NS_OK;
 }
 
-void
+NS_IMETHODIMP
 TCPSocket::UpdateBufferedAmount(uint32_t aBufferedAmount, uint32_t aTrackingNumber)
 {
   if (aTrackingNumber != mTrackingNumber) {
-    return;
+    return NS_OK;
   }
   mBufferedAmount = aBufferedAmount;
   if (!mBufferedAmount) {
     if (mWaitingForDrain) {
       mWaitingForDrain = false;
-      FireEvent(NS_LITERAL_STRING("drain"));
+      return FireEvent(NS_LITERAL_STRING("drain"));
     }
   }
+  return NS_OK;
 }
 
 #ifdef MOZ_WIDGET_GONK
 void
 TCPSocket::SaveNetworkStats(bool aEnforce)
 {
   if (!mTxBytes && !mRxBytes) {
     // There is no traffic at all. No need to save statistics.
--- a/dom/network/TCPSocket.h
+++ b/dom/network/TCPSocket.h
@@ -9,16 +9,17 @@
 #include "mozilla/dom/TCPSocketBinding.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "nsITransport.h"
 #include "nsIStreamListener.h"
 #include "nsIAsyncInputStream.h"
 #include "nsISupportsImpl.h"
 #include "nsIObserver.h"
 #include "nsWeakReference.h"
+#include "nsITCPSocketCallback.h"
 #include "js/RootingAPI.h"
 
 class nsISocketTransport;
 class nsIInputStreamPump;
 class nsIScriptableInputStream;
 class nsIBinaryInputStream;
 class nsIMultiplexInputStream;
 class nsIAsyncStreamCopier;
@@ -67,28 +68,30 @@ private:
 };
 
 class TCPSocket final : public DOMEventTargetHelper
                       , public nsIStreamListener
                       , public nsITransportEventSink
                       , public nsIInputStreamCallback
                       , public nsIObserver
                       , public nsSupportsWeakReference
+                      , public nsITCPSocketCallback
 {
 public:
   TCPSocket(nsIGlobalObject* aGlobal, const nsAString& aHost, uint16_t aPort,
             bool aSsl, bool aUseArrayBuffers);
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(TCPSocket, DOMEventTargetHelper)
   NS_DECL_NSIREQUESTOBSERVER
   NS_DECL_NSISTREAMLISTENER
   NS_DECL_NSITRANSPORTEVENTSINK
   NS_DECL_NSIINPUTSTREAMCALLBACK
   NS_DECL_NSIOBSERVER
+  NS_DECL_NSITCPSOCKETCALLBACK
 
   nsPIDOMWindow* GetParentObject() const
   {
     return GetOwner();
   }
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
@@ -149,27 +152,19 @@ public:
   IMPL_EVENT_HANDLER(error);
   IMPL_EVENT_HANDLER(close);
 
   nsresult Init();
 
   // Inform this socket that a buffered send() has completed sending.
   void NotifyCopyComplete(nsresult aStatus);
 
-  // Set this child socket's number of buffered bytes, based on the count from the parent
-  // process associated with the given sequence id.
-  void UpdateBufferedAmount(uint32_t aAmount, uint32_t aTrackingNumber);
-  // Set this child socket's ready state, based on the state in the parent process.
-  void UpdateReadyState(uint32_t aReadyState);
-  // Dispatch an "error" event at this object.
-  void FireErrorEvent(const nsAString& aName, const nsAString& aMessage);
-  // Dispatch an event of the given type at this object.
-  void FireEvent(const nsAString& aType);
-  // Dispatch a "data" event at this object.
-  void FireDataEvent(JSContext* aCx, const nsAString& aType, JS::Handle<JS::Value> aData);
+  // Initialize this socket from a low-level connection that hasn't connected yet
+  // (called from RecvOpenBind() in TCPSocketParent).
+  nsresult InitWithUnconnectedTransport(nsISocketTransport* aTransport);
 
 private:
   ~TCPSocket();
 
   // Initialize this socket with an existing IPC actor.
   void InitWithSocketChild(TCPSocketChild* aBridge);
   // Initialize this socket from an existing low-level connection.
   nsresult InitWithTransport(nsISocketTransport* aTransport);
--- a/dom/network/TCPSocketChild.cpp
+++ b/dom/network/TCPSocketChild.cpp
@@ -6,16 +6,17 @@
 
 #include <algorithm>
 #include "TCPSocketChild.h"
 #include "mozilla/unused.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/net/NeckoChild.h"
 #include "mozilla/dom/PBrowserChild.h"
 #include "mozilla/dom/TabChild.h"
+#include "nsITCPSocketCallback.h"
 #include "TCPSocket.h"
 #include "nsContentUtils.h"
 #include "jsapi.h"
 #include "jsfriendapi.h"
 
 using mozilla::net::gNeckoChild;
 
 namespace IPC {
@@ -88,26 +89,42 @@ NS_IMETHODIMP_(MozExternalRefCountType) 
 
 TCPSocketChild::TCPSocketChild(const nsAString& aHost, const uint16_t& aPort)
 : mHost(aHost)
 , mPort(aPort)
 {
 }
 
 void
-TCPSocketChild::SendOpen(TCPSocket* aSocket, bool aUseSSL, bool aUseArrayBuffers)
+TCPSocketChild::SendOpen(nsITCPSocketCallback* aSocket, bool aUseSSL, bool aUseArrayBuffers)
 {
   mSocket = aSocket;
 
   AddIPDLReference();
   gNeckoChild->SendPTCPSocketConstructor(this, mHost, mPort);
   PTCPSocketChild::SendOpen(mHost, mPort, aUseSSL, aUseArrayBuffers);
 }
 
 void
+TCPSocketChild::SendWindowlessOpenBind(nsITCPSocketCallback* aSocket,
+                                       const nsACString& aRemoteHost, uint16_t aRemotePort,
+                                       const nsACString& aLocalHost, uint16_t aLocalPort,
+                                       bool aUseSSL)
+{
+  mSocket = aSocket;
+  AddIPDLReference();
+  gNeckoChild->SendPTCPSocketConstructor(this,
+                                         NS_ConvertUTF8toUTF16(aRemoteHost),
+                                         aRemotePort);
+  PTCPSocketChild::SendOpenBind(nsCString(aRemoteHost), aRemotePort,
+                                nsCString(aLocalHost), aLocalPort,
+                                aUseSSL, true);
+}
+
+void
 TCPSocketChildBase::ReleaseIPDLReference()
 {
   MOZ_ASSERT(mIPCOpen);
   mIPCOpen = false;
   this->Release();
 }
 
 void
@@ -142,36 +159,23 @@ TCPSocketChild::RecvCallback(const nsStr
 
   } else if (aData.type() == CallbackData::TTCPError) {
     const TCPError& err(aData.get_TCPError());
     mSocket->FireErrorEvent(err.name(), err.message());
 
   } else if (aData.type() == CallbackData::TSendableData) {
     const SendableData& data = aData.get_SendableData();
 
-    AutoJSAPI api;
-    if (NS_WARN_IF(!api.Init(mSocket->GetOwner()))) {
-      return true;
-    }
-    JSContext* cx = api.cx();
-    JS::Rooted<JS::Value> val(cx);
-
     if (data.type() == SendableData::TArrayOfuint8_t) {
-      bool ok = IPC::DeserializeArrayBuffer(cx, data.get_ArrayOfuint8_t(), &val);
-      NS_ENSURE_TRUE(ok, true);
-
+      mSocket->FireDataArrayEvent(aType, data.get_ArrayOfuint8_t());
     } else if (data.type() == SendableData::TnsCString) {
-      bool ok = ToJSValue(cx, NS_ConvertASCIItoUTF16(data.get_nsCString()), &val);
-      NS_ENSURE_TRUE(ok, true);
-
+      mSocket->FireDataStringEvent(aType, data.get_nsCString());
     } else {
       MOZ_CRASH("Invalid callback data type!");
     }
-    mSocket->FireDataEvent(cx, aType, val);
-
   } else {
     MOZ_CRASH("Invalid callback type!");
   }
   return true;
 }
 
 void
 TCPSocketChild::SendSend(const nsACString& aData, uint32_t aTrackingNumber)
@@ -194,16 +198,23 @@ TCPSocketChild::SendSend(const ArrayBuff
   }
 
   InfallibleTArray<uint8_t> arr;
   arr.SwapElements(fallibleArr);
   SendData(arr, aTrackingNumber);
   return NS_OK;
 }
 
+NS_IMETHODIMP
+TCPSocketChild::SendSendArray(nsTArray<uint8_t>& aArray, uint32_t aTrackingNumber)
+{
+  SendData(aArray, aTrackingNumber);
+  return NS_OK;
+}
+
 void
 TCPSocketChild::SetSocket(TCPSocket* aSocket)
 {
   mSocket = aSocket;
 }
 
 void
 TCPSocketChild::GetHost(nsAString& aHost)
--- a/dom/network/TCPSocketChild.h
+++ b/dom/network/TCPSocketChild.h
@@ -7,16 +7,25 @@
 #ifndef mozilla_dom_TCPSocketChild_h
 #define mozilla_dom_TCPSocketChild_h
 
 #include "mozilla/net/PTCPSocketChild.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsCOMPtr.h"
 #include "js/TypeDecls.h"
 
+class nsITCPSocketCallback;
+
+namespace IPC {
+bool
+DeserializeArrayBuffer(JSContext* cx,
+                       const InfallibleTArray<uint8_t>& aBuffer,
+                       JS::MutableHandle<JS::Value> aVal);
+}
+
 namespace mozilla {
 namespace dom {
 
 class TCPSocket;
 
 class TCPSocketChildBase : public nsISupports {
 public:
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(TCPSocketChildBase)
@@ -24,35 +33,43 @@ public:
 
   void AddIPDLReference();
   void ReleaseIPDLReference();
 
 protected:
   TCPSocketChildBase();
   virtual ~TCPSocketChildBase();
 
-  nsRefPtr<TCPSocket> mSocket;
+  nsCOMPtr<nsITCPSocketCallback> mSocket;
   bool mIPCOpen;
 };
 
 class TCPSocketChild : public mozilla::net::PTCPSocketChild
                      , public TCPSocketChildBase
 {
 public:
   NS_IMETHOD_(MozExternalRefCountType) Release() override;
 
   TCPSocketChild(const nsAString& aHost, const uint16_t& aPort);
   ~TCPSocketChild();
 
-  void SendOpen(TCPSocket* aSocket, bool aUseSSL, bool aUseArrayBuffers);
+  void SendOpen(nsITCPSocketCallback* aSocket, bool aUseSSL, bool aUseArrayBuffers);
+  void SendWindowlessOpenBind(nsITCPSocketCallback* aSocket,
+                              const nsACString& aRemoteHost, uint16_t aRemotePort,
+                              const nsACString& aLocalHost, uint16_t aLocalPort,
+                              bool aUseSSL);
+  NS_IMETHOD SendSendArray(nsTArray<uint8_t>& aArray,
+                           uint32_t aTrackingNumber);
   void SendSend(const nsACString& aData, uint32_t aTrackingNumber);
   nsresult SendSend(const ArrayBuffer& aData,
                     uint32_t aByteOffset,
                     uint32_t aByteLength,
                     uint32_t aTrackingNumber);
+  void SendSendArray(nsTArray<uint8_t>* arr,
+                     uint32_t trackingNumber);
   void SetSocket(TCPSocket* aSocket);
 
   void GetHost(nsAString& aHost);
   void GetPort(uint16_t* aPort);
 
   virtual bool RecvCallback(const nsString& aType,
                             const CallbackData& aData,
                             const uint32_t& aReadyState) override;
--- a/dom/network/TCPSocketParent.cpp
+++ b/dom/network/TCPSocketParent.cpp
@@ -11,16 +11,18 @@
 #include "mozilla/unused.h"
 #include "mozilla/AppProcessChecker.h"
 #include "mozilla/net/NeckoCommon.h"
 #include "mozilla/net/PNeckoParent.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/TabParent.h"
 #include "mozilla/HoldDropJSObjects.h"
+#include "nsISocketTransportService.h"
+#include "nsISocketTransport.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsNetUtil.h"
 
 namespace IPC {
 
 //Defined in TCPSocketChild.cpp
 extern bool
 DeserializeArrayBuffer(JSContext* aCx,
@@ -166,16 +168,84 @@ TCPSocketParent::RecvOpen(const nsString
   mSocket = new TCPSocket(nullptr, aHost, aPort, aUseSSL, aUseArrayBuffers);
   mSocket->SetAppIdAndBrowser(appId, inBrowser);
   mSocket->SetSocketBridgeParent(this);
   NS_ENSURE_SUCCESS(mSocket->Init(), true);
   return true;
 }
 
 bool
+TCPSocketParent::RecvOpenBind(const nsCString& aRemoteHost,
+                              const uint16_t& aRemotePort,
+                              const nsCString& aLocalAddr,
+                              const uint16_t& aLocalPort,
+                              const bool&     aUseSSL,
+                              const bool&     aUseArrayBuffers)
+{
+  if (net::UsingNeckoIPCSecurity() &&
+      !AssertAppProcessPermission(Manager()->Manager(), "tcp-socket")) {
+    FireInteralError(this, __LINE__);
+    return true;
+  }
+
+  nsresult rv;
+  nsCOMPtr<nsISocketTransportService> sts =
+    do_GetService("@mozilla.org/network/socket-transport-service;1", &rv);
+  if (NS_FAILED(rv)) {
+    FireInteralError(this, __LINE__);
+    return true;
+  }
+
+  nsCOMPtr<nsISocketTransport> socketTransport;
+  rv = sts->CreateTransport(nullptr, 0,
+                            aRemoteHost, aRemotePort,
+                            nullptr, getter_AddRefs(socketTransport));
+  if (NS_FAILED(rv)) {
+    FireInteralError(this, __LINE__);
+    return true;
+  }
+
+  PRNetAddr prAddr;
+  if (PR_SUCCESS != PR_InitializeNetAddr(PR_IpAddrAny, aLocalPort, &prAddr)) {
+    FireInteralError(this, __LINE__);
+    return true;
+  }
+  if (PR_SUCCESS != PR_StringToNetAddr(aLocalAddr.BeginReading(), &prAddr)) {
+    FireInteralError(this, __LINE__);
+    return true;
+  }
+
+  mozilla::net::NetAddr addr;
+  PRNetAddrToNetAddr(&prAddr, &addr);
+  rv = socketTransport->Bind(&addr);
+  if (NS_FAILED(rv)) {
+    FireInteralError(this, __LINE__);
+    return true;
+  }
+
+  // Obtain App ID
+  uint32_t appId = nsIScriptSecurityManager::NO_APP_ID;
+  bool     inBrowser = false;
+  const PContentParent *content = Manager()->Manager();
+  const InfallibleTArray<PBrowserParent*>& browsers = content->ManagedPBrowserParent();
+  if (browsers.Length() > 0) {
+    TabParent *tab = static_cast<TabParent*>(browsers[0]);
+    appId = tab->OwnAppId();
+    inBrowser = tab->IsBrowserElement();
+  }
+
+  mSocket = new TCPSocket(nullptr, NS_ConvertUTF8toUTF16(aRemoteHost), aRemotePort, aUseSSL, aUseArrayBuffers);
+  mSocket->SetAppIdAndBrowser(appId, inBrowser);
+  mSocket->SetSocketBridgeParent(this);
+  rv = mSocket->InitWithUnconnectedTransport(socketTransport);
+  NS_ENSURE_SUCCESS(rv, true);
+  return true;
+}
+
+bool
 TCPSocketParent::RecvStartTLS()
 {
   NS_ENSURE_TRUE(mSocket, true);
   ErrorResult rv;
   mSocket->UpgradeToSecure(rv);
   NS_ENSURE_FALSE(rv.Failed(), true);
   return true;
 }
--- a/dom/network/TCPSocketParent.h
+++ b/dom/network/TCPSocketParent.h
@@ -47,16 +47,23 @@ class TCPSocketParent : public mozilla::
 public:
   NS_IMETHOD_(MozExternalRefCountType) Release() override;
 
   TCPSocketParent() {}
 
   virtual bool RecvOpen(const nsString& aHost, const uint16_t& aPort,
                         const bool& useSSL, const bool& aUseArrayBuffers) override;
 
+  virtual bool RecvOpenBind(const nsCString& aRemoteHost,
+                            const uint16_t& aRemotePort,
+                            const nsCString& aLocalAddr,
+                            const uint16_t& aLocalPort,
+                            const bool&     aUseSSL,
+                            const bool& aUseArrayBuffers) override;
+
   virtual bool RecvStartTLS() override;
   virtual bool RecvSuspend() override;
   virtual bool RecvResume() override;
   virtual bool RecvClose() override;
   virtual bool RecvData(const SendableData& aData,
                         const uint32_t& aTrackingNumber) override;
   virtual bool RecvRequestDelete() override;
   virtual nsresult OfflineNotification(nsISupports *) override;
--- a/dom/network/interfaces/moz.build
+++ b/dom/network/interfaces/moz.build
@@ -1,16 +1,17 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 XPIDL_SOURCES += [
     'nsIMozNavigatorNetwork.idl',
+    'nsITCPSocketCallback.idl',
     'nsIUDPSocketChild.idl',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
     XPIDL_SOURCES += [
         'nsIDOMNetworkStatsManager.idl',
         'nsINetworkStatsServiceProxy.idl',
     ]
new file mode 100644
--- /dev/null
+++ b/dom/network/interfaces/nsITCPSocketCallback.idl
@@ -0,0 +1,62 @@
+/* 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/. */
+
+/**
+ * MozTCPSocket exposes a TCP client and server sockets
+ * to highly privileged apps. It provides a buffered, non-blocking
+ * interface for sending. For receiving, it uses an asynchronous,
+ * event handler based interface.
+ */
+
+#include "domstubs.idl"
+
+%{C++
+template<class T> class InfallibleTArray;
+%}
+[ref] native nsUint8TArrayRef(InfallibleTArray<uint8_t>);
+[ptr] native JSContextPtr(JSContext);
+
+
+/*
+ * This interface is implemented in TCPSocket.cpp as an internal interface
+ * for use in cross-process socket implementation.
+ * Needed to account for multiple possible types that can be provided to
+ * the socket callbacks as arguments.
+ */
+[scriptable, uuid(ac2c4b69-cb79-4767-b1ce-bcf62945cd39)]
+interface nsITCPSocketCallback : nsISupports {
+  // Limitation of TCPSocket's buffer size.
+  const unsigned long BUFFER_SIZE = 65536;
+
+  // Dispatch an "error" event at this object with the given name and type.
+  void fireErrorEvent(in AString name, in AString type);
+
+  // Dispatch a "data" event at this object with a string
+  void fireDataStringEvent(in DOMString type, in ACString data);
+
+  // Dispatch a "data" event at this object with an Array
+  void fireDataArrayEvent(in DOMString type, [const] in nsUint8TArrayRef data);
+
+  // Dispatch a "data" event at this object with an ArrayBuffer argument
+  void fireDataEvent(in JSContextPtr cx, in DOMString type, in jsval data);
+
+  // Dispatch an event of the given type at this object.
+  void fireEvent(in DOMString type);
+
+  // Update the DOM object's readyState.
+  // @param readyState
+  //        new ready state
+  void updateReadyState(in unsigned long readystate);
+
+  // Update the DOM object's bufferedAmount value with a tracking number to
+  // to allow tracking of which writes are "in-flight"
+  // @param bufferedAmount
+  //        TCPSocket parent's bufferedAmount.
+  // @param trackingNumber
+  //        A number to ensure the bufferedAmount is updated after data
+  //        from child are sent to parent.
+  void updateBufferedAmount(in uint32_t bufferedAmount,
+                            in uint32_t trackingNumber);
+};
+
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -265,17 +265,17 @@ var interfaceNamesInGlobalScope =
     {name: "CameraRecorderProfile", b2g: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "CameraRecorderProfiles", b2g: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "CameraRecorderVideoProfile", b2g: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "CameraStateChangeEvent", b2g: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "CanvasCaptureMediaStream", disabled: true},
+    {name: "CanvasCaptureMediaStream"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "CanvasGradient",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "CanvasPattern",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "CanvasRenderingContext2D",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "CaretPosition",
--- a/dom/webidl/MediaStreamTrack.webidl
+++ b/dom/webidl/MediaStreamTrack.webidl
@@ -50,29 +50,31 @@ dictionary MediaTrackConstraintSet {
     boolean scrollWithPage;
     ConstrainDOMString deviceId;
 };
 
 dictionary MediaTrackConstraints : MediaTrackConstraintSet {
     sequence<MediaTrackConstraintSet> advanced;
 };
 
-interface MediaStreamTrack {
+[Exposed=Window]
+interface MediaStreamTrack : EventTarget {
     readonly    attribute DOMString             kind;
     readonly    attribute DOMString             id;
     readonly    attribute DOMString             label;
                 attribute boolean               enabled;
-//    readonly    attribute MediaStreamTrackState readyState;
-//    readonly    attribute SourceTypeEnum        sourceType;
-//    readonly    attribute DOMString             sourceId;
-//                attribute EventHandler          onstarted;
-//                attribute EventHandler          onmute;
-//                attribute EventHandler          onunmute;
+//  readonly    attribute boolean               muted;
+//              attribute EventHandler          onmute;
+//              attribute EventHandler          onunmute;
+//  readonly    attribute boolean               _readonly;
+//  readonly    attribute boolean               remote;
+//  readonly    attribute MediaStreamTrackState readyState;
 //                attribute EventHandler          onended;
-//    any                    getConstraint (DOMString constraintName, optional boolean mandatory = false);
-//    void                   setConstraint (DOMString constraintName, any constraintValue, optional boolean mandatory = false);
-//    MediaTrackConstraints? constraints ();
-//    void                   applyConstraints (MediaTrackConstraints constraints);
-//    void                   prependConstraint (DOMString constraintName, any constraintValue);
-//    void                   appendConstraint (DOMString constraintName, any constraintValue);
-//                attribute EventHandler          onoverconstrained;
+//  MediaStreamTrack       clone ();
     void                   stop ();
+//  MediaTrackCapabilities getCapabilities ();
+//  MediaTrackConstraints  getConstraints ();
+//  MediaTrackSettings     getSettings ();
+
+    [Throws]
+    Promise<void>          applyConstraints (optional MediaTrackConstraints constraints);
+//              attribute EventHandler          onoverconstrained;
 };
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -2787,16 +2787,33 @@ GetLcovInfo(JSContext* cx, unsigned argc
 
     if (!str)
         return false;
 
     args.rval().setString(str);
     return true;
 }
 
+#ifdef DEBUG
+static bool
+SetRNGState(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    if (!args.requireAtLeast(cx, "SetRNGState", 1))
+        return false;
+
+    double seed;
+    if (!ToNumber(cx, args[0], &seed))
+        return false;
+
+    cx->compartment()->rngState = static_cast<uint64_t>(seed) & RNG_MASK;
+    return true;
+}
+#endif
+
 static const JSFunctionSpecWithHelp TestingFunctions[] = {
     JS_FN_HELP("gc", ::GC, 0, 0,
 "gc([obj] | 'compartment' [, 'shrinking'])",
 "  Run the garbage collector. When obj is given, GC only its compartment.\n"
 "  If 'compartment' is given, GC any compartments that were scheduled for\n"
 "  GC via schedulegc.\n"
 "  If 'shrinking' is passed as the optional second argument, perform a\n"
 "  shrinking GC rather than a normal GC."),
@@ -3236,16 +3253,22 @@ gc::ZealModeHelpText),
 "  On non-ARM, no-op. On ARM, set the hardware capabilities. The list of \n"
 "  flags is available by calling this function with \"help\" as the flag's name"),
 
     JS_FN_HELP("getLcovInfo", GetLcovInfo, 1, 0,
 "getLcovInfo(global)",
 "  Generate LCOV tracefile for the given compartment.  If no global are provided then\n"
 "  the current global is used as the default one.\n"),
 
+#ifdef DEBUG
+    JS_FN_HELP("setRNGState", SetRNGState, 1, 0,
+"setRNGState(seed)",
+"  Set this compartment's RNG state.\n"),
+#endif
+
     JS_FS_HELP_END
 };
 
 static const JSPropertySpec TestingProperties[] = {
     JS_PSG("timesAccessed", TimesAccessed, 0),
     JS_PS_END
 };
 
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/math-random.js
@@ -0,0 +1,44 @@
+function test() {
+  // With this seed, state won't be reset in 10000 iteration.  The result is
+  // deterministic and it can be used to check the correctness of
+  // implementation.
+  setRNGState(0x12341234);
+
+  function f() {
+    let x = [];
+    for (let i = 0; i < 10000; i++)  {
+      x.push(Math.random());
+    }
+    return x;
+  }
+  let x = f();
+  assertEq(x[0], 0.3562073961260165);
+  assertEq(x[10], 0.9777930699941514);
+  assertEq(x[100], 0.9146259915430884);
+  assertEq(x[1000], 0.315983055288946);
+  assertEq(x[2000], 0.7132284805929497);
+  assertEq(x[3000], 0.9621073641614717);
+  assertEq(x[4000], 0.3928228025111996);
+  assertEq(x[5000], 0.555710685962832);
+  assertEq(x[6000], 0.5207553912782503);
+  assertEq(x[7000], 0.08268413491723015);
+  assertEq(x[8000], 0.031796243723989925);
+  assertEq(x[9000], 0.900683320457098);
+  assertEq(x[9999], 0.7750389203054577);
+
+  // With this seed, state will be reset before calculating high bits.
+  // The result is nondeterministic, but it should be in [0, 1) range.
+  setRNGState(0);
+  x = f();
+  assertEq(x[0] >= 0 && x[0] < 1, true);
+
+  // With this seed, high bits will be 0 and state will be reset before
+  // calculating low bits.  The result is also nondeterministic, but it should
+  // be in [0, 1 / (1 << 26)) range.
+  setRNGState(0x615c0e462aa9);
+  x = f();
+  assertEq(x[0] >= 0 && x[0] < 1 / (1 << 26), true);
+}
+
+if (typeof setRNGState == "function")
+  test();
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -10299,10 +10299,133 @@ CodeGenerator::visitNewTarget(LNewTarget
     masm.bind(&actualArgsSufficient);
 
     BaseValueIndex newTarget(masm.getStackPointer(), argvLen, frameSize() + JitFrameLayout::offsetOfActualArgs());
     masm.loadValue(newTarget, output);
 
     masm.bind(&done);
 }
 
+// Out-of-line math_random_no_outparam call for LRandom.
+class OutOfLineRandom : public OutOfLineCodeBase<CodeGenerator>
+{
+    LRandom* lir_;
+
+  public:
+    explicit OutOfLineRandom(LRandom* lir)
+      : lir_(lir)
+    { }
+
+    void accept(CodeGenerator* codegen) {
+        codegen->visitOutOfLineRandom(this);
+    }
+
+    LRandom* lir() const {
+        return lir_;
+    }
+};
+
+static const uint64_t RNG_HIGH_MASK = (0xFFFFFFFFFFFFFFFFULL >>
+                                       (RNG_STATE_WIDTH - RNG_HIGH_BITS)) << RNG_LOW_BITS;
+static const double RNG_DSCALE_INV = 1 / RNG_DSCALE;
+
+void
+CodeGenerator::visitRandom(LRandom* ins)
+{
+    FloatRegister output = ToFloatRegister(ins->output());
+    Register JSCompartmentReg = ToRegister(ins->temp1());
+#ifdef JS_PUNBOX64
+    Register64 rngStateReg = Register64(ToRegister(ins->tempMaybeEAX()));
+    Register64 highReg = Register64(ToRegister(ins->tempMaybeEDX()));
+#else
+    Register64 rngStateReg = Register64(ToRegister(ins->temp2()), ToRegister(ins->temp3()));
+    Register64 highReg = Register64(ToRegister(ins->tempMaybeEAX()), ToRegister(ins->tempMaybeEDX()));
+#endif
+    // tempReg is used only on x86.
+    Register tempReg = ToRegister(ins->tempMaybeEAX());
+
+    // rngState = cx->compartment()->rngState;
+    masm.loadJSContext(JSCompartmentReg);
+    masm.loadPtr(Address(JSCompartmentReg, JSContext::offsetOfCompartment()), JSCompartmentReg);
+    masm.load64(Address(JSCompartmentReg, JSCompartment::offsetOfRngState()), rngStateReg);
+
+    // if rngState == 0, escape from inlined code and call
+    // math_random_no_outparam.
+    OutOfLineRandom* ool = new(alloc()) OutOfLineRandom(ins);
+    addOutOfLineCode(ool, ins->mir());
+    masm.branchTest64(Assembler::Zero, rngStateReg, rngStateReg, tempReg, ool->entry());
+
+    // rngState = rngState * RNG_MULTIPLIER;
+    masm.mul64(Imm64(RNG_MULTIPLIER), rngStateReg);
+
+    // rngState += RNG_ADDEND;
+    masm.add64(Imm32(RNG_ADDEND), rngStateReg);
+
+    // rngState &= RNG_MASK;
+    masm.and64(Imm64(RNG_MASK), rngStateReg);
+
+    // if rngState == 0, escape from inlined code and call
+    // math_random_no_outparam.
+    masm.branchTest64(Assembler::Zero, rngStateReg, rngStateReg, tempReg, ool->entry());
+
+    // high = (rngState >> (RNG_STATE_WIDTH - RNG_HIGH_BITS)) << RNG_LOW_BITS;
+    masm.move64(rngStateReg, highReg);
+    masm.lshift64(Imm32(RNG_LOW_BITS - (RNG_STATE_WIDTH - RNG_HIGH_BITS)), highReg);
+    masm.and64(Imm64(RNG_HIGH_MASK), highReg);
+#ifdef JS_CODEGEN_X86
+    // eax and edx are overwritten by mul64 on x86.
+    masm.push64(highReg);
+#endif
+
+    // rngState = rngState * RNG_MULTIPLIER;
+    masm.mul64(Imm64(RNG_MULTIPLIER), rngStateReg);
+
+    // rngState += RNG_ADDEND;
+    masm.add64(Imm32(RNG_ADDEND), rngStateReg);
+
+    // rngState &= RNG_MASK;
+    masm.and64(Imm64(RNG_MASK), rngStateReg);
+
+    // cx->compartment()->rngState = rngState;
+    masm.store64(rngStateReg, Address(JSCompartmentReg, JSCompartment::offsetOfRngState()));
+
+    // low = rngState >> (RNG_STATE_WIDTH - RNG_LOW_BITS);
+    const Register64& lowReg = rngStateReg;
+    masm.rshift64(Imm32(RNG_STATE_WIDTH - RNG_LOW_BITS), lowReg);
+
+    // output = double(high | low);
+#ifdef JS_CODEGEN_X86
+    masm.pop64(highReg);
+#endif
+    masm.or64(highReg, lowReg);
+    masm.convertUInt64ToDouble(lowReg, tempReg, output);
+
+    // output = output * RNG_DSCALE_INV;
+    masm.mulDoublePtr(ImmPtr(&RNG_DSCALE_INV), tempReg, output);
+
+    masm.bind(ool->rejoin());
+}
+
+void
+CodeGenerator::visitOutOfLineRandom(OutOfLineRandom* ool)
+{
+    LRandom* ins = ool->lir();
+    Register temp1 = ToRegister(ins->tempMaybeEAX());
+    Register temp2 = ToRegister(ins->tempMaybeEDX());
+    MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
+
+    LiveRegisterSet regs;
+    setReturnDoubleRegs(&regs);
+    saveVolatile(regs);
+
+    masm.loadJSContext(temp1);
+
+    masm.setupUnalignedABICall(temp2);
+    masm.passABIArg(temp1);
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, math_random_no_outparam), MoveOp::DOUBLE);
+
+    restoreVolatile(regs);
+
+    masm.jump(ool->rejoin());
+}
+
 } // namespace jit
 } // namespace js
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -40,16 +40,17 @@ class OutOfLineUnboxFloatingPoint;
 class OutOfLineStoreElementHole;
 class OutOfLineTypeOfV;
 class OutOfLineUpdateCache;
 class OutOfLineCallPostWriteBarrier;
 class OutOfLineIsCallable;
 class OutOfLineRegExpExec;
 class OutOfLineRegExpTest;
 class OutOfLineLambdaArrow;
+class OutOfLineRandom;
 
 class CodeGenerator : public CodeGeneratorSpecific
 {
     void generateArgumentsChecks(bool bailout = true);
     bool generateBody();
 
   public:
     CodeGenerator(MIRGenerator* gen, LIRGraph* graph, MacroAssembler* masm = nullptr);
@@ -377,16 +378,19 @@ class CodeGenerator : public CodeGenerat
     void visitAssertResultT(LAssertResultT* ins);
     void emitAssertResultV(const ValueOperand output, const TemporaryTypeSet* typeset);
     void emitAssertObjectOrStringResult(Register input, MIRType type, const TemporaryTypeSet* typeset);
 
     void visitInterruptCheck(LInterruptCheck* lir);
     void visitAsmJSInterruptCheck(LAsmJSInterruptCheck* lir);
     void visitRecompileCheck(LRecompileCheck* ins);
 
+    void visitRandom(LRandom* ins);
+    void visitOutOfLineRandom(OutOfLineRandom* ool);
+
     IonScriptCounts* extractScriptCounts() {
         IonScriptCounts* counts = scriptCounts_;
         scriptCounts_ = nullptr;  // prevent delete in dtor
         return counts;
     }
 
   private:
     void addGetPropertyCache(LInstruction* ins, LiveRegisterSet liveRegs, Register objReg,
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -675,29 +675,47 @@ class MacroAssembler : public MacroAssem
     inline void and32(Imm32 imm, Register dest) PER_SHARED_ARCH;
     inline void and32(Imm32 imm, Register src, Register dest) DEFINED_ON(arm64);
     inline void and32(Imm32 imm, const Address& dest) PER_SHARED_ARCH;
     inline void and32(const Address& src, Register dest) PER_SHARED_ARCH;
 
     inline void andPtr(Register src, Register dest) PER_ARCH;
     inline void andPtr(Imm32 imm, Register dest) PER_ARCH;
 
+    inline void and64(Imm64 imm, Register64 dest) PER_ARCH;
+
     inline void or32(Register src, Register dest) PER_SHARED_ARCH;
     inline void or32(Imm32 imm, Register dest) PER_SHARED_ARCH;
     inline void or32(Imm32 imm, const Address& dest) PER_SHARED_ARCH;
 
     inline void orPtr(Register src, Register dest) PER_ARCH;
     inline void orPtr(Imm32 imm, Register dest) PER_ARCH;
 
+    inline void or64(Register64 src, Register64 dest) PER_ARCH;
+
     inline void xor32(Register src, Register dest) DEFINED_ON(x86_shared);
     inline void xor32(Imm32 imm, Register dest) PER_SHARED_ARCH;
 
     inline void xorPtr(Register src, Register dest) PER_ARCH;
     inline void xorPtr(Imm32 imm, Register dest) PER_ARCH;
 
+    // ===============================================================
+    // Shift functions
+
+    inline void lshiftPtr(Imm32 imm, Register dest) PER_ARCH;
+
+    inline void lshift64(Imm32 imm, Register64 dest) PER_ARCH;
+
+    inline void rshiftPtr(Imm32 imm, Register dest) PER_ARCH;
+    inline void rshiftPtr(Imm32 imm, Register src, Register dest) DEFINED_ON(arm64);
+
+    inline void rshiftPtrArithmetic(Imm32 imm, Register dest) PER_ARCH;
+
+    inline void rshift64(Imm32 imm, Register64 dest) PER_ARCH;
+
     //}}} check_macroassembler_style
   public:
 
     // Emits a test of a value against all types in a TypeSet. A scratch
     // register is required.
     template <typename Source>
     void guardTypeSet(const Source& address, const TypeSet* types, BarrierKind kind, Register scratch, Label* miss);
 
--- a/js/src/jit/Registers.h
+++ b/js/src/jit/Registers.h
@@ -88,16 +88,36 @@ struct Register {
     static uint32_t FirstBit(SetType x) {
         return Codes::FirstBit(x);
     }
     static uint32_t LastBit(SetType x) {
         return Codes::LastBit(x);
     }
 };
 
+struct Register64
+{
+#ifdef JS_PUNBOX64
+    Register reg;
+#else
+    Register high;
+    Register low;
+#endif
+
+#ifdef JS_PUNBOX64
+    explicit MOZ_CONSTEXPR Register64(Register r)
+      : reg(r)
+    {}
+#else
+    MOZ_CONSTEXPR Register64(Register h, Register l)
+      : high(h), low(l)
+    {}
+#endif
+};
+
 class RegisterDump
 {
   public:
     typedef mozilla::Array<Registers::RegisterContent, Registers::Total> GPRArray;
     typedef mozilla::Array<FloatRegisters::RegisterContent, FloatRegisters::TotalPhys> FPUArray;
 
   protected: // Silence Clang warning.
     GPRArray regs_;
--- a/js/src/jit/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -5,17 +5,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jit/arm/CodeGenerator-arm.h"
 
 #include "mozilla/MathAlgorithms.h"
 
 #include "jscntxt.h"
 #include "jscompartment.h"
-#include "jsmath.h"
 #include "jsnum.h"
 
 #include "jit/CodeGenerator.h"
 #include "jit/JitCompartment.h"
 #include "jit/JitFrames.h"
 #include "jit/MIR.h"
 #include "jit/MIRGraph.h"
 #include "js/Conversions.h"
@@ -2670,21 +2669,17 @@ CodeGeneratorARM::memoryBarrier(MemoryBa
 
 void
 CodeGeneratorARM::visitMemoryBarrier(LMemoryBarrier* ins)
 {
     memoryBarrier(ins->type());
 }
 
 void
-CodeGeneratorARM::visitRandom(LRandom* ins)
+CodeGeneratorARM::setReturnDoubleRegs(LiveRegisterSet* regs)
 {
-    Register temp = ToRegister(ins->temp());
-    Register temp2 = ToRegister(ins->temp2());
-
-    masm.loadJSContext(temp);
-
-    masm.setupUnalignedABICall(temp2);
-    masm.passABIArg(temp);
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, math_random_no_outparam), MoveOp::DOUBLE);
-
-    MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
+    MOZ_ASSERT(ReturnFloat32Reg.code_ == FloatRegisters::s0);
+    MOZ_ASSERT(ReturnDoubleReg.code_ == FloatRegisters::s0);
+    FloatRegister s1 = {FloatRegisters::s1, VFPRegister::Single};
+    regs->add(ReturnFloat32Reg);
+    regs->add(s1);
+    regs->add(ReturnDoubleReg);
 }
--- a/js/src/jit/arm/CodeGenerator-arm.h
+++ b/js/src/jit/arm/CodeGenerator-arm.h
@@ -211,17 +211,17 @@ class CodeGeneratorARM : public CodeGene
     void visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr* ins);
     void visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc* ins);
     void visitAsmJSPassStackArg(LAsmJSPassStackArg* ins);
 
     void visitMemoryBarrier(LMemoryBarrier* ins);
 
     void generateInvalidateEpilogue();
 
-    void visitRandom(LRandom* ins);
+    void setReturnDoubleRegs(LiveRegisterSet* regs);
 
     // Generating a result.
     template<typename S, typename T>
     void atomicBinopToTypedIntArray(AtomicOp op, Scalar::Type arrayType, const S& value,
                                     const T& mem, Register flagTemp, Register outTemp,
                                     AnyRegister output);
 
     // Generating no result.
--- a/js/src/jit/arm/LIR-arm.h
+++ b/js/src/jit/arm/LIR-arm.h
@@ -492,29 +492,12 @@ class LAsmJSAtomicBinopCallout : public 
         return getOperand(1);
     }
 
     const MAsmJSAtomicBinopHeap* mir() const {
         return mir_->toAsmJSAtomicBinopHeap();
     }
 };
 
-// Math.random().
-class LRandom : public LCallInstructionHelper<1, 0, 2>
-{
-  public:
-    LIR_HEADER(Random)
-    LRandom(const LDefinition& temp, const LDefinition& temp2) {
-        setTemp(0, temp);
-        setTemp(1, temp2);
-    }
-    const LDefinition* temp() {
-        return getTemp(0);
-    }
-    const LDefinition* temp2() {
-        return getTemp(1);
-    }
-};
-
 } // namespace jit
 } // namespace js
 
 #endif /* jit_arm_LIR_arm_h */
--- a/js/src/jit/arm/Lowering-arm.cpp
+++ b/js/src/jit/arm/Lowering-arm.cpp
@@ -736,11 +736,15 @@ LIRGeneratorARM::visitSubstr(MSubstr* in
                                          tempByteOpRegister());
     define(lir, ins);
     assignSafepoint(lir, ins);
 }
 
 void
 LIRGeneratorARM::visitRandom(MRandom* ins)
 {
-    LRandom* lir = new(alloc()) LRandom(tempFixed(CallTempReg0), tempFixed(CallTempReg1));
-    defineReturn(lir, ins);
+    LRandom *lir = new(alloc()) LRandom(temp(),
+                                        temp(),
+                                        temp(),
+                                        temp(),
+                                        temp());
+    defineFixed(lir, ins, LFloatReg(ReturnDoubleReg));
 }
--- a/js/src/jit/arm/MacroAssembler-arm-inl.h
+++ b/js/src/jit/arm/MacroAssembler-arm-inl.h
@@ -59,16 +59,23 @@ MacroAssembler::andPtr(Register src, Reg
 
 void
 MacroAssembler::andPtr(Imm32 imm, Register dest)
 {
     ma_and(imm, dest);
 }
 
 void
+MacroAssembler::and64(Imm64 imm, Register64 dest)
+{
+    and32(Imm32(imm.value & 0xFFFFFFFFL), dest.low);
+    and32(Imm32((imm.value >> 32) & 0xFFFFFFFFL), dest.high);
+}
+
+void
 MacroAssembler::or32(Register src, Register dest)
 {
     ma_orr(src, dest);
 }
 
 void
 MacroAssembler::or32(Imm32 imm, Register dest)
 {
@@ -92,16 +99,23 @@ MacroAssembler::orPtr(Register src, Regi
 
 void
 MacroAssembler::orPtr(Imm32 imm, Register dest)
 {
     ma_orr(imm, dest);
 }
 
 void
+MacroAssembler::or64(Register64 src, Register64 dest)
+{
+    or32(src.low, dest.low);
+    or32(src.high, dest.high);
+}
+
+void
 MacroAssembler::xor32(Imm32 imm, Register dest)
 {
     ma_eor(imm, dest, SetCC);
 }
 
 void
 MacroAssembler::xorPtr(Register src, Register dest)
 {
@@ -109,15 +123,52 @@ MacroAssembler::xorPtr(Register src, Reg
 }
 
 void
 MacroAssembler::xorPtr(Imm32 imm, Register dest)
 {
     ma_eor(imm, dest);
 }
 
+// ===============================================================
+// Shift functions
+
+void
+MacroAssembler::lshiftPtr(Imm32 imm, Register dest)
+{
+    ma_lsl(imm, dest, dest);
+}
+
+void
+MacroAssembler::lshift64(Imm32 imm, Register64 dest)
+{
+    as_mov(dest.high, lsl(dest.high, imm.value));
+    as_orr(dest.high, dest.high, lsr(dest.low, 32 - imm.value));
+    as_mov(dest.low, lsl(dest.low, imm.value));
+}
+
+void
+MacroAssembler::rshiftPtr(Imm32 imm, Register dest)
+{
+    ma_lsr(imm, dest, dest);
+}
+
+void
+MacroAssembler::rshiftPtrArithmetic(Imm32 imm, Register dest)
+{
+    ma_asr(imm, dest, dest);
+}
+
+void
+MacroAssembler::rshift64(Imm32 imm, Register64 dest)
+{
+    as_mov(dest.low, lsr(dest.low, imm.value));
+    as_orr(dest.low, dest.low, lsl(dest.high, 32 - imm.value));
+    as_mov(dest.high, lsr(dest.high, imm.value));
+}
+
 //}}} check_macroassembler_style
 // ===============================================================
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_arm_MacroAssembler_arm_inl_h */
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -85,16 +85,29 @@ void
 MacroAssemblerARM::convertUInt32ToDouble(Register src, FloatRegister dest_)
 {
     // Direct conversions aren't possible.
     VFPRegister dest = VFPRegister(dest_);
     as_vxfer(src, InvalidReg, dest.uintOverlay(), CoreToFloat);
     as_vcvt(dest, dest.uintOverlay());
 }
 
+static const double TO_DOUBLE_HIGH_SCALE = 0x100000000;
+
+void
+MacroAssemblerARMCompat::convertUInt64ToDouble(Register64 src, Register temp, FloatRegister dest)
+{
+    convertUInt32ToDouble(src.high, dest);
+    movePtr(ImmPtr(&TO_DOUBLE_HIGH_SCALE), ScratchRegister);
+    loadDouble(Address(ScratchRegister, 0), ScratchDoubleReg);
+    mulDouble(ScratchDoubleReg, dest);
+    convertUInt32ToDouble(src.low, ScratchDoubleReg);
+    addDouble(ScratchDoubleReg, dest);
+}
+
 void
 MacroAssemblerARM::convertUInt32ToFloat32(Register src, FloatRegister dest_)
 {
     // Direct conversions aren't possible.
     VFPRegister dest = VFPRegister(dest_);
     as_vxfer(src, InvalidReg, dest.uintOverlay(), CoreToFloat);
     as_vcvt(VFPRegister(dest).singleOverlay(), dest.uintOverlay());
 }
@@ -3368,16 +3381,32 @@ MacroAssemblerARMCompat::storeUnboxedVal
 template void
 MacroAssemblerARMCompat::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const Address& dest,
                                            MIRType slotType);
 
 template void
 MacroAssemblerARMCompat::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const BaseIndex& dest,
                                            MIRType slotType);
 
+
+void
+MacroAssemblerARMCompat::branchTest64(Condition cond, Register64 lhs, Register64 rhs,
+                                      Register temp, Label* label)
+{
+    if (cond == Assembler::Zero) {
+        MOZ_ASSERT(lhs.low == rhs.low);
+        MOZ_ASSERT(lhs.high == rhs.high);
+        mov(lhs.low, ScratchRegister);
+        asMasm().or32(lhs.high, ScratchRegister);
+        branchTestPtr(cond, ScratchRegister, ScratchRegister, label);
+    } else {
+        MOZ_CRASH("Unsupported condition");
+    }
+}
+
 void
 MacroAssemblerARMCompat::moveValue(const Value& val, Register type, Register data)
 {
     jsval_layout jv = JSVAL_TO_IMPL(val);
     ma_mov(Imm32(jv.s.tag), type);
     if (val.isMarkable())
         ma_mov(ImmGCPtr(reinterpret_cast<gc::Cell*>(val.toGCThing())), data);
     else
--- a/js/src/jit/arm/MacroAssembler-arm.h
+++ b/js/src/jit/arm/MacroAssembler-arm.h
@@ -982,16 +982,17 @@ class MacroAssemblerARMCompat : public M
     }
     void branchPtr(Condition cond, Register lhs, Imm32 imm, Label* label) {
         branch32(cond, lhs, imm, label);
     }
     void decBranchPtr(Condition cond, Register lhs, Imm32 imm, Label* label) {
         subPtr(imm, lhs);
         branch32(cond, lhs, Imm32(0), label);
     }
+    void branchTest64(Condition cond, Register64 lhs, Register64 rhs, Register temp, Label* label);
     void moveValue(const Value& val, Register type, Register data);
 
     CodeOffsetJump jumpWithPatch(RepatchLabel* label, Condition cond = Always,
                                  Label* documentation = nullptr);
     CodeOffsetJump backedgeJump(RepatchLabel* label, Label* documentation) {
         return jumpWithPatch(label, Always, documentation);
     }
     template <typename T>
@@ -1198,41 +1199,54 @@ class MacroAssemblerARMCompat : public M
     template <typename T>
     void branchSub32(Condition cond, T src, Register dest, Label* label) {
         sub32(src, dest);
         j(cond, label);
     }
 
     void addPtr(Register src, Register dest);
     void addPtr(const Address& src, Register dest);
+    void add64(Imm32 imm, Register64 dest) {
+        ma_add(imm, dest.low, SetCC);
+        ma_adc(Imm32(0), dest.high, LeaveCC);
+    }
+    void not32(Register reg);
 
     void move32(Imm32 imm, Register dest);
     void move32(Register src, Register dest);
 
     void movePtr(Register src, Register dest);
     void movePtr(ImmWord imm, Register dest);
     void movePtr(ImmPtr imm, Register dest);
     void movePtr(AsmJSImmPtr imm, Register dest);
     void movePtr(ImmGCPtr imm, Register dest);
+    void move64(Register64 src, Register64 dest) {
+        move32(src.low, dest.low);
+        move32(src.high, dest.high);
+    }
 
     void load8SignExtend(const Address& address, Register dest);
     void load8SignExtend(const BaseIndex& src, Register dest);
 
     void load8ZeroExtend(const Address& address, Register dest);
     void load8ZeroExtend(const BaseIndex& src, Register dest);
 
     void load16SignExtend(const Address& address, Register dest);
     void load16SignExtend(const BaseIndex& src, Register dest);
 
     void load16ZeroExtend(const Address& address, Register dest);
     void load16ZeroExtend(const BaseIndex& src, Register dest);
 
     void load32(const Address& address, Register dest);
     void load32(const BaseIndex& address, Register dest);
     void load32(AbsoluteAddress address, Register dest);
+    void load64(const Address& address, Register64 dest) {
+        load32(address, dest.low);
+        load32(Address(address.base, address.offset + 4), dest.high);
+    }
 
     void loadPtr(const Address& address, Register dest);
     void loadPtr(const BaseIndex& src, Register dest);
     void loadPtr(AbsoluteAddress address, Register dest);
     void loadPtr(AsmJSAbsoluteAddress address, Register dest);
 
     void loadPrivate(const Address& address, Register dest);
 
@@ -1289,16 +1303,21 @@ class MacroAssemblerARMCompat : public M
     void store32(Register src, AbsoluteAddress address);
     void store32(Register src, const Address& address);
     void store32(Register src, const BaseIndex& address);
     void store32(Imm32 src, const Address& address);
     void store32(Imm32 src, const BaseIndex& address);
 
     void store32_NoSecondScratch(Imm32 src, const Address& address);
 
+    void store64(Register64 src, Address address) {
+        store32(src.low, address);
+        store32(src.high, Address(address.base, address.offset + 4));
+    }
+
     template <typename T> void storePtr(ImmWord imm, T address);
     template <typename T> void storePtr(ImmPtr imm, T address);
     template <typename T> void storePtr(ImmGCPtr imm, T address);
     void storePtr(Register src, const Address& address);
     void storePtr(Register src, const BaseIndex& address);
     void storePtr(Register src, AbsoluteAddress dest);
     void storeDouble(FloatRegister src, Address addr) {
         ma_vstr(src, addr);
@@ -1637,16 +1656,49 @@ class MacroAssemblerARMCompat : public M
         addPtr(Imm32(imm.value), dest);
     }
     void addPtr(ImmPtr imm, const Register dest) {
         addPtr(ImmWord(uintptr_t(imm.value)), dest);
     }
     void mulBy3(const Register& src, const Register& dest) {
         as_add(dest, src, lsl(src, 1));
     }
+    void mul64(Imm64 imm, const Register64& dest) {
+        // LOW32  = LOW(LOW(dest) * LOW(imm));
+        // HIGH32 = LOW(HIGH(dest) * LOW(imm)) [multiply imm into upper bits]
+        //        + LOW(LOW(dest) * HIGH(imm)) [multiply dest into upper bits]
+        //        + HIGH(LOW(dest) * LOW(imm)) [carry]
+
+        // HIGH(dest) = LOW(HIGH(dest) * LOW(imm));
+        ma_mov(Imm32(imm.value & 0xFFFFFFFFL), ScratchRegister);
+        as_mul(dest.high, dest.high, ScratchRegister);
+
+        // high:low = LOW(dest) * LOW(imm);
+        as_umull(secondScratchReg_, ScratchRegister, dest.low, ScratchRegister);
+
+        // HIGH(dest) += high;
+        as_add(dest.high, dest.high, O2Reg(secondScratchReg_));
+
+        // HIGH(dest) += LOW(LOW(dest) * HIGH(imm));
+        if (((imm.value >> 32) & 0xFFFFFFFFL) == 5)
+            as_add(secondScratchReg_, dest.low, lsl(dest.low, 2));
+        else
+            MOZ_CRASH("Not supported imm");
+        as_add(dest.high, dest.high, O2Reg(secondScratchReg_));
+
+        // LOW(dest) = low;
+        ma_mov(ScratchRegister, dest.low);
+    }
+
+    void convertUInt64ToDouble(Register64 src, Register temp, FloatRegister dest);
+    void mulDoublePtr(ImmPtr imm, Register temp, FloatRegister dest) {
+        movePtr(imm, ScratchRegister);
+        loadDouble(Address(ScratchRegister, 0), ScratchDoubleReg);
+        mulDouble(ScratchDoubleReg, dest);
+    }
 
     void setStackArg(Register reg, uint32_t arg);
 
     void breakpoint();
     // Conditional breakpoint.
     void breakpoint(Condition cc);
 
     // Trigger the simulator's interactive read-eval-print loop.
@@ -1659,26 +1711,16 @@ class MacroAssemblerARMCompat : public M
                       Label* label);
 
     void compareFloat(FloatRegister lhs, FloatRegister rhs);
     void branchFloat(DoubleCondition cond, FloatRegister lhs, FloatRegister rhs,
                      Label* label);
 
     void checkStackAlignment();
 
-    void rshiftPtr(Imm32 imm, Register dest) {
-        ma_lsr(imm, dest, dest);
-    }
-    void rshiftPtrArithmetic(Imm32 imm, Register dest) {
-        ma_asr(imm, dest, dest);
-    }
-    void lshiftPtr(Imm32 imm, Register dest) {
-        ma_lsl(imm, dest, dest);
-    }
-
     // If source is a double, load it into dest. If source is int32, convert it
     // to double. Else, branch to failure.
     void ensureDouble(const ValueOperand& source, FloatRegister dest, Label* failure);
 
     void
     emitSet(Assembler::Condition cond, Register dest)
     {
         ma_mov(Imm32(0), dest);
--- a/js/src/jit/arm64/CodeGenerator-arm64.cpp
+++ b/js/src/jit/arm64/CodeGenerator-arm64.cpp
@@ -603,22 +603,16 @@ CodeGeneratorARM64::visitInterruptCheck(
 }
 
 void
 CodeGeneratorARM64::generateInvalidateEpilogue()
 {
     MOZ_CRASH("generateInvalidateEpilogue");
 }
 
-void
-CodeGeneratorARM64::visitRandom(LRandom* ins)
-{
-    MOZ_CRASH("visitRandom");
-}
-
 template <class U>
 Register
 getBase(U* mir)
 {
     switch (mir->base()) {
       case U::Heap: return HeapReg;
       case U::Global: return GlobalReg;
     }
@@ -727,8 +721,19 @@ CodeGeneratorARM64::visitNegD(LNegD* ins
     MOZ_CRASH("visitNegD");
 }
 
 void
 CodeGeneratorARM64::visitNegF(LNegF* ins)
 {
     MOZ_CRASH("visitNegF");
 }
+
+void
+CodeGeneratorARM64::setReturnDoubleRegs(LiveRegisterSet* regs)
+{
+    MOZ_ASSERT(ReturnFloat32Reg.code_ == FloatRegisters::s0);
+    MOZ_ASSERT(ReturnDoubleReg.code_ == FloatRegisters::d0);
+    FloatRegister s1 = {FloatRegisters::s1, FloatRegisters::Single};
+    regs->add(ReturnFloat32Reg);
+    regs->add(s1);
+    regs->add(ReturnDoubleReg);
+}
--- a/js/src/jit/arm64/CodeGenerator-arm64.h
+++ b/js/src/jit/arm64/CodeGenerator-arm64.h
@@ -215,17 +215,17 @@ class CodeGeneratorARM64 : public CodeGe
     void visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar* ins);
     void visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar* ins);
     void visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr* ins);
     void visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc* ins);
     void visitAsmJSPassStackArg(LAsmJSPassStackArg* ins);
 
     void generateInvalidateEpilogue();
 
-    void visitRandom(LRandom* ins);
+    void setReturnDoubleRegs(LiveRegisterSet* regs);
 
   protected:
     void postAsmJSCall(LAsmJSCall* lir) {
         MOZ_CRASH("postAsmJSCall");
     }
 
     void visitEffectiveAddress(LEffectiveAddress* ins);
     void visitUDiv(LUDiv* ins);
--- a/js/src/jit/arm64/LIR-arm64.h
+++ b/js/src/jit/arm64/LIR-arm64.h
@@ -402,29 +402,12 @@ class LAsmJSLoadFuncPtr : public LInstru
     const LAllocation* index() {
         return getOperand(0);
     }
     const LDefinition* temp() {
         return getTemp(0);
     }
 };
 
-// Math.random().
-class LRandom : public LCallInstructionHelper<1, 0, 2>
-{
-  public:
-    LIR_HEADER(Random)
-    LRandom(const LDefinition& temp, const LDefinition& temp2) {
-        setTemp(0, temp);
-        setTemp(1, temp2);
-    }
-    const LDefinition* temp() {
-        return getTemp(0);
-    }
-    const LDefinition* temp2() {
-        return getTemp(1);
-    }
-};
-
 } // namespace jit
 } // namespace js
 
 #endif /* jit_arm64_LIR_arm64_h */
--- a/js/src/jit/arm64/Lowering-arm64.cpp
+++ b/js/src/jit/arm64/Lowering-arm64.cpp
@@ -299,10 +299,13 @@ void
 LIRGeneratorARM64::visitSubstr(MSubstr* ins)
 {
     MOZ_CRASH("visitSubstr");
 }
 
 void
 LIRGeneratorARM64::visitRandom(MRandom* ins)
 {
-    MOZ_CRASH("visitRandom");
+    LRandom *lir = new(alloc()) LRandom(temp(),
+                                        temp(),
+                                        temp());
+    defineFixed(lir, ins, LFloatReg(ReturnDoubleReg));
 }
--- a/js/src/jit/arm64/MacroAssembler-arm64-inl.h
+++ b/js/src/jit/arm64/MacroAssembler-arm64-inl.h
@@ -69,16 +69,25 @@ MacroAssembler::andPtr(Register src, Reg
 
 void
 MacroAssembler::andPtr(Imm32 imm, Register dest)
 {
     And(ARMRegister(dest, 64), ARMRegister(dest, 64), Operand(imm.value));
 }
 
 void
+MacroAssembler::and64(Imm64 imm, Register64 dest)
+{
+    vixl::UseScratchRegisterScope temps(this);
+    const Register scratch = temps.AcquireX().asUnsized();
+    mov(ImmWord(imm.value), scratch);
+    andPtr(scratch, dest.reg);
+}
+
+void
 MacroAssembler::or32(Imm32 imm, Register dest)
 {
     Orr(ARMRegister(dest, 32), ARMRegister(dest, 32), Operand(imm.value));
 }
 
 void
 MacroAssembler::or32(Register src, Register dest)
 {
@@ -104,16 +113,22 @@ MacroAssembler::orPtr(Register src, Regi
 
 void
 MacroAssembler::orPtr(Imm32 imm, Register dest)
 {
     Orr(ARMRegister(dest, 64), ARMRegister(dest, 64), Operand(imm.value));
 }
 
 void
+MacroAssembler::or64(Register64 src, Register64 dest)
+{
+    orPtr(src.reg, dest.reg);
+}
+
+void
 MacroAssembler::xor32(Imm32 imm, Register dest)
 {
     Eor(ARMRegister(dest, 32), ARMRegister(dest, 32), Operand(imm.value));
 }
 
 void
 MacroAssembler::xorPtr(Register src, Register dest)
 {
@@ -121,16 +136,55 @@ MacroAssembler::xorPtr(Register src, Reg
 }
 
 void
 MacroAssembler::xorPtr(Imm32 imm, Register dest)
 {
     Eor(ARMRegister(dest, 64), ARMRegister(dest, 64), Operand(imm.value));
 }
 
+// ===============================================================
+// Shift functions
+
+void
+MacroAssembler::lshiftPtr(Imm32 imm, Register dest)
+{
+    Lsl(ARMRegister(dest, 64), ARMRegister(dest, 64), imm.value);
+}
+
+void
+MacroAssembler::lshift64(Imm32 imm, Register64 dest)
+{
+    lshiftPtr(imm, dest.reg);
+}
+
+void
+MacroAssembler::rshiftPtr(Imm32 imm, Register dest)
+{
+    Lsr(ARMRegister(dest, 64), ARMRegister(dest, 64), imm.value);
+}
+
+void
+MacroAssembler::rshiftPtr(Imm32 imm, Register src, Register dest)
+{
+    Lsr(ARMRegister(dest, 64), ARMRegister(src, 64), imm.value);
+}
+
+void
+MacroAssembler::rshiftPtrArithmetic(Imm32 imm, Register dest)
+{
+    Asr(ARMRegister(dest, 64), ARMRegister(dest, 64), imm.value);
+}
+
+void
+MacroAssembler::rshift64(Imm32 imm, Register64 dest)
+{
+    rshiftPtr(imm, dest.reg);
+}
+
 //}}} check_macroassembler_style
 // ===============================================================
 
 template <typename T>
 void
 MacroAssemblerCompat::andToStackPtr(T t)
 {
     asMasm().andPtr(t, getStackPointer());
--- a/js/src/jit/arm64/MacroAssembler-arm64.cpp
+++ b/js/src/jit/arm64/MacroAssembler-arm64.cpp
@@ -120,16 +120,23 @@ MacroAssemblerCompat::movePatchablePtr(I
 
     // Add the entry to the pool, fix up the LDR imm19 offset,
     // and add the completed instruction to the buffer.
     return armbuffer_.allocEntry(numInst, numPoolEntries,
                                  (uint8_t*)&instructionScratch, literalAddr);
 }
 
 void
+MacroAssemblerCompat::loadPrivate(const Address& src, Register dest)
+{
+    loadPtr(src, dest);
+    asMasm().lshiftPtr(Imm32(1), dest);
+}
+
+void
 MacroAssemblerCompat::handleFailureWithHandlerTail(void* handler)
 {
     // Reserve space for exception information.
     int64_t size = (sizeof(ResumeFromException) + 7) & ~7;
     Sub(GetStackPointer64(), GetStackPointer64(), Operand(size));
     if (!GetStackPointer64().Is(sp))
         Mov(sp, GetStackPointer64());
 
--- a/js/src/jit/arm64/MacroAssembler-arm64.h
+++ b/js/src/jit/arm64/MacroAssembler-arm64.h
@@ -779,16 +779,19 @@ class MacroAssemblerCompat : public vixl
     void movePtr(AsmJSImmPtr imm, Register dest) {
         BufferOffset off = movePatchablePtr(ImmWord(0xffffffffffffffffULL), dest);
         append(AsmJSAbsoluteLink(CodeOffsetLabel(off.getOffset()), imm.kind()));
     }
     void movePtr(ImmGCPtr imm, Register dest) {
         BufferOffset load = movePatchablePtr(ImmPtr(imm.value), dest);
         writeDataRelocation(imm, load);
     }
+    void move64(Register64 src, Register64 dest) {
+        movePtr(src.reg, dest.reg);
+    }
 
     void mov(ImmWord imm, Register dest) {
         movePtr(imm, dest);
     }
     void mov(ImmPtr imm, Register dest) {
         movePtr(imm, dest);
     }
     void mov(AsmJSImmPtr imm, Register dest) {
@@ -845,20 +848,17 @@ class MacroAssemblerCompat : public vixl
 
             Add(scratch, ARMRegister(base, 64), Operand(int64_t(src.offset)));
             Ldr(dest64, MemOperand(scratch, index64, vixl::LSL, scale));
             return;
         }
 
         Ldr(dest64, MemOperand(ARMRegister(base, 64), index64, vixl::LSL, scale));
     }
-    void loadPrivate(const Address& src, Register dest) {
-        loadPtr(src, dest);
-        lshiftPtr(Imm32(1), dest);
-    }
+    void loadPrivate(const Address& src, Register dest);
 
     void store8(Register src, const Address& address) {
         Strb(ARMRegister(src, 32), MemOperand(ARMRegister(address.base, 64), address.offset));
     }
     void store8(Imm32 imm, const Address& address) {
         vixl::UseScratchRegisterScope temps(this);
         const ARMRegister scratch32 = temps.AcquireW();
         MOZ_ASSERT(scratch32.asUnsized() != address.base);
@@ -984,16 +984,20 @@ class MacroAssemblerCompat : public vixl
         temps.Exclude(ARMRegister(ScratchReg2, 32)); // Disallow ScratchReg2.
         const ARMRegister scratch32 = temps.AcquireW();
 
         MOZ_ASSERT(scratch32.asUnsized() != address.base);
         Mov(scratch32, uint64_t(imm.value));
         Str(scratch32, MemOperand(ARMRegister(address.base, 64), address.offset));
     }
 
+    void store64(Register64 src, Address address) {
+        storePtr(src.reg, address);
+    }
+
     // SIMD.
     void loadInt32x1(const Address& addr, FloatRegister dest) { MOZ_CRASH("NYI"); }
     void loadInt32x1(const BaseIndex& addr, FloatRegister dest) { MOZ_CRASH("NYI"); }
     void loadInt32x2(const Address& addr, FloatRegister dest) { MOZ_CRASH("NYI"); }
     void loadInt32x2(const BaseIndex& addr, FloatRegister dest) { MOZ_CRASH("NYI"); }
     void loadInt32x3(const Address& src, FloatRegister dest) { MOZ_CRASH("NYI"); }
     void loadInt32x3(const BaseIndex& src, FloatRegister dest) { MOZ_CRASH("NYI"); }
     void storeInt32x1(FloatRegister src, const Address& dest) { MOZ_CRASH("NYI"); }
@@ -1057,30 +1061,16 @@ class MacroAssemblerCompat : public vixl
     void branchStackPtr(Condition cond, T rhs, Label* label) {
         branchPtr(cond, getStackPointer(), rhs, label);
     }
     template <typename T>
     void branchStackPtrRhs(Condition cond, T lhs, Label* label) {
         branchPtr(cond, lhs, getStackPointer(), label);
     }
 
-    void rshiftPtr(Imm32 imm, Register dest) {
-        Lsr(ARMRegister(dest, 64), ARMRegister(dest, 64), imm.value);
-    }
-    void rshiftPtr(Imm32 imm, Register src, Register dest) {
-        Lsr(ARMRegister(dest, 64), ARMRegister(src, 64), imm.value);
-    }
-
-    void rshiftPtrArithmetic(Imm32 imm, Register dest) {
-        Asr(ARMRegister(dest, 64), ARMRegister(dest, 64), imm.value);
-    }
-    void lshiftPtr(Imm32 imm, Register dest) {
-        Lsl(ARMRegister(dest, 64), ARMRegister(dest, 64), imm.value);
-    }
-
     void testPtr(Register lhs, Register rhs) {
         Tst(ARMRegister(lhs, 64), Operand(ARMRegister(rhs, 64)));
     }
     void test32(Register lhs, Register rhs) {
         Tst(ARMRegister(lhs, 32), Operand(ARMRegister(rhs, 32)));
     }
     void test32(const Address& addr, Imm32 imm) {
         vixl::UseScratchRegisterScope temps(this);
@@ -1157,17 +1147,17 @@ class MacroAssemblerCompat : public vixl
         vixl::UseScratchRegisterScope temps(this);
         const Register scratch = temps.AcquireX().asUnsized();
         MOZ_ASSERT(scratch != lhs.base);
         loadPtr(lhs, scratch);
         cmpPtr(scratch, rhs);
     }
 
     void loadDouble(const Address& src, FloatRegister dest) {
-        Ldr(ARMFPRegister(dest, 64), MemOperand(ARMRegister(src.base,64), src.offset));
+        Ldr(ARMFPRegister(dest, 64), MemOperand(src));
     }
     void loadDouble(const BaseIndex& src, FloatRegister dest) {
         ARMRegister base(src.base, 64);
         ARMRegister index(src.index, 64);
 
         if (src.offset == 0) {
             Ldr(ARMFPRegister(dest, 64), MemOperand(base, index, vixl::LSL, unsigned(src.scale)));
             return;
@@ -1301,16 +1291,19 @@ class MacroAssemblerCompat : public vixl
         doBaseIndex(ARMRegister(dest, 32), src, vixl::LDR_w);
     }
     void load32(AbsoluteAddress address, Register dest) {
         vixl::UseScratchRegisterScope temps(this);
         const ARMRegister scratch64 = temps.AcquireX();
         movePtr(ImmWord((uintptr_t)address.addr), scratch64.asUnsized());
         ldr(ARMRegister(dest, 32), MemOperand(scratch64));
     }
+    void load64(const Address& address, Register64 dest) {
+        loadPtr(address, dest.reg);
+    }
 
     void load8SignExtend(const Address& address, Register dest) {
         Ldrsb(ARMRegister(dest, 32), MemOperand(ARMRegister(address.base, 64), address.offset));
     }
     void load8SignExtend(const BaseIndex& src, Register dest) {
         doBaseIndex(ARMRegister(dest, 32), src, vixl::LDRSB_w);
     }
 
@@ -1361,16 +1354,19 @@ class MacroAssemblerCompat : public vixl
         vixl::UseScratchRegisterScope temps(this);
         const ARMRegister scratch32 = temps.AcquireW();
         MOZ_ASSERT(scratch32.asUnsized() != dest.base);
 
         Ldr(scratch32, MemOperand(ARMRegister(dest.base, 64), dest.offset));
         Adds(scratch32, scratch32, Operand(imm.value));
         Str(scratch32, MemOperand(ARMRegister(dest.base, 64), dest.offset));
     }
+    void add64(Imm32 imm, Register64 dest) {
+        Add(ARMRegister(dest.reg, 64), ARMRegister(dest.reg, 64), Operand(imm.value));
+    }
 
     void sub32(Imm32 imm, Register dest) {
         Sub(ARMRegister(dest, 32), ARMRegister(dest, 32), Operand(imm.value));
     }
     void sub32(Register src, Register dest) {
         Sub(ARMRegister(dest, 32), ARMRegister(dest, 32), Operand(ARMRegister(src, 32)));
     }
 
@@ -1906,16 +1902,19 @@ class MacroAssemblerCompat : public vixl
         vixl::UseScratchRegisterScope temps(this);
         const ARMRegister scratch64 = temps.AcquireX();
         MOZ_ASSERT(scratch64.asUnsized() != valaddr.base);
         MOZ_ASSERT(scratch64.asUnsized() != value.valueReg());
         loadValue(valaddr, scratch64.asUnsized());
         Cmp(ARMRegister(value.valueReg(), 64), Operand(scratch64));
         B(label, cond);
     }
+    void branchTest64(Condition cond, Register64 lhs, Register64 rhs, Register temp, Label* label) {
+        branchTestPtr(cond, lhs.reg, rhs.reg, label);
+    }
 
     void compareDouble(DoubleCondition cond, FloatRegister lhs, FloatRegister rhs) {
         Fcmp(ARMFPRegister(lhs, 64), ARMFPRegister(rhs, 64));
     }
     void branchDouble(DoubleCondition cond, FloatRegister lhs, FloatRegister rhs, Label* label) {
         compareDouble(cond, lhs, rhs);
         switch (cond) {
           case DoubleNotEqual: {
@@ -2953,16 +2952,37 @@ class MacroAssemblerCompat : public vixl
     }
 
     void mulBy3(Register src, Register dest) {
         ARMRegister xdest(dest, 64);
         ARMRegister xsrc(src, 64);
         Add(xdest, xsrc, Operand(xsrc, vixl::LSL, 1));
     }
 
+    void mul64(Imm64 imm, const Register64& dest) {
+        vixl::UseScratchRegisterScope temps(this);
+        const ARMRegister scratch64 = temps.AcquireX();
+        MOZ_ASSERT(dest.reg != scratch64.asUnsized());
+        mov(ImmWord(imm.value), scratch64.asUnsized());
+        Mul(ARMRegister(dest.reg, 64), ARMRegister(dest.reg, 64), scratch64);
+    }
+
+    void convertUInt64ToDouble(Register64 src, Register temp, FloatRegister dest) {
+        Ucvtf(ARMFPRegister(dest, 64), ARMRegister(src.reg, 64));
+    }
+    void mulDoublePtr(ImmPtr imm, Register temp, FloatRegister dest) {
+        vixl::UseScratchRegisterScope temps(this);
+        const Register scratch = temps.AcquireX().asUnsized();
+        MOZ_ASSERT(temp != scratch);
+        movePtr(imm, scratch);
+        const ARMFPRegister scratchDouble = temps.AcquireD();
+        Ldr(scratchDouble, MemOperand(Address(scratch, 0)));
+        fmul(ARMFPRegister(dest, 64), ARMFPRegister(dest, 64), scratchDouble);
+    }
+
     template <typename T>
     void branchAdd32(Condition cond, T src, Register dest, Label* label) {
         adds32(src, dest);
         branch(cond, label);
     }
 
     template <typename T>
     void branchSub32(Condition cond, T src, Register dest, Label* label) {
--- a/js/src/jit/mips-shared/LIR-mips-shared.h
+++ b/js/src/jit/mips-shared/LIR-mips-shared.h
@@ -279,29 +279,12 @@ class LAsmJSLoadFuncPtr : public LInstru
     const MAsmJSLoadFuncPtr* mir() const {
         return mir_->toAsmJSLoadFuncPtr();
     }
     const LAllocation* index() {
         return getOperand(0);
     }
 };
 
-// Math.random().
-class LRandom : public LCallInstructionHelper<1, 0, 2>
-{
-  public:
-    LIR_HEADER(Random)
-    LRandom(const LDefinition& temp, const LDefinition& temp2) {
-        setTemp(0, temp);
-        setTemp(1, temp2);
-    }
-    const LDefinition* temp() {
-        return getTemp(0);
-    }
-    const LDefinition* temp2() {
-        return getTemp(1);
-    }
-};
-
 } // namespace jit
 } // namespace js
 
 #endif /* jit_mips_shared_LIR_mips_shared_h */
--- a/js/src/jit/mips-shared/Lowering-mips-shared.cpp
+++ b/js/src/jit/mips-shared/Lowering-mips-shared.cpp
@@ -410,11 +410,15 @@ void
 LIRGeneratorMIPSShared::visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop* ins)
 {
     MOZ_CRASH("NYI");
 }
 
 void
 LIRGeneratorMIPSShared::visitRandom(MRandom* ins)
 {
-    LRandom* lir = new(alloc()) LRandom(tempFixed(CallTempReg0), tempFixed(CallTempReg1));
-    defineReturn(lir, ins);
+    LRandom *lir = new(alloc()) LRandom(temp(),
+                                        temp(),
+                                        temp(),
+                                        temp(),
+                                        temp());
+    defineFixed(lir, ins, LFloatReg(ReturnDoubleReg));
 }
--- a/js/src/jit/mips32/CodeGenerator-mips32.cpp
+++ b/js/src/jit/mips32/CodeGenerator-mips32.cpp
@@ -5,17 +5,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jit/mips32/CodeGenerator-mips32.h"
 
 #include "mozilla/MathAlgorithms.h"
 
 #include "jscntxt.h"
 #include "jscompartment.h"
-#include "jsmath.h"
 #include "jsnum.h"
 
 #include "jit/CodeGenerator.h"
 #include "jit/JitCompartment.h"
 #include "jit/JitFrames.h"
 #include "jit/MIR.h"
 #include "jit/MIRGraph.h"
 #include "js/Conversions.h"
@@ -2115,21 +2114,15 @@ CodeGeneratorMIPS::visitNegF(LNegF* ins)
 {
     FloatRegister input = ToFloatRegister(ins->input());
     FloatRegister output = ToFloatRegister(ins->output());
 
     masm.as_negs(output, input);
 }
 
 void
-CodeGeneratorMIPS::visitRandom(LRandom* ins)
+CodeGeneratorMIPS::setReturnDoubleRegs(LiveRegisterSet* regs)
 {
-    Register temp = ToRegister(ins->temp());
-    Register temp2 = ToRegister(ins->temp2());
-
-    masm.loadJSContext(temp);
-
-    masm.setupUnalignedABICall(temp2);
-    masm.passABIArg(temp);
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, math_random_no_outparam), MoveOp::DOUBLE);
-
-    MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
+    MOZ_ASSERT(ReturnFloat32Reg.code_ == ReturnDoubleReg.code_);
+    regs->add(ReturnFloat32Reg);
+    regs->add(ReturnDoubleReg.singleOverlay(1));
+    regs->add(ReturnDoubleReg);
 }
--- a/js/src/jit/mips32/CodeGenerator-mips32.h
+++ b/js/src/jit/mips32/CodeGenerator-mips32.h
@@ -236,17 +236,17 @@ class CodeGeneratorMIPS : public CodeGen
     void visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar* ins);
     void visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr* ins);
     void visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc* ins);
 
     void visitAsmJSPassStackArg(LAsmJSPassStackArg* ins);
 
     void generateInvalidateEpilogue();
 
-    void visitRandom(LRandom* ins);
+    void setReturnDoubleRegs(LiveRegisterSet* regs);
 
   protected:
     void visitEffectiveAddress(LEffectiveAddress* ins);
     void visitUDivOrMod(LUDivOrMod* ins);
 
   public:
     // Unimplemented SIMD instructions
     void visitSimdSplatX4(LSimdSplatX4* lir) { MOZ_CRASH("NYI"); }
--- a/js/src/jit/mips32/Lowering-mips32.cpp
+++ b/js/src/jit/mips32/Lowering-mips32.cpp
@@ -168,8 +168,19 @@ LIRGeneratorMIPS::lowerTruncateDToInt32(
 void
 LIRGeneratorMIPS::lowerTruncateFToInt32(MTruncateToInt32* ins)
 {
     MDefinition* opd = ins->input();
     MOZ_ASSERT(opd->type() == MIRType_Float32);
 
     define(new(alloc()) LTruncateFToInt32(useRegister(opd), LDefinition::BogusTemp()), ins);
 }
+
+void
+LIRGeneratorMIPS::visitRandom(MRandom* ins)
+{
+    LRandom *lir = new(alloc()) LRandom(temp(),
+                                        temp(),
+                                        temp(),
+                                        temp(),
+                                        temp());
+    defineFixed(lir, ins, LFloatReg(ReturnDoubleReg));
+}
--- a/js/src/jit/mips32/MacroAssembler-mips32-inl.h
+++ b/js/src/jit/mips32/MacroAssembler-mips32-inl.h
@@ -57,16 +57,23 @@ MacroAssembler::andPtr(Register src, Reg
 
 void
 MacroAssembler::andPtr(Imm32 imm, Register dest)
 {
     ma_and(dest, imm);
 }
 
 void
+MacroAssembler::and64(Imm64 imm, Register64 dest)
+{
+    and32(Imm32(imm.value & LOW_32_MASK), dest.low);
+    and32(Imm32((imm.value >> 32) & LOW_32_MASK), dest.high);
+}
+
+void
 MacroAssembler::or32(Register src, Register dest)
 {
     ma_or(dest, src);
 }
 
 void
 MacroAssembler::or32(Imm32 imm, Register dest)
 {
@@ -89,16 +96,23 @@ MacroAssembler::orPtr(Register src, Regi
 
 void
 MacroAssembler::orPtr(Imm32 imm, Register dest)
 {
     ma_or(dest, imm);
 }
 
 void
+MacroAssembler::or64(Register64 src, Register64 dest)
+{
+    or32(src.low, dest.low);
+    or32(src.high, dest.high);
+}
+
+void
 MacroAssembler::xor32(Imm32 imm, Register dest)
 {
     ma_xor(dest, imm);
 }
 
 void
 MacroAssembler::xorPtr(Register src, Register dest)
 {
@@ -106,15 +120,56 @@ MacroAssembler::xorPtr(Register src, Reg
 }
 
 void
 MacroAssembler::xorPtr(Imm32 imm, Register dest)
 {
     ma_xor(dest, imm);
 }
 
+// ===============================================================
+// Shift functions
+
+void
+MacroAssembler::lshiftPtr(Imm32 imm, Register dest)
+{
+    ma_sll(dest, dest, imm);
+}
+
+void
+MacroAssembler::lshift64(Imm32 imm, Register64 dest)
+{
+    ScratchRegisterScope scratch(*this);
+    as_sll(dest.high, dest.high, imm.value);
+    as_srl(scratch, dest.low, 32 - imm.value);
+    as_or(dest.high, dest.high, scratch);
+    as_sll(dest.low, dest.low, imm.value);
+}
+
+void
+MacroAssembler::rshiftPtr(Imm32 imm, Register dest)
+{
+    ma_srl(dest, dest, imm);
+}
+
+void
+MacroAssembler::rshiftPtrArithmetic(Imm32 imm, Register dest)
+{
+    ma_sra(dest, dest, imm);
+}
+
+void
+MacroAssembler::rshift64(Imm32 imm, Register64 dest)
+{
+    ScratchRegisterScope scratch(*this);
+    as_srl(dest.low, dest.low, imm.value);
+    as_sll(scratch, dest.high, 32 - imm.value);
+    as_or(dest.low, dest.low, scratch);
+    as_srl(dest.high, dest.high, imm.value);
+}
+
 //}}} check_macroassembler_style
 // ===============================================================
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_mips32_MacroAssembler_mips32_inl_h */
--- a/js/src/jit/mips32/MacroAssembler-mips32.cpp
+++ b/js/src/jit/mips32/MacroAssembler-mips32.cpp
@@ -73,16 +73,73 @@ MacroAssemblerMIPSCompat::convertUInt32T
     as_cvtdw(dest, dest);
 
     // Add unsigned value of INT32_MIN
     ma_lid(SecondScratchDoubleReg, 2147483648.0);
     as_addd(dest, dest, SecondScratchDoubleReg);
 }
 
 void
+MacroAssemblerMIPSCompat::mul64(Imm64 imm, const Register64& dest)
+{
+    // LOW32  = LOW(LOW(dest) * LOW(imm));
+    // HIGH32 = LOW(HIGH(dest) * LOW(imm)) [multiply imm into upper bits]
+    //        + LOW(LOW(dest) * HIGH(imm)) [multiply dest into upper bits]
+    //        + HIGH(LOW(dest) * LOW(imm)) [carry]
+
+    // HIGH(dest) = LOW(HIGH(dest) * LOW(imm));
+    ma_li(ScratchRegister, Imm32(imm.value & LOW_32_MASK));
+    as_multu(dest.high, ScratchRegister);
+    as_mflo(dest.high);
+
+    // mfhi:mflo = LOW(dest) * LOW(imm);
+    as_multu(dest.low, ScratchRegister);
+
+    // HIGH(dest) += mfhi;
+    as_mfhi(ScratchRegister);
+    as_addu(dest.high, dest.high, ScratchRegister);
+
+    if (((imm.value >> 32) & LOW_32_MASK) == 5) {
+        // Optimized case for Math.random().
+
+        // HIGH(dest) += LOW(LOW(dest) * HIGH(imm));
+        as_sll(ScratchRegister, dest.low, 2);
+        as_addu(ScratchRegister, ScratchRegister, dest.low);
+        as_addu(dest.high, dest.high, ScratchRegister);
+
+        // LOW(dest) = mflo;
+        as_mflo(dest.low);
+    } else {
+        // tmp = mflo
+        as_mflo(SecondScratchReg);
+
+        // HIGH(dest) += LOW(LOW(dest) * HIGH(imm));
+        ma_li(ScratchRegister, Imm32((imm.value >> 32) & LOW_32_MASK));
+        as_multu(dest.low, ScratchRegister);
+        as_mflo(ScratchRegister);
+        as_addu(dest.high, dest.high, ScratchRegister);
+
+        // LOW(dest) = tmp;
+        ma_move(dest.low, SecondScratchReg);
+    }
+}
+
+static const double TO_DOUBLE_HIGH_SCALE = 0x100000000;
+
+void
+MacroAssemblerMIPSCompat::convertUInt64ToDouble(Register64 src, Register temp, FloatRegister dest)
+{
+    convertUInt32ToDouble(src.high, dest);
+    loadConstantDouble(TO_DOUBLE_HIGH_SCALE, ScratchDoubleReg);
+    mulDouble(ScratchDoubleReg, dest);
+    convertUInt32ToDouble(src.low, ScratchDoubleReg);
+    addDouble(ScratchDoubleReg, dest);
+}
+
+void
 MacroAssemblerMIPSCompat::convertUInt32ToFloat32(Register src, FloatRegister dest)
 {
     Label positive, done;
     ma_b(src, src, &positive, NotSigned, ShortJump);
 
     // We cannot do the same as convertUInt32ToDouble because float32 doesn't
     // have enough precision.
     convertUInt32ToDouble(src, dest);
@@ -2553,16 +2610,30 @@ MacroAssemblerMIPSCompat::branchTestDoub
 
 void
 MacroAssemblerMIPSCompat::branchTestBooleanTruthy(bool b, const ValueOperand& operand,
                                                   Label* label)
 {
     ma_b(operand.payloadReg(), operand.payloadReg(), label, b ? NonZero : Zero);
 }
 
+void
+MacroAssemblerMIPSCompat::branchTest64(Condition cond, Register64 lhs, Register64 rhs,
+                                       Register temp, Label* label)
+{
+    if (cond == Assembler::Zero) {
+        MOZ_ASSERT(lhs.low == rhs.low);
+        MOZ_ASSERT(lhs.high == rhs.high);
+        as_or(ScratchRegister, lhs.low, lhs.high);
+        branchTestPtr(cond, ScratchRegister, ScratchRegister, label);
+    } else {
+        MOZ_CRASH("Unsupported condition");
+    }
+}
+
 Register
 MacroAssemblerMIPSCompat::extractObject(const Address& address, Register scratch)
 {
     ma_lw(scratch, Address(address.base, address.offset + PAYLOAD_OFFSET));
     return scratch;
 }
 
 Register
--- a/js/src/jit/mips32/MacroAssembler-mips32.h
+++ b/js/src/jit/mips32/MacroAssembler-mips32.h
@@ -60,16 +60,20 @@ struct ImmType : public ImmTag
 
 static const ValueOperand JSReturnOperand = ValueOperand(JSReturnReg_Type, JSReturnReg_Data);
 static const ValueOperand softfpReturnOperand = ValueOperand(v1, v0);
 
 static Register CallReg = t9;
 static const int defaultShift = 3;
 static_assert(1 << defaultShift == sizeof(JS::Value), "The defaultShift is wrong");
 
+static const uint32_t LOW_32_MASK = (1LL << 32) - 1;
+static const int32_t LOW_32_OFFSET = 0;
+static const int32_t HIGH_32_OFFSET = 4;
+
 class MacroAssemblerMIPS : public Assembler
 {
   protected:
     // Perform a downcast. Should be removed by Bug 996602.
     MacroAssembler& asMasm();
     const MacroAssembler& asMasm() const;
 
     void branchWithCode(InstImm code, Label* label, JumpKind jumpKind);
@@ -671,16 +675,18 @@ class MacroAssemblerMIPSCompat : public 
     void branchTestPtr(Condition cond, Register lhs, const Imm32 rhs, Label* label) {
         ma_li(ScratchRegister, rhs);
         branchTestPtr(cond, lhs, ScratchRegister, label);
     }
     void branchTestPtr(Condition cond, const Address& lhs, Imm32 imm, Label* label) {
         loadPtr(lhs, SecondScratchReg);
         branchTestPtr(cond, SecondScratchReg, imm, label);
     }
+    void branchTest64(Condition cond, Register64 lhs, Register64 rhs, Register temp,
+                      Label* label);
     void branchPtr(Condition cond, Register lhs, Register rhs, Label* label) {
         ma_b(lhs, rhs, label, cond);
     }
     void branchPtr(Condition cond, Register lhs, ImmGCPtr ptr, Label* label) {
         ma_b(lhs, ptr, label, cond);
     }
     void branchPtr(Condition cond, Register lhs, ImmWord imm, Label* label) {
         ma_b(lhs, imm, label, cond);
@@ -1096,16 +1102,21 @@ class MacroAssemblerMIPSCompat : public 
     template <typename T, typename S>
     void atomicXor32(const T& value, const S& mem) {
         MOZ_CRASH("NYI");
     }
 
     void add32(Register src, Register dest);
     void add32(Imm32 imm, Register dest);
     void add32(Imm32 imm, const Address& dest);
+    void add64(Imm32 imm, Register64 dest) {
+        as_addiu(dest.low, dest.low, imm.value);
+        as_sltiu(ScratchRegister, dest.low, imm.value);
+        as_addu(dest.high, dest.high, ScratchRegister);
+    }
     void sub32(Imm32 imm, Register dest);
     void sub32(Register src, Register dest);
 
     void incrementInt32Value(const Address& addr) {
         add32(Imm32(1), ToPayload(addr));
     }
 
     template <typename T>
@@ -1135,16 +1146,20 @@ class MacroAssemblerMIPSCompat : public 
     }
 
     void addPtr(Register src, Register dest);
     void subPtr(Register src, Register dest);
     void addPtr(const Address& src, Register dest);
 
     void move32(Imm32 imm, Register dest);
     void move32(Register src, Register dest);
+    void move64(Register64 src, Register64 dest) {
+        move32(src.low, dest.low);
+        move32(src.high, dest.high);
+    }
 
     void movePtr(Register src, Register dest);
     void movePtr(ImmWord imm, Register dest);
     void movePtr(ImmPtr imm, Register dest);
     void movePtr(AsmJSImmPtr imm, Register dest);
     void movePtr(ImmGCPtr imm, Register dest);
 
     void load8SignExtend(const Address& address, Register dest);
@@ -1158,16 +1173,20 @@ class MacroAssemblerMIPSCompat : public 
 
     void load16ZeroExtend(const Address& address, Register dest);
     void load16ZeroExtend(const BaseIndex& src, Register dest);
 
     void load32(const Address& address, Register dest);
     void load32(const BaseIndex& address, Register dest);
     void load32(AbsoluteAddress address, Register dest);
     void load32(AsmJSAbsoluteAddress address, Register dest);
+    void load64(const Address& address, Register64 dest) {
+        load32(Address(address.base, address.offset + LOW_32_OFFSET), dest.low);
+        load32(Address(address.base, address.offset + HIGH_32_OFFSET), dest.high);
+    }
 
     void loadPtr(const Address& address, Register dest);
     void loadPtr(const BaseIndex& src, Register dest);
     void loadPtr(AbsoluteAddress address, Register dest);
     void loadPtr(AsmJSAbsoluteAddress address, Register dest);
 
     void loadPrivate(const Address& address, Register dest);
 
@@ -1228,16 +1247,21 @@ class MacroAssemblerMIPSCompat : public 
     void store32(Imm32 src, const BaseIndex& address);
 
     // NOTE: This will use second scratch on MIPS. Only ARM needs the
     // implementation without second scratch.
     void store32_NoSecondScratch(Imm32 src, const Address& address) {
         store32(src, address);
     }
 
+    void store64(Register64 src, Address address) {
+        store32(src.low, Address(address.base, address.offset + LOW_32_OFFSET));
+        store32(src.high, Address(address.base, address.offset + HIGH_32_OFFSET));
+    }
+
     template <typename T> void storePtr(ImmWord imm, T address);
     template <typename T> void storePtr(ImmPtr imm, T address);
     template <typename T> void storePtr(ImmGCPtr imm, T address);
     void storePtr(Register src, const Address& address);
     void storePtr(Register src, const BaseIndex& address);
     void storePtr(Register src, AbsoluteAddress dest);
     void storeDouble(FloatRegister src, Address addr) {
         ma_sd(src, addr);
@@ -1297,40 +1321,39 @@ class MacroAssemblerMIPSCompat : public 
     void addPtr(ImmPtr imm, const Register dest) {
         addPtr(ImmWord(uintptr_t(imm.value)), dest);
     }
     void mulBy3(const Register& src, const Register& dest) {
         as_addu(dest, src, src);
         as_addu(dest, dest, src);
     }
 
+    void mul64(Imm64 imm, const Register64& dest);
+
+    void convertUInt64ToDouble(Register64 src, Register temp, FloatRegister dest);
+    void mulDoublePtr(ImmPtr imm, Register temp, FloatRegister dest) {
+        movePtr(imm, ScratchRegister);
+        loadDouble(Address(ScratchRegister, 0), ScratchDoubleReg);
+        mulDouble(ScratchDoubleReg, dest);
+    }
+
     void breakpoint();
 
     void branchDouble(DoubleCondition cond, FloatRegister lhs, FloatRegister rhs,
                       Label* label);
 
     void branchFloat(DoubleCondition cond, FloatRegister lhs, FloatRegister rhs,
                      Label* label);
 
     void checkStackAlignment();
 
     void alignStackPointer();
     void restoreStackPointer();
     static void calculateAlignedStackPointer(void** stackPointer);
 
-    void rshiftPtr(Imm32 imm, Register dest) {
-        ma_srl(dest, dest, imm);
-    }
-    void rshiftPtrArithmetic(Imm32 imm, Register dest) {
-        ma_sra(dest, dest, imm);
-    }
-    void lshiftPtr(Imm32 imm, Register dest) {
-        ma_sll(dest, dest, imm);
-    }
-
     // If source is a double, load it into dest. If source is int32,
     // convert it to double. Else, branch to failure.
     void ensureDouble(const ValueOperand& source, FloatRegister dest, Label* failure);
 
     template <typename T1, typename T2>
     void cmpPtrSet(Assembler::Condition cond, T1 lhs, T2 rhs, Register dest)
     {
         ma_cmp_set(dest, lhs, rhs, cond);
--- a/js/src/jit/none/CodeGenerator-none.h
+++ b/js/src/jit/none/CodeGenerator-none.h
@@ -46,16 +46,17 @@ class CodeGeneratorNone : public CodeGen
     void testZeroEmitBranch(Assembler::Condition, Register, MBasicBlock*, MBasicBlock*) {
         MOZ_CRASH();
     }
     void emitTableSwitchDispatch(MTableSwitch*, Register, Register) { MOZ_CRASH(); }
     ValueOperand ToValue(LInstruction*, size_t) { MOZ_CRASH(); }
     ValueOperand ToOutValue(LInstruction*) { MOZ_CRASH(); }
     ValueOperand ToTempValue(LInstruction*, size_t) { MOZ_CRASH(); }
     void generateInvalidateEpilogue() { MOZ_CRASH(); }
+    void setReturnDoubleRegs(LiveRegisterSet* regs) { MOZ_CRASH(); }
 };
 
 typedef CodeGeneratorNone CodeGeneratorSpecific;
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_none_CodeGenerator_none_h */
--- a/js/src/jit/none/LIR-none.h
+++ b/js/src/jit/none/LIR-none.h
@@ -107,14 +107,13 @@ class LModPowTwoI : public LInstructionH
     int32_t shift() { MOZ_CRASH(); }
     LModPowTwoI(const LAllocation& lhs, int32_t shift) { MOZ_CRASH(); }
     MMod* mir() const { MOZ_CRASH(); }
 };
 
 class LGuardShape : public LInstruction {};
 class LGuardObjectGroup : public LInstruction {};
 class LMulI : public LInstruction {};
-class LRandom : public LInstructionHelper<1, 0, 5> {};
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_none_LIR_none_h */
--- a/js/src/jit/none/Lowering-none.h
+++ b/js/src/jit/none/Lowering-none.h
@@ -82,17 +82,16 @@ class LIRGeneratorNone : public LIRGener
 
     LTableSwitch* newLTableSwitch(LAllocation, LDefinition, MTableSwitch*) { MOZ_CRASH(); }
     LTableSwitchV* newLTableSwitchV(MTableSwitch*) { MOZ_CRASH(); }
     void visitSimdSelect(MSimdSelect* ins) { MOZ_CRASH(); }
     void visitSimdSplatX4(MSimdSplatX4* ins) { MOZ_CRASH(); }
     void visitSimdValueX4(MSimdValueX4* lir) { MOZ_CRASH(); }
     void visitSubstr(MSubstr*) { MOZ_CRASH(); }
     void visitSimdBinaryArith(js::jit::MSimdBinaryArith*) { MOZ_CRASH(); }
-    void visitRandom(MRandom* ) { MOZ_CRASH(); }
 
 };
 
 typedef LIRGeneratorNone LIRGeneratorSpecific;
 
 } // namespace jit
 } // namespace js
 
--- a/js/src/jit/none/MacroAssembler-none.h
+++ b/js/src/jit/none/MacroAssembler-none.h
@@ -417,19 +417,16 @@ class MacroAssemblerNone : public Assemb
     void branchTestBooleanTruthy(bool, ValueOperand, Label*) { MOZ_CRASH(); }
     void branchTestStringTruthy(bool, ValueOperand, Label*) { MOZ_CRASH(); }
     void branchTestDoubleTruthy(bool, FloatRegister, Label*) { MOZ_CRASH(); }
 
     template <typename T> void loadUnboxedValue(T, MIRType, AnyRegister) { MOZ_CRASH(); }
     template <typename T> void storeUnboxedValue(ConstantOrRegister, MIRType, T, MIRType) { MOZ_CRASH(); }
     template <typename T> void storeUnboxedPayload(ValueOperand value, T, size_t) { MOZ_CRASH(); }
 
-    void rshiftPtr(Imm32, Register) { MOZ_CRASH(); }
-    void rshiftPtrArithmetic(Imm32, Register) { MOZ_CRASH(); }
-    void lshiftPtr(Imm32, Register) { MOZ_CRASH(); }
     void convertUInt32ToDouble(Register, FloatRegister) { MOZ_CRASH(); }
     void convertUInt32ToFloat32(Register, FloatRegister) { MOZ_CRASH(); }
     void inc64(AbsoluteAddress) { MOZ_CRASH(); }
     void incrementInt32Value(Address) { MOZ_CRASH(); }
     void ensureDouble(ValueOperand, FloatRegister, Label*) { MOZ_CRASH(); }
     void handleFailureWithHandlerTail(void*) { MOZ_CRASH(); }
 
     void branchPtrInNurseryRange(Condition, Register, Register, Label*) { MOZ_CRASH(); }
--- a/js/src/jit/shared/Assembler-shared.h
+++ b/js/src/jit/shared/Assembler-shared.h
@@ -120,16 +120,25 @@ struct Imm32
 struct ImmWord
 {
     uintptr_t value;
 
     explicit ImmWord(uintptr_t value) : value(value)
     { }
 };
 
+// Used for 64-bit immediates which do not require relocation.
+struct Imm64
+{
+    uint64_t value;
+
+    explicit Imm64(uint64_t value) : value(value)
+    { }
+};
+
 #ifdef DEBUG
 static inline bool
 IsCompilingAsmJS()
 {
     // asm.js compilation pushes a JitContext with a null JSCompartment.
     JitContext* jctx = MaybeGetJitContext();
     return jctx && jctx->compartment == nullptr;
 }
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -7171,12 +7171,63 @@ class LArrowNewTarget : public LInstruct
 
     LIR_HEADER(ArrowNewTarget)
 
     const LAllocation* callee() {
         return getOperand(0);
     }
 };
 
+// Math.random().
+#ifdef JS_PUNBOX64
+# define LRANDOM_NUM_TEMPS 3
+#else
+# define LRANDOM_NUM_TEMPS 5
+#endif
+
+class LRandom : public LInstructionHelper<1, 0, LRANDOM_NUM_TEMPS>
+{
+  public:
+    LIR_HEADER(Random)
+    LRandom(const LDefinition &tempMaybeEAX, const LDefinition &tempMaybeEDX,
+            const LDefinition &temp1
+#ifndef JS_PUNBOX64
+            , const LDefinition &temp2, const LDefinition &temp3
+#endif
+            )
+    {
+        setTemp(0, tempMaybeEAX);
+        setTemp(1, tempMaybeEDX);
+        setTemp(2, temp1);
+#ifndef JS_PUNBOX64
+        setTemp(3, temp2);
+        setTemp(4, temp3);
+#endif
+    }
+    // On x86, following 2 methods return eax and edx necessary for mull.
+    // On others, following 2 methods return ordinary temporary registers.
+    const LDefinition* tempMaybeEAX() {
+        return getTemp(0);
+    }
+    const LDefinition* tempMaybeEDX() {
+        return getTemp(1);
+    }
+    const LDefinition *temp1() {
+        return getTemp(2);
+    }
+#ifndef JS_PUNBOX64
+    const LDefinition *temp2() {
+        return getTemp(3);
+    }
+    const LDefinition *temp3() {
+        return getTemp(4);
+    }
+#endif
+
+    MRandom* mir() const {
+        return mir_->toRandom();
+    }
+};
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_shared_LIR_shared_h */
new file mode 100644
--- /dev/null
+++ b/js/src/jit/x64/BaseAssembler-x64.h
@@ -0,0 +1,782 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef jit_x64_BaseAssembler_x64_h
+#define jit_x64_BaseAssembler_x64_h
+
+#include "jit/x86-shared/BaseAssembler-x86-shared.h"
+
+namespace js {
+namespace jit {
+
+namespace X86Encoding {
+
+class BaseAssemblerX64 : public BaseAssembler
+{
+  public:
+
+    // Arithmetic operations:
+
+    void addq_rr(RegisterID src, RegisterID dst)
+    {
+        spew("addq       %s, %s", GPReg64Name(src), GPReg64Name(dst));
+        m_formatter.oneByteOp64(OP_ADD_GvEv, src, dst);
+    }
+
+    void addq_mr(int32_t offset, RegisterID base, RegisterID dst)
+    {
+        spew("addq       " MEM_ob ", %s", ADDR_ob(offset, base), GPReg64Name(dst));
+        m_formatter.oneByteOp64(OP_ADD_GvEv, offset, base, dst);
+    }
+
+    void addq_mr(const void* addr, RegisterID dst)
+    {
+        spew("addq       %p, %s", addr, GPReg64Name(dst));
+        m_formatter.oneByteOp64(OP_ADD_GvEv, addr, dst);
+    }
+
+    void addq_ir(int32_t imm, RegisterID dst)
+    {
+        spew("addq       $%d, %s", imm, GPReg64Name(dst));
+        if (CAN_SIGN_EXTEND_8_32(imm)) {
+            m_formatter.oneByteOp64(OP_GROUP1_EvIb, dst, GROUP1_OP_ADD);
+            m_formatter.immediate8s(imm);
+        } else {
+            if (dst == rax)
+                m_formatter.oneByteOp64(OP_ADD_EAXIv);
+            else
+                m_formatter.oneByteOp64(OP_GROUP1_EvIz, dst, GROUP1_OP_ADD);
+            m_formatter.immediate32(imm);
+        }
+    }
+
+    void addq_im(int32_t imm, int32_t offset, RegisterID base)
+    {
+        spew("addq       $%d, " MEM_ob, imm, ADDR_ob(offset, base));
+        if (CAN_SIGN_EXTEND_8_32(imm)) {
+            m_formatter.oneByteOp64(OP_GROUP1_EvIb, offset, base, GROUP1_OP_ADD);
+            m_formatter.immediate8s(imm);
+        } else {
+            m_formatter.oneByteOp64(OP_GROUP1_EvIz, offset, base, GROUP1_OP_ADD);
+            m_formatter.immediate32(imm);
+        }
+    }
+
+    void addq_im(int32_t imm, const void* addr)
+    {
+        spew("addq       $%d, %p", imm, addr);
+        if (CAN_SIGN_EXTEND_8_32(imm)) {
+            m_formatter.oneByteOp64(OP_GROUP1_EvIb, addr, GROUP1_OP_ADD);
+            m_formatter.immediate8s(imm);
+        } else {
+            m_formatter.oneByteOp64(OP_GROUP1_EvIz, addr, GROUP1_OP_ADD);
+            m_formatter.immediate32(imm);
+        }
+    }
+
+    void andq_rr(RegisterID src, RegisterID dst)
+    {
+        spew("andq       %s, %s", GPReg64Name(src), GPReg64Name(dst));
+        m_formatter.oneByteOp64(OP_AND_GvEv, src, dst);
+    }
+
+    void andq_mr(int32_t offset, RegisterID base, RegisterID dst)
+    {
+        spew("andq       " MEM_ob ", %s", ADDR_ob(offset, base), GPReg64Name(dst));
+        m_formatter.oneByteOp64(OP_AND_GvEv, offset, base, dst);
+    }
+
+    void andq_mr(int32_t offset, RegisterID base, RegisterID index, int scale, RegisterID dst)
+    {
+        spew("andq       " MEM_obs ", %s", ADDR_obs(offset, base, index, scale), GPReg64Name(dst));
+        m_formatter.oneByteOp64(OP_AND_GvEv, offset, base, index, scale, dst);
+    }
+
+    void andq_mr(const void* addr, RegisterID dst)
+    {
+        spew("andq       %p, %s", addr, GPReg64Name(dst));
+        m_formatter.oneByteOp64(OP_AND_GvEv, addr, dst);
+    }
+
+    void orq_mr(int32_t offset, RegisterID base, RegisterID dst)
+    {
+        spew("orq        " MEM_ob ", %s", ADDR_ob(offset, base), GPReg64Name(dst));
+        m_formatter.oneByteOp64(OP_OR_GvEv, offset, base, dst);
+    }
+
+    void orq_mr(const void* addr, RegisterID dst)
+    {
+        spew("orq        %p, %s", addr, GPReg64Name(dst));
+        m_formatter.oneByteOp64(OP_OR_GvEv, addr, dst);
+    }
+
+    void andq_ir(int32_t imm, RegisterID dst)
+    {
+        spew("andq       $0x%" PRIx64 ", %s", int64_t(imm), GPReg64Name(dst));
+        if (CAN_SIGN_EXTEND_8_32(imm)) {
+            m_formatter.oneByteOp64(OP_GROUP1_EvIb, dst, GROUP1_OP_AND);
+            m_formatter.immediate8s(imm);
+        } else {
+            if (dst == rax)
+                m_formatter.oneByteOp64(OP_AND_EAXIv);
+            else
+                m_formatter.oneByteOp64(OP_GROUP1_EvIz, dst, GROUP1_OP_AND);
+            m_formatter.immediate32(imm);
+        }
+    }
+
+    void negq_r(RegisterID dst)
+    {
+        spew("negq       %s", GPReg64Name(dst));
+        m_formatter.oneByteOp64(OP_GROUP3_Ev, dst, GROUP3_OP_NEG);
+    }
+
+    void orq_rr(RegisterID src, RegisterID dst)
+    {
+        spew("orq        %s, %s", GPReg64Name(src), GPReg64Name(dst));
+        m_formatter.oneByteOp64(OP_OR_GvEv, src, dst);
+    }
+
+    void orq_ir(int32_t imm, RegisterID dst)
+    {
+        spew("orq        $0x%" PRIx64 ", %s", int64_t(imm), GPReg64Name(dst));
+        if (CAN_SIGN_EXTEND_8_32(imm)) {
+            m_formatter.oneByteOp64(OP_GROUP1_EvIb, dst, GROUP1_OP_OR);
+            m_formatter.immediate8s(imm);
+        } else {
+            if (dst == rax)
+                m_formatter.oneByteOp64(OP_OR_EAXIv);
+            else
+                m_formatter.oneByteOp64(OP_GROUP1_EvIz, dst, GROUP1_OP_OR);
+            m_formatter.immediate32(imm);
+        }
+    }
+
+    void notq_r(RegisterID dst)
+    {
+        spew("notq       %s", GPReg64Name(dst));
+        m_formatter.oneByteOp64(OP_GROUP3_Ev, dst, GROUP3_OP_NOT);
+    }
+
+    void subq_rr(RegisterID src, RegisterID dst)
+    {
+        spew("subq       %s, %s", GPReg64Name(src), GPReg64Name(dst));
+        m_formatter.oneByteOp64(OP_SUB_GvEv, src, dst);
+    }
+
+    void subq_rm(RegisterID src, int32_t offset, RegisterID base)
+    {
+        spew("subq       %s, " MEM_ob, GPReg64Name(src), ADDR_ob(offset, base));
+        m_formatter.oneByteOp64(OP_SUB_EvGv, offset, base, src);
+    }
+
+    void subq_mr(int32_t offset, RegisterID base, RegisterID dst)
+    {
+        spew("subq       " MEM_ob ", %s", ADDR_ob(offset, base), GPReg64Name(dst));
+        m_formatter.oneByteOp64(OP_SUB_GvEv, offset, base, dst);
+    }
+
+    void subq_mr(const void* addr, RegisterID dst)
+    {
+        spew("subq       %p, %s", addr, GPReg64Name(dst));
+        m_formatter.oneByteOp64(OP_SUB_GvEv, addr, dst);
+    }
+
+    void subq_ir(int32_t imm, RegisterID dst)
+    {
+        spew("subq       $%d, %s", imm, GPReg64Name(dst));
+        if (CAN_SIGN_EXTEND_8_32(imm)) {
+            m_formatter.oneByteOp64(OP_GROUP1_EvIb, dst, GROUP1_OP_SUB);
+            m_formatter.immediate8s(imm);
+        } else {
+            if (dst == rax)
+                m_formatter.oneByteOp64(OP_SUB_EAXIv);
+            else
+                m_formatter.oneByteOp64(OP_GROUP1_EvIz, dst, GROUP1_OP_SUB);
+            m_formatter.immediate32(imm);
+        }
+    }
+
+    void xorq_rr(RegisterID src, RegisterID dst)
+    {
+        spew("xorq       %s, %s", GPReg64Name(src), GPReg64Name(dst));
+        m_formatter.oneByteOp64(OP_XOR_GvEv, src, dst);
+    }
+
+    void xorq_ir(int32_t imm, RegisterID dst)
+    {
+        spew("xorq       $0x%" PRIx64 ", %s", int64_t(imm), GPReg64Name(dst));
+        if (CAN_SIGN_EXTEND_8_32(imm)) {
+            m_formatter.oneByteOp64(OP_GROUP1_EvIb, dst, GROUP1_OP_XOR);
+            m_formatter.immediate8s(imm);
+        } else {
+            if (dst == rax)
+                m_formatter.oneByteOp64(OP_XOR_EAXIv);
+            else
+                m_formatter.oneByteOp64(OP_GROUP1_EvIz, dst, GROUP1_OP_XOR);
+            m_formatter.immediate32(imm);
+        }
+    }
+
+    void sarq_CLr(RegisterID dst)
+    {
+        spew("sarq       %%cl, %s", GPReg64Name(dst));
+        m_formatter.oneByteOp64(OP_GROUP2_EvCL, dst, GROUP2_OP_SAR);
+    }
+
+    void sarq_ir(int32_t imm, RegisterID dst)
+    {
+        MOZ_ASSERT(imm < 64);
+        spew("sarq       $%d, %s", imm, GPReg64Name(dst));
+        if (imm == 1)
+            m_formatter.oneByteOp64(OP_GROUP2_Ev1, dst, GROUP2_OP_SAR);
+        else {
+            m_formatter.oneByteOp64(OP_GROUP2_EvIb, dst, GROUP2_OP_SAR);
+            m_formatter.immediate8u(imm);
+        }
+    }
+
+    void shlq_ir(int32_t imm, RegisterID dst)
+    {
+        MOZ_ASSERT(imm < 64);
+        spew("shlq       $%d, %s", imm, GPReg64Name(dst));
+        if (imm == 1)
+            m_formatter.oneByteOp64(OP_GROUP2_Ev1, dst, GROUP2_OP_SHL);
+        else {
+            m_formatter.oneByteOp64(OP_GROUP2_EvIb, dst, GROUP2_OP_SHL);
+            m_formatter.immediate8u(imm);
+        }
+    }
+
+    void shrq_ir(int32_t imm, RegisterID dst)
+    {
+        MOZ_ASSERT(imm < 64);
+        spew("shrq       $%d, %s", imm, GPReg64Name(dst));
+        if (imm == 1)
+            m_formatter.oneByteOp64(OP_GROUP2_Ev1, dst, GROUP2_OP_SHR);
+        else {
+            m_formatter.oneByteOp64(OP_GROUP2_EvIb, dst, GROUP2_OP_SHR);
+            m_formatter.immediate8u(imm);
+        }
+    }
+
+    void imulq_rr(RegisterID src, RegisterID dst)
+    {
+        spew("imulq      %s, %s", GPReg64Name(src), GPReg64Name(dst));
+        m_formatter.twoByteOp64(OP2_IMUL_GvEv, src, dst);
+    }
+
+    // Comparisons:
+
+    void cmpq_rr(RegisterID rhs, RegisterID lhs)
+    {
+        spew("cmpq       %s, %s", GPReg64Name(rhs), GPReg64Name(lhs));
+        m_formatter.oneByteOp64(OP_CMP_GvEv, rhs, lhs);
+    }
+
+    void cmpq_rm(RegisterID rhs, int32_t offset, RegisterID base)
+    {
+        spew("cmpq       %s, " MEM_ob, GPReg64Name(rhs), ADDR_ob(offset, base));
+        m_formatter.oneByteOp64(OP_CMP_EvGv, offset, base, rhs);
+    }
+
+    void cmpq_mr(int32_t offset, RegisterID base, RegisterID lhs)
+    {
+        spew("cmpq       " MEM_ob ", %s", ADDR_ob(offset, base), GPReg64Name(lhs));
+        m_formatter.oneByteOp64(OP_CMP_GvEv, offset, base, lhs);
+    }
+
+    void cmpq_ir(int32_t rhs, RegisterID lhs)
+    {
+        if (rhs == 0) {
+            testq_rr(lhs, lhs);
+            return;
+        }
+
+        spew("cmpq       $0x%" PRIx64 ", %s", int64_t(rhs), GPReg64Name(lhs));
+        if (CAN_SIGN_EXTEND_8_32(rhs)) {
+            m_formatter.oneByteOp64(OP_GROUP1_EvIb, lhs, GROUP1_OP_CMP);
+            m_formatter.immediate8s(rhs);
+        } else {
+            if (lhs == rax)
+                m_formatter.oneByteOp64(OP_CMP_EAXIv);
+            else
+                m_formatter.oneByteOp64(OP_GROUP1_EvIz, lhs, GROUP1_OP_CMP);
+            m_formatter.immediate32(rhs);
+        }
+    }
+
+    void cmpq_im(int32_t rhs, int32_t offset, RegisterID base)
+    {
+        spew("cmpq       $0x%" PRIx64 ", " MEM_ob, int64_t(rhs), ADDR_ob(offset, base));
+        if (CAN_SIGN_EXTEND_8_32(rhs)) {
+            m_formatter.oneByteOp64(OP_GROUP1_EvIb, offset, base, GROUP1_OP_CMP);
+            m_formatter.immediate8s(rhs);
+        } else {
+            m_formatter.oneByteOp64(OP_GROUP1_EvIz, offset, base, GROUP1_OP_CMP);
+            m_formatter.immediate32(rhs);
+        }
+    }
+
+    void cmpq_im(int32_t rhs, int32_t offset, RegisterID base, RegisterID index, int scale)
+    {
+        spew("cmpq       $0x%x, " MEM_obs, rhs, ADDR_obs(offset, base, index, scale));
+        if (CAN_SIGN_EXTEND_8_32(rhs)) {
+            m_formatter.oneByteOp64(OP_GROUP1_EvIb, offset, base, index, scale, GROUP1_OP_CMP);
+            m_formatter.immediate8s(rhs);
+        } else {
+            m_formatter.oneByteOp64(OP_GROUP1_EvIz, offset, base, index, scale, GROUP1_OP_CMP);
+            m_formatter.immediate32(rhs);
+        }
+    }
+    void cmpq_im(int32_t rhs, const void* addr)
+    {
+        spew("cmpq       $0x%" PRIx64 ", %p", int64_t(rhs), addr);
+        if (CAN_SIGN_EXTEND_8_32(rhs)) {
+            m_formatter.oneByteOp64(OP_GROUP1_EvIb, addr, GROUP1_OP_CMP);
+            m_formatter.immediate8s(rhs);
+        } else {
+            m_formatter.oneByteOp64(OP_GROUP1_EvIz, addr, GROUP1_OP_CMP);
+            m_formatter.immediate32(rhs);
+        }
+    }
+    void cmpq_rm(RegisterID rhs, const void* addr)
+    {
+        spew("cmpq       %s, %p", GPReg64Name(rhs), addr);
+        m_formatter.oneByteOp64(OP_CMP_EvGv, addr, rhs);
+    }
+
+    void testq_rr(RegisterID rhs, RegisterID lhs)
+    {
+        spew("testq      %s, %s", GPReg64Name(rhs), GPReg64Name(lhs));
+        m_formatter.oneByteOp64(OP_TEST_EvGv, lhs, rhs);
+    }
+
+    void testq_ir(int32_t rhs, RegisterID lhs)
+    {
+        // If the mask fits in a 32-bit immediate, we can use testl with a
+        // 32-bit subreg.
+        if (CAN_ZERO_EXTEND_32_64(rhs)) {
+            testl_ir(rhs, lhs);
+            return;
+        }
+        spew("testq      $0x%" PRIx64 ", %s", int64_t(rhs), GPReg64Name(lhs));
+        if (lhs == rax)
+            m_formatter.oneByteOp64(OP_TEST_EAXIv);
+        else
+            m_formatter.oneByteOp64(OP_GROUP3_EvIz, lhs, GROUP3_OP_TEST);
+        m_formatter.immediate32(rhs);
+    }
+
+    void testq_i32m(int32_t rhs, int32_t offset, RegisterID base)
+    {
+        spew("testq      $0x%" PRIx64 ", " MEM_ob, int64_t(rhs), ADDR_ob(offset, base));
+        m_formatter.oneByteOp64(OP_GROUP3_EvIz, offset, base, GROUP3_OP_TEST);
+        m_formatter.immediate32(rhs);
+    }
+
+    void testq_i32m(int32_t rhs, int32_t offset, RegisterID base, RegisterID index, int scale)
+    {
+        spew("testq      $0x%4x, " MEM_obs, rhs, ADDR_obs(offset, base, index, scale));
+        m_formatter.oneByteOp64(OP_GROUP3_EvIz, offset, base, index, scale, GROUP3_OP_TEST);
+        m_formatter.immediate32(rhs);
+    }
+
+    // Various move ops:
+
+    void xchgq_rr(RegisterID src, RegisterID dst)
+    {
+        spew("xchgq      %s, %s", GPReg64Name(src), GPReg64Name(dst));
+        m_formatter.oneByteOp64(OP_XCHG_GvEv, src, dst);
+    }
+    void xchgq_rm(RegisterID src, int32_t offset, RegisterID base)
+    {
+        spew("xchgq      %s, " MEM_ob, GPReg64Name(src), ADDR_ob(offset, base));
+        m_formatter.oneByteOp64(OP_XCHG_GvEv, offset, base, src);
+    }
+    void xchgq_rm(RegisterID src, int32_t offset, RegisterID base, RegisterID index, int scale)
+    {
+        spew("xchgq      %s, " MEM_obs, GPReg64Name(src), ADDR_obs(offset, base, index, scale));
+        m_formatter.oneByteOp64(OP_XCHG_GvEv, offset, base, index, scale, src);
+    }
+
+    void movq_rr(RegisterID src, RegisterID dst)
+    {
+        spew("movq       %s, %s", GPReg64Name(src), GPReg64Name(dst));
+        m_formatter.oneByteOp64(OP_MOV_GvEv, src, dst);
+    }
+
+    void movq_rm(RegisterID src, int32_t offset, RegisterID base)
+    {
+        spew("movq       %s, " MEM_ob, GPReg64Name(src), ADDR_ob(offset, base));
+        m_formatter.oneByteOp64(OP_MOV_EvGv, offset, base, src);
+    }
+
+    void movq_rm_disp32(RegisterID src, int32_t offset, RegisterID base)
+    {
+        spew("movq       %s, " MEM_o32b, GPReg64Name(src), ADDR_o32b(offset, base));
+        m_formatter.oneByteOp64_disp32(OP_MOV_EvGv, offset, base, src);
+    }
+
+    void movq_rm(RegisterID src, int32_t offset, RegisterID base, RegisterID index, int scale)
+    {
+        spew("movq       %s, " MEM_obs, GPReg64Name(src), ADDR_obs(offset, base, index, scale));
+        m_formatter.oneByteOp64(OP_MOV_EvGv, offset, base, index, scale, src);
+    }
+
+    void movq_rm(RegisterID src, const void* addr)
+    {
+        if (src == rax && !IsAddressImmediate(addr)) {
+            movq_EAXm(addr);
+            return;
+        }
+
+        spew("movq       %s, %p", GPReg64Name(src), addr);
+        m_formatter.oneByteOp64(OP_MOV_EvGv, addr, src);
+    }
+
+    void movq_mEAX(const void* addr)
+    {
+        if (IsAddressImmediate(addr)) {
+            movq_mr(addr, rax);
+            return;
+        }
+
+        spew("movq       %p, %%rax", addr);
+        m_formatter.oneByteOp64(OP_MOV_EAXOv);
+        m_formatter.immediate64(reinterpret_cast<int64_t>(addr));
+    }
+
+    void movq_EAXm(const void* addr)
+    {
+        if (IsAddressImmediate(addr)) {
+            movq_rm(rax, addr);
+            return;
+        }
+
+        spew("movq       %%rax, %p", addr);
+        m_formatter.oneByteOp64(OP_MOV_OvEAX);
+        m_formatter.immediate64(reinterpret_cast<int64_t>(addr));
+    }
+
+    void movq_mr(int32_t offset, RegisterID base, RegisterID dst)
+    {
+        spew("movq       " MEM_ob ", %s", ADDR_ob(offset, base), GPReg64Name(dst));
+        m_formatter.oneByteOp64(OP_MOV_GvEv, offset, base, dst);
+    }
+
+    void movq_mr_disp32(int32_t offset, RegisterID base, RegisterID dst)
+    {
+        spew("movq       " MEM_o32b ", %s", ADDR_o32b(offset, base), GPReg64Name(dst));
+        m_formatter.oneByteOp64_disp32(OP_MOV_GvEv, offset, base, dst);
+    }
+
+    void movq_mr(int32_t offset, RegisterID base, RegisterID index, int scale, RegisterID dst)
+    {
+        spew("movq       " MEM_obs ", %s", ADDR_obs(offset, base, index, scale), GPReg64Name(dst));
+        m_formatter.oneByteOp64(OP_MOV_GvEv, offset, base, index, scale, dst);
+    }
+
+    void movq_mr(const void* addr, RegisterID dst)
+    {
+        if (dst == rax && !IsAddressImmediate(addr)) {
+            movq_mEAX(addr);
+            return;
+        }
+
+        spew("movq       %p, %s", addr, GPReg64Name(dst));
+        m_formatter.oneByteOp64(OP_MOV_GvEv, addr, dst);
+    }
+
+    void leaq_mr(int32_t offset, RegisterID base, RegisterID index, int scale, RegisterID dst)
+    {
+        spew("leaq       " MEM_obs ", %s", ADDR_obs(offset, base, index, scale), GPReg64Name(dst)),
+        m_formatter.oneByteOp64(OP_LEA, offset, base, index, scale, dst);
+    }
+
+    void movq_i32m(int32_t imm, int32_t offset, RegisterID base)
+    {
+        spew("movq       $%d, " MEM_ob, imm, ADDR_ob(offset, base));
+        m_formatter.oneByteOp64(OP_GROUP11_EvIz, offset, base, GROUP11_MOV);
+        m_formatter.immediate32(imm);
+    }
+
+    void movq_i32m(int32_t imm, int32_t offset, RegisterID base, RegisterID index, int scale)
+    {
+        spew("movq       $%d, " MEM_obs, imm, ADDR_obs(offset, base, index, scale));
+        m_formatter.oneByteOp64(OP_GROUP11_EvIz, offset, base, index, scale, GROUP11_MOV);
+        m_formatter.immediate32(imm);
+    }
+    void movq_i32m(int32_t imm, const void* addr)
+    {
+        spew("movq       $%d, %p", imm, addr);
+        m_formatter.oneByteOp64(OP_GROUP11_EvIz, addr, GROUP11_MOV);
+        m_formatter.immediate32(imm);
+    }
+
+    // Note that this instruction sign-extends its 32-bit immediate field to 64
+    // bits and loads the 64-bit value into a 64-bit register.
+    //
+    // Note also that this is similar to the movl_i32r instruction, except that
+    // movl_i32r *zero*-extends its 32-bit immediate, and it has smaller code
+    // size, so it's preferred for values which could use either.
+    void movq_i32r(int32_t imm, RegisterID dst)
+    {
+        spew("movq       $%d, %s", imm, GPRegName(dst));
+        m_formatter.oneByteOp64(OP_GROUP11_EvIz, dst, GROUP11_MOV);
+        m_formatter.immediate32(imm);
+    }
+
+    void movq_i64r(int64_t imm, RegisterID dst)
+    {
+        spew("movabsq    $0x%" PRIx64 ", %s", imm, GPReg64Name(dst));
+        m_formatter.oneByteOp64(OP_MOV_EAXIv, dst);
+        m_formatter.immediate64(imm);
+    }
+
+    void movslq_rr(RegisterID src, RegisterID dst)
+    {
+        spew("movslq     %s, %s", GPReg32Name(src), GPReg64Name(dst));
+        m_formatter.oneByteOp64(OP_MOVSXD_GvEv, src, dst);
+    }
+    void movslq_mr(int32_t offset, RegisterID base, RegisterID dst)
+    {
+        spew("movslq     " MEM_ob ", %s", ADDR_ob(offset, base), GPReg64Name(dst));
+        m_formatter.oneByteOp64(OP_MOVSXD_GvEv, offset, base, dst);
+    }
+    void movslq_mr(int32_t offset, RegisterID base, RegisterID index, int scale, RegisterID dst)
+    {
+        spew("movslq     " MEM_obs ", %s", ADDR_obs(offset, base, index, scale), GPReg64Name(dst));
+        m_formatter.oneByteOp64(OP_MOVSXD_GvEv, offset, base, index, scale, dst);
+    }
+
+    MOZ_WARN_UNUSED_RESULT JmpSrc
+    movl_ripr(RegisterID dst)
+    {
+        m_formatter.oneByteRipOp(OP_MOV_GvEv, 0, (RegisterID)dst);
+        JmpSrc label(m_formatter.size());
+        spew("movl       " MEM_o32r ", %s", ADDR_o32r(label.offset()), GPReg32Name(dst));
+        return label;
+    }
+
+    MOZ_WARN_UNUSED_RESULT JmpSrc
+    movl_rrip(RegisterID src)
+    {
+        m_formatter.oneByteRipOp(OP_MOV_EvGv, 0, (RegisterID)src);
+        JmpSrc label(m_formatter.size());
+        spew("movl       %s, " MEM_o32r "", GPReg32Name(src), ADDR_o32r(label.offset()));
+        return label;
+    }
+
+    MOZ_WARN_UNUSED_RESULT JmpSrc
+    movq_ripr(RegisterID dst)
+    {
+        m_formatter.oneByteRipOp64(OP_MOV_GvEv, 0, dst);
+        JmpSrc label(m_formatter.size());
+        spew("movq       " MEM_o32r ", %s", ADDR_o32r(label.offset()), GPRegName(dst));
+        return label;
+    }
+
+    void leaq_mr(int32_t offset, RegisterID base, RegisterID dst)
+    {
+        spew("leaq       " MEM_ob ", %s", ADDR_ob(offset, base), GPReg64Name(dst));
+        m_formatter.oneByteOp64(OP_LEA, offset, base, dst);
+    }
+
+    MOZ_WARN_UNUSED_RESULT JmpSrc
+    leaq_rip(RegisterID dst)
+    {
+        m_formatter.oneByteRipOp64(OP_LEA, 0, dst);
+        JmpSrc label(m_formatter.size());
+        spew("leaq       " MEM_o32r ", %s", ADDR_o32r(label.offset()), GPRegName(dst));
+        return label;
+    }
+
+    // Flow control:
+
+    void jmp_rip(int ripOffset)
+    {
+        // rip-relative addressing.
+        spew("jmp        *%d(%%rip)", ripOffset);
+        m_formatter.oneByteRipOp(OP_GROUP5_Ev, ripOffset, GROUP5_OP_JMPN);
+    }
+
+    void immediate64(int64_t imm)
+    {
+        spew(".quad      %lld", (long long)imm);
+        m_formatter.immediate64(imm);
+    }
+
+    // SSE operations:
+
+    void vcvtsq2sd_rr(RegisterID src1, XMMRegisterID src0, XMMRegisterID dst)
+    {
+        twoByteOpInt64Simd("vcvtsi2sd", VEX_SD, OP2_CVTSI2SD_VsdEd, src1, src0, dst);
+    }
+    void vcvtsq2ss_rr(RegisterID src1, XMMRegisterID src0, XMMRegisterID dst)
+    {
+        twoByteOpInt64Simd("vcvtsi2ss", VEX_SS, OP2_CVTSI2SD_VsdEd, src1, src0, dst);
+    }
+
+    void vcvtsi2sdq_rr(RegisterID src, XMMRegisterID dst)
+    {
+        twoByteOpInt64Simd("vcvtsi2sdq", VEX_SD, OP2_CVTSI2SD_VsdEd, src, invalid_xmm, dst);
+    }
+
+    void vcvttsd2sq_rr(XMMRegisterID src, RegisterID dst)
+    {
+        twoByteOpSimdInt64("vcvttsd2si", VEX_SD, OP2_CVTTSD2SI_GdWsd, src, dst);
+    }
+
+    void vcvttss2sq_rr(XMMRegisterID src, RegisterID dst)
+    {
+        twoByteOpSimdInt64("vcvttss2si", VEX_SS, OP2_CVTTSD2SI_GdWsd, src, dst);
+    }
+
+    void vmovq_rr(XMMRegisterID src, RegisterID dst)
+    {
+        // While this is called "vmovq", it actually uses the vmovd encoding
+        // with a REX prefix modifying it to be 64-bit.
+        twoByteOpSimdInt64("vmovq", VEX_PD, OP2_MOVD_EdVd, (XMMRegisterID)dst, (RegisterID)src);
+    }
+
+    void vmovq_rr(RegisterID src, XMMRegisterID dst)
+    {
+        // While this is called "vmovq", it actually uses the vmovd encoding
+        // with a REX prefix modifying it to be 64-bit.
+        twoByteOpInt64Simd("vmovq", VEX_PD, OP2_MOVD_VdEd, src, invalid_xmm, dst);
+    }
+
+    MOZ_WARN_UNUSED_RESULT JmpSrc
+    vmovsd_ripr(XMMRegisterID dst)
+    {
+        return twoByteRipOpSimd("vmovsd", VEX_SD, OP2_MOVSD_VsdWsd, invalid_xmm, dst);
+    }
+    MOZ_WARN_UNUSED_RESULT JmpSrc
+    vmovss_ripr(XMMRegisterID dst)
+    {
+        return twoByteRipOpSimd("vmovss", VEX_SS, OP2_MOVSD_VsdWsd, invalid_xmm, dst);
+    }
+    MOZ_WARN_UNUSED_RESULT JmpSrc
+    vmovsd_rrip(XMMRegisterID src)
+    {
+        return twoByteRipOpSimd("vmovsd", VEX_SD, OP2_MOVSD_WsdVsd, invalid_xmm, src);
+    }
+    MOZ_WARN_UNUSED_RESULT JmpSrc
+    vmovss_rrip(XMMRegisterID src)
+    {
+        return twoByteRipOpSimd("vmovss", VEX_SS, OP2_MOVSD_WsdVsd, invalid_xmm, src);
+    }
+    MOZ_WARN_UNUSED_RESULT JmpSrc
+    vmovdqa_rrip(XMMRegisterID src)
+    {
+        return twoByteRipOpSimd("vmovdqa", VEX_PD, OP2_MOVDQ_WdqVdq, invalid_xmm, src);
+    }
+    MOZ_WARN_UNUSED_RESULT JmpSrc
+    vmovaps_rrip(XMMRegisterID src)
+    {
+        return twoByteRipOpSimd("vmovdqa", VEX_PS, OP2_MOVAPS_WsdVsd, invalid_xmm, src);
+    }
+
+    MOZ_WARN_UNUSED_RESULT JmpSrc
+    vmovaps_ripr(XMMRegisterID dst)
+    {
+        return twoByteRipOpSimd("vmovaps", VEX_PS, OP2_MOVAPS_VsdWsd, invalid_xmm, dst);
+    }
+
+    MOZ_WARN_UNUSED_RESULT JmpSrc
+    vmovdqa_ripr(XMMRegisterID dst)
+    {
+        return twoByteRipOpSimd("vmovdqa", VEX_PD, OP2_MOVDQ_VdqWdq, invalid_xmm, dst);
+    }
+
+  private:
+
+    MOZ_WARN_UNUSED_RESULT JmpSrc
+    twoByteRipOpSimd(const char* name, VexOperandType ty, TwoByteOpcodeID opcode,
+                     XMMRegisterID src0, XMMRegisterID dst)
+    {
+        if (useLegacySSEEncoding(src0, dst)) {
+            m_formatter.legacySSEPrefix(ty);
+            m_formatter.twoByteRipOp(opcode, 0, dst);
+            JmpSrc label(m_formatter.size());
+            if (IsXMMReversedOperands(opcode))
+                spew("%-11s%s, " MEM_o32r "", legacySSEOpName(name), XMMRegName(dst), ADDR_o32r(label.offset()));
+            else
+                spew("%-11s" MEM_o32r ", %s", legacySSEOpName(name), ADDR_o32r(label.offset()), XMMRegName(dst));
+            return label;
+        }
+
+        m_formatter.twoByteRipOpVex(ty, opcode, 0, src0, dst);
+        JmpSrc label(m_formatter.size());
+        if (src0 == invalid_xmm) {
+            if (IsXMMReversedOperands(opcode))
+                spew("%-11s%s, " MEM_o32r "", name, XMMRegName(dst), ADDR_o32r(label.offset()));
+            else
+                spew("%-11s" MEM_o32r ", %s", name, ADDR_o32r(label.offset()), XMMRegName(dst));
+        } else {
+            spew("%-11s" MEM_o32r ", %s, %s", name, ADDR_o32r(label.offset()), XMMRegName(src0), XMMRegName(dst));
+        }
+        return label;
+    }
+
+    void twoByteOpInt64Simd(const char* name, VexOperandType ty, TwoByteOpcodeID opcode,
+                            RegisterID rm, XMMRegisterID src0, XMMRegisterID dst)
+    {
+        if (useLegacySSEEncoding(src0, dst)) {
+            if (IsXMMReversedOperands(opcode))
+                spew("%-11s%s, %s", legacySSEOpName(name), XMMRegName(dst), GPRegName(rm));
+            else
+                spew("%-11s%s, %s", legacySSEOpName(name), GPRegName(rm), XMMRegName(dst));
+            m_formatter.legacySSEPrefix(ty);
+            m_formatter.twoByteOp64(opcode, rm, dst);
+            return;
+        }
+
+        if (src0 == invalid_xmm) {
+            if (IsXMMReversedOperands(opcode))
+                spew("%-11s%s, %s", name, XMMRegName(dst), GPRegName(rm));
+            else
+                spew("%-11s%s, %s", name, GPRegName(rm), XMMRegName(dst));
+        } else {
+            spew("%-11s%s, %s, %s", name, GPRegName(rm), XMMRegName(src0), XMMRegName(dst));
+        }
+        m_formatter.twoByteOpVex64(ty, opcode, rm, src0, dst);
+    }
+
+    void twoByteOpSimdInt64(const char* name, VexOperandType ty, TwoByteOpcodeID opcode,
+                            XMMRegisterID rm, RegisterID dst)
+    {
+        if (useLegacySSEEncodingForOtherOutput()) {
+            if (IsXMMReversedOperands(opcode))
+                spew("%-11s%s, %s", legacySSEOpName(name), GPRegName(dst), XMMRegName(rm));
+            else if (opcode == OP2_MOVD_EdVd)
+                spew("%-11s%s, %s", legacySSEOpName(name), XMMRegName((XMMRegisterID)dst), GPRegName((RegisterID)rm));
+            else
+                spew("%-11s%s, %s", legacySSEOpName(name), XMMRegName(rm), GPRegName(dst));
+            m_formatter.legacySSEPrefix(ty);
+            m_formatter.twoByteOp64(opcode, (RegisterID)rm, dst);
+            return;
+        }
+
+        if (IsXMMReversedOperands(opcode))
+            spew("%-11s%s, %s", name, GPRegName(dst), XMMRegName(rm));
+        else if (opcode == OP2_MOVD_EdVd)
+            spew("%-11s%s, %s", name, XMMRegName((XMMRegisterID)dst), GPRegName((RegisterID)rm));
+        else
+            spew("%-11s%s, %s", name, XMMRegName(rm), GPRegName(dst));
+        m_formatter.twoByteOpVex64(ty, opcode, (RegisterID)rm, invalid_xmm, (XMMRegisterID)dst);
+    }
+};
+
+typedef BaseAssemblerX64 BaseAssemblerSpecific;
+
+} // namespace X86Encoding
+
+} // namespace jit
+} // namespace js
+
+#endif /* jit_x64_BaseAssembler_x64_h */
--- a/js/src/jit/x64/CodeGenerator-x64.cpp
+++ b/js/src/jit/x64/CodeGenerator-x64.cpp
@@ -836,134 +836,8 @@ CodeGeneratorX64::visitTruncateFToInt32(
     FloatRegister input = ToFloatRegister(ins->input());
     Register output = ToRegister(ins->output());
 
     // On x64, branchTruncateFloat32 uses vcvttss2sq. Unlike the x86
     // implementation, this should handle most floats and we can just
     // call a stub if it fails.
     emitTruncateFloat32(input, output, ins->mir());
 }
-
-namespace js {
-namespace jit {
-
-// Out-of-line math_random_no_outparam call for LRandom.
-class OutOfLineRandom : public OutOfLineCodeBase<CodeGeneratorX64>
-{
-    LRandom* lir_;
-
-  public:
-    explicit OutOfLineRandom(LRandom* lir)
-      : lir_(lir)
-    { }
-
-    void accept(CodeGeneratorX64* codegen) {
-        codegen->visitOutOfLineRandom(this);
-    }
-
-    LRandom* lir() const {
-        return lir_;
-    }
-};
-
-} // namespace jit
-} // namespace js
-
-static const double RNG_DSCALE_INV = 1 / RNG_DSCALE;
-
-void
-CodeGeneratorX64::visitRandom(LRandom* ins)
-{
-    FloatRegister output = ToFloatRegister(ins->output());
-
-    Register JSCompartmentReg = ToRegister(ins->temp());
-    Register rngStateReg = ToRegister(ins->temp2());
-    Register highReg = ToRegister(ins->temp3());
-    Register lowReg = ToRegister(ins->temp4());
-    Register rngMaskReg = ToRegister(ins->temp5());
-
-    // rngState = cx->compartment()->rngState
-    masm.loadJSContext(JSCompartmentReg);
-    masm.loadPtr(Address(JSCompartmentReg, JSContext::offsetOfCompartment()), JSCompartmentReg);
-    masm.loadPtr(Address(JSCompartmentReg, JSCompartment::offsetOfRngState()), rngStateReg);
-
-    // if rngState == 0, escape from inlined code and call
-    // math_random_no_outparam.
-    OutOfLineRandom* ool = new(alloc()) OutOfLineRandom(ins);
-    addOutOfLineCode(ool, ins->mir());
-    masm.branchTestPtr(Assembler::Zero, rngStateReg, rngStateReg, ool->entry());
-
-    // nextstate = rngState * RNG_MULTIPLIER;
-    Register& rngMultiplierReg = lowReg;
-    masm.movq(ImmWord(RNG_MULTIPLIER), rngMultiplierReg);
-    masm.imulq(rngMultiplierReg, rngStateReg);
-
-    // nextstate += RNG_ADDEND;
-    masm.addq(Imm32(RNG_ADDEND), rngStateReg);
-
-    // nextstate &= RNG_MASK;
-    masm.movq(ImmWord(RNG_MASK), rngMaskReg);
-    masm.andq(rngMaskReg, rngStateReg);
-
-    // rngState = nextstate
-
-    // if rngState == 0, escape from inlined code and call
-    // math_random_no_outparam.
-    masm.j(Assembler::Zero, ool->entry());
-
-    // high = (nextstate >> (RNG_STATE_WIDTH - RNG_HIGH_BITS)) << RNG_LOW_BITS;
-    masm.movq(rngStateReg, highReg);
-    masm.shrq(Imm32(RNG_STATE_WIDTH - RNG_HIGH_BITS), highReg);
-    masm.shlq(Imm32(RNG_LOW_BITS), highReg);
-
-    // nextstate = rngState * RNG_MULTIPLIER;
-    masm.imulq(rngMultiplierReg, rngStateReg);
-
-    // nextstate += RNG_ADDEND;
-    masm.addq(Imm32(RNG_ADDEND), rngStateReg);
-
-    // nextstate &= RNG_MASK;
-    masm.andq(rngMaskReg, rngStateReg);
-
-    // low = nextstate >> (RNG_STATE_WIDTH - RNG_LOW_BITS);
-    masm.movq(rngStateReg, lowReg);
-    masm.shrq(Imm32(RNG_STATE_WIDTH - RNG_LOW_BITS), lowReg);
-
-    // output = double(high | low);
-    masm.orq(highReg, lowReg);
-    masm.vcvtsi2sdq(lowReg, output);
-
-    // output = output * RNG_DSCALE_INV;
-    Register& rngDscaleInvReg = lowReg;
-    masm.movq(ImmPtr(&RNG_DSCALE_INV), rngDscaleInvReg);
-    masm.vmulsd(Operand(rngDscaleInvReg, 0), output, output);
-
-    // cx->compartment()->rngState = nextstate
-    masm.storePtr(rngStateReg, Address(JSCompartmentReg, JSCompartment::offsetOfRngState()));
-
-    masm.bind(ool->rejoin());
-}
-
-void
-CodeGeneratorX64::visitOutOfLineRandom(OutOfLineRandom* ool)
-{
-    LRandom* ins = ool->lir();
-    Register temp = ToRegister(ins->temp());
-    Register temp2 = ToRegister(ins->temp2());
-    MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
-
-    LiveRegisterSet regs;
-    regs.add(ReturnFloat32Reg);
-    regs.add(ReturnDoubleReg);
-    regs.add(ReturnInt32x4Reg);
-    regs.add(ReturnFloat32x4Reg);
-    saveVolatile(regs);
-
-    masm.loadJSContext(temp);
-
-    masm.setupUnalignedABICall(temp2);
-    masm.passABIArg(temp);
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, math_random_no_outparam), MoveOp::DOUBLE);
-
-    restoreVolatile(regs);
-
-    masm.jump(ool->rejoin());
-}
--- a/js/src/jit/x64/CodeGenerator-x64.h
+++ b/js/src/jit/x64/CodeGenerator-x64.h
@@ -7,18 +7,16 @@
 #ifndef jit_x64_CodeGenerator_x64_h
 #define jit_x64_CodeGenerator_x64_h
 
 #include "jit/x86-shared/CodeGenerator-x86-shared.h"
 
 namespace js {
 namespace jit {
 
-class OutOfLineRandom;
-
 class CodeGeneratorX64 : public CodeGeneratorX86Shared
 {
     CodeGeneratorX64* thisFromCtor() {
         return this;
     }
 
   protected:
     ValueOperand ToValue(LInstruction* ins, size_t pos);
@@ -56,18 +54,16 @@ class CodeGeneratorX64 : public CodeGene
     void visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap* ins);
     void visitAsmJSAtomicBinopHeapForEffect(LAsmJSAtomicBinopHeapForEffect* ins);
     void visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar* ins);
     void visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar* ins);
     void visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr* ins);
     void visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc* ins);
     void visitAsmJSUInt32ToDouble(LAsmJSUInt32ToDouble* lir);
     void visitAsmJSUInt32ToFloat32(LAsmJSUInt32ToFloat32* lir);
-    void visitRandom(LRandom* ins);
-    void visitOutOfLineRandom(OutOfLineRandom* ool);
 };
 
 typedef CodeGeneratorX64 CodeGeneratorSpecific;
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_x64_CodeGenerator_x64_h */
--- a/js/src/jit/x64/LIR-x64.h
+++ b/js/src/jit/x64/LIR-x64.h
@@ -94,47 +94,12 @@ class LAsmJSLoadFuncPtr : public LInstru
     const LAllocation* index() {
         return getOperand(0);
     }
     const LDefinition* temp() {
         return getTemp(0);
     }
 };
 
-// Math.random().
-class LRandom : public LInstructionHelper<1, 0, 5>
-{
-  public:
-    LIR_HEADER(Random)
-    LRandom(const LDefinition &temp, const LDefinition &temp2, const LDefinition &temp3,
-            const LDefinition &temp4, const LDefinition &temp5)
-    {
-        setTemp(0, temp);
-        setTemp(1, temp2);
-        setTemp(2, temp3);
-        setTemp(3, temp4);
-        setTemp(4, temp5);
-    }
-    const LDefinition* temp() {
-        return getTemp(0);
-    }
-    const LDefinition* temp2() {
-        return getTemp(1);
-    }
-    const LDefinition *temp3() {
-        return getTemp(2);
-    }
-    const LDefinition *temp4() {
-        return getTemp(3);
-    }
-    const LDefinition *temp5() {
-        return getTemp(4);
-    }
-
-    MRandom* mir() const {
-        return mir_->toRandom();
-    }
-};
-
 } // namespace jit
 } // namespace js
 
 #endif /* jit_x64_LIR_x64_h */
--- a/js/src/jit/x64/Lowering-x64.cpp
+++ b/js/src/jit/x64/Lowering-x64.cpp
@@ -333,13 +333,11 @@ LIRGeneratorX64::visitStoreTypedArrayEle
     MOZ_CRASH("NYI");
 }
 
 void
 LIRGeneratorX64::visitRandom(MRandom* ins)
 {
     LRandom *lir = new(alloc()) LRandom(temp(),
                                         temp(),
-                                        temp(),
-                                        temp(),
                                         temp());
     defineFixed(lir, ins, LFloatReg(ReturnDoubleReg));
 }
--- a/js/src/jit/x64/MacroAssembler-x64-inl.h
+++ b/js/src/jit/x64/MacroAssembler-x64-inl.h
@@ -25,38 +25,84 @@ MacroAssembler::andPtr(Register src, Reg
 
 void
 MacroAssembler::andPtr(Imm32 imm, Register dest)
 {
     andq(imm, dest);
 }
 
 void
+MacroAssembler::and64(Imm64 imm, Register64 dest)
+{
+    movq(ImmWord(uintptr_t(imm.value)), ScratchReg);
+    andq(ScratchReg, dest.reg);
+}
+
+void
 MacroAssembler::orPtr(Register src, Register dest)
 {
     orq(src, dest);
 }
 
 void
 MacroAssembler::orPtr(Imm32 imm, Register dest)
 {
     orq(imm, dest);
 }
 
 void
+MacroAssembler::or64(Register64 src, Register64 dest)
+{
+    orq(src.reg, dest.reg);
+}
+
+void
 MacroAssembler::xorPtr(Register src, Register dest)
 {
     xorq(src, dest);
 }
 
 void
 MacroAssembler::xorPtr(Imm32 imm, Register dest)
 {
     xorq(imm, dest);
 }
 
+// ===============================================================
+// Shift functions
+
+void
+MacroAssembler::lshiftPtr(Imm32 imm, Register dest)
+{
+    shlq(imm, dest);
+}
+
+void
+MacroAssembler::lshift64(Imm32 imm, Register64 dest)
+{
+    shlq(imm, dest.reg);
+}
+
+void
+MacroAssembler::rshiftPtr(Imm32 imm, Register dest)
+{
+    shrq(imm, dest);
+}
+
+void
+MacroAssembler::rshiftPtrArithmetic(Imm32 imm, Register dest)
+{
+    sarq(imm, dest);
+}
+
+void
+MacroAssembler::rshift64(Imm32 imm, Register64 dest)
+{
+    shrq(imm, dest.reg);
+}
+
 //}}} check_macroassembler_style
 // ===============================================================
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_x64_MacroAssembler_x64_inl_h */
--- a/js/src/jit/x64/MacroAssembler-x64.cpp
+++ b/js/src/jit/x64/MacroAssembler-x64.cpp
@@ -179,16 +179,26 @@ MacroAssemblerX64::finish()
           default: MOZ_CRASH("unexpected SimdConstant type");
         }
     }
 
     MacroAssemblerX86Shared::finish();
 }
 
 void
+MacroAssemblerX64::branchPrivatePtr(Condition cond, Address lhs, Register ptr, Label* label)
+{
+    ScratchRegisterScope scratch(asMasm());
+    if (ptr != scratch)
+        movePtr(ptr, scratch);
+    asMasm().rshiftPtr(Imm32(1), scratch);
+    branchPtr(cond, lhs, scratch, label);
+}
+
+void
 MacroAssemblerX64::handleFailureWithHandlerTail(void* handler)
 {
     // Reserve space for exception information.
     subq(Imm32(sizeof(ResumeFromException)), rsp);
     movq(rsp, rax);
 
     // Call the handler.
     asMasm().setupUnalignedABICall(rcx);
--- a/js/src/jit/x64/MacroAssembler-x64.h
+++ b/js/src/jit/x64/MacroAssembler-x64.h
@@ -599,31 +599,38 @@ class MacroAssemblerX64 : public MacroAs
         }
     }
     void addPtr(ImmPtr imm, Register dest) {
         addPtr(ImmWord(uintptr_t(imm.value)), dest);
     }
     void addPtr(const Address& src, Register dest) {
         addq(Operand(src), dest);
     }
+    void add64(Imm32 imm, Register64 dest) {
+        addq(imm, dest.reg);
+    }
     void subPtr(Imm32 imm, Register dest) {
         subq(imm, dest);
     }
     void subPtr(Register src, Register dest) {
         subq(src, dest);
     }
     void subPtr(const Address& addr, Register dest) {
         subq(Operand(addr), dest);
     }
     void subPtr(Register src, const Address& dest) {
         subq(src, Operand(dest));
     }
     void mulBy3(const Register& src, const Register& dest) {
         lea(Operand(src, src, TimesTwo), dest);
     }
+    void mul64(Imm64 imm, const Register64& dest) {
+        movq(ImmWord(uintptr_t(imm.value)), ScratchReg);
+        imulq(ScratchReg, dest.reg);
+    }
 
     void branch32(Condition cond, AbsoluteAddress lhs, Imm32 rhs, Label* label) {
         if (X86Encoding::IsAddressImmediate(lhs.addr)) {
             branch32(cond, Operand(lhs), rhs, label);
         } else {
             ScratchRegisterScope scratch(asMasm());
             mov(ImmPtr(lhs.addr), scratch);
             branch32(cond, Address(scratch, 0), rhs, label);
@@ -680,24 +687,17 @@ class MacroAssemblerX64 : public MacroAs
         mov(AsmJSImmPtr(addr.kind()), scratch);
         branchPtr(cond, Operand(scratch, 0x0), ptr, label);
     }
 
     void branchPrivatePtr(Condition cond, Address lhs, ImmPtr ptr, Label* label) {
         branchPtr(cond, lhs, ImmWord(uintptr_t(ptr.value) >> 1), label);
     }
 
-    void branchPrivatePtr(Condition cond, Address lhs, Register ptr, Label* label) {
-        ScratchRegisterScope scratch(asMasm());
-        if (ptr != scratch)
-            movePtr(ptr, scratch);
-        rshiftPtr(Imm32(1), scratch);
-        branchPtr(cond, lhs, scratch, label);
-    }
-
+    void branchPrivatePtr(Condition cond, Address lhs, Register ptr, Label* label);
     template <typename T, typename S>
     void branchPtr(Condition cond, const T& lhs, const S& ptr, Label* label) {
         cmpPtr(Operand(lhs), ptr);
         j(cond, label);
     }
 
     CodeOffsetJump jumpWithPatch(RepatchLabel* label, Label* documentation = nullptr) {
         JmpSrc src = jmpSrc(label);
@@ -736,16 +736,20 @@ class MacroAssemblerX64 : public MacroAs
         testPtr(Operand(lhs), imm);
         j(cond, label);
     }
     void decBranchPtr(Condition cond, Register lhs, Imm32 imm, Label* label) {
         subPtr(imm, lhs);
         j(cond, label);
     }
 
+    void branchTest64(Condition cond, Register64 lhs, Register64 rhs, Register temp, Label* label) {
+        branchTestPtr(cond, lhs.reg, rhs.reg, label);
+    }
+
     void movePtr(Register src, Register dest) {
         movq(src, dest);
     }
     void movePtr(Register src, const Operand& dest) {
         movq(src, dest);
     }
     void movePtr(ImmWord imm, Register dest) {
         mov(imm, dest);
@@ -754,16 +758,19 @@ class MacroAssemblerX64 : public MacroAs
         mov(imm, dest);
     }
     void movePtr(AsmJSImmPtr imm, Register dest) {
         mov(imm, dest);
     }
     void movePtr(ImmGCPtr imm, Register dest) {
         movq(imm, dest);
     }
+    void move64(Register64 src, Register64 dest) {
+        movq(src.reg, dest.reg);
+    }
     void loadPtr(AbsoluteAddress address, Register dest) {
         if (X86Encoding::IsAddressImmediate(address.addr)) {
             movq(Operand(address), dest);
         } else {
             ScratchRegisterScope scratch(asMasm());
             mov(ImmPtr(address.addr), scratch);
             loadPtr(Address(scratch, 0x0), dest);
         }
@@ -785,16 +792,19 @@ class MacroAssemblerX64 : public MacroAs
         if (X86Encoding::IsAddressImmediate(address.addr)) {
             movl(Operand(address), dest);
         } else {
             ScratchRegisterScope scratch(asMasm());
             mov(ImmPtr(address.addr), scratch);
             load32(Address(scratch, 0x0), dest);
         }
     }
+    void load64(const Address& address, Register64 dest) {
+        movq(Operand(address), dest.reg);
+    }
     template <typename T>
     void storePtr(ImmWord imm, T address) {
         if ((intptr_t)imm.value <= INT32_MAX && (intptr_t)imm.value >= INT32_MIN) {
             movq(Imm32((int32_t)imm.value), Operand(address));
         } else {
             ScratchRegisterScope scratch(asMasm());
             mov(imm, scratch);
             movq(scratch, Operand(address));
@@ -832,24 +842,18 @@ class MacroAssemblerX64 : public MacroAs
         if (X86Encoding::IsAddressImmediate(address.addr)) {
             movl(src, Operand(address));
         } else {
             ScratchRegisterScope scratch(asMasm());
             mov(ImmPtr(address.addr), scratch);
             store32(src, Address(scratch, 0x0));
         }
     }
-    void rshiftPtr(Imm32 imm, Register dest) {
-        shrq(imm, dest);
-    }
-    void rshiftPtrArithmetic(Imm32 imm, Register dest) {
-        sarq(imm, dest);
-    }
-    void lshiftPtr(Imm32 imm, Register dest) {
-        shlq(imm, dest);
+    void store64(Register64 src, Address address) {
+        movq(src.reg, Operand(address));
     }
 
     void splitTag(Register src, Register dest) {
         if (src != dest)
             movq(src, dest);
         shrq(Imm32(JSVAL_TAG_SHIFT), dest);
     }
     void splitTag(const ValueOperand& operand, Register dest) {
@@ -1365,16 +1369,25 @@ class MacroAssemblerX64 : public MacroAs
     void convertUInt32ToDouble(Register src, FloatRegister dest) {
         vcvtsq2sd(src, dest, dest);
     }
 
     void convertUInt32ToFloat32(Register src, FloatRegister dest) {
         vcvtsq2ss(src, dest, dest);
     }
 
+    void convertUInt64ToDouble(Register64 src, Register temp, FloatRegister dest) {
+        vcvtsi2sdq(src.reg, dest);
+    }
+
+    void mulDoublePtr(ImmPtr imm, Register temp, FloatRegister dest) {
+        movq(imm, ScratchReg);
+        vmulsd(Operand(ScratchReg, 0), dest, dest);
+    }
+
     void inc64(AbsoluteAddress dest) {
         if (X86Encoding::IsAddressImmediate(dest.addr)) {
             addPtr(Imm32(1), Operand(dest));
         } else {
             ScratchRegisterScope scratch(asMasm());
             mov(ImmPtr(dest.addr), scratch);
             addPtr(Imm32(1), Address(scratch, 0));
         }
--- a/js/src/jit/x86-shared/Assembler-x86-shared.h
+++ b/js/src/jit/x86-shared/Assembler-x86-shared.h
@@ -5,17 +5,24 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jit_x86_shared_Assembler_x86_shared_h
 #define jit_x86_shared_Assembler_x86_shared_h
 
 #include <cstddef>
 
 #include "jit/shared/Assembler-shared.h"
-#include "jit/x86-shared/BaseAssembler-x86-shared.h"
+
+#if defined(JS_CODEGEN_X86)
+# include "jit/x86/BaseAssembler-x86.h"
+#elif defined(JS_CODEGEN_X64)
+# include "jit/x64/BaseAssembler-x64.h"
+#else
+# error "Unknown architecture!"
+#endif
 
 namespace js {
 namespace jit {
 
 struct ScratchFloat32Scope : public AutoFloatRegisterScope
 {
     explicit ScratchFloat32Scope(MacroAssembler& masm)
       : AutoFloatRegisterScope(masm, ScratchFloat32Reg)
@@ -256,17 +263,17 @@ class AssemblerX86Shared : public Assemb
             dataRelocations_.writeUnsigned(masm.currentOffset());
         }
     }
     void writePrebarrierOffset(CodeOffsetLabel label) {
         preBarriers_.writeUnsigned(label.offset());
     }
 
   protected:
-    X86Encoding::BaseAssembler masm;
+    X86Encoding::BaseAssemblerSpecific masm;
 
     typedef X86Encoding::JmpSrc JmpSrc;
     typedef X86Encoding::JmpDst JmpDst;
 
   public:
     AssemblerX86Shared()
     {
         if (!HasAVX())
--- a/js/src/jit/x86-shared/BaseAssembler-x86-shared.h
+++ b/js/src/jit/x86-shared/BaseAssembler-x86-shared.h
@@ -251,30 +251,16 @@ public:
     void pop_flags()
     {
         spew("popf");
         m_formatter.oneByteOp(OP_POPFLAGS);
     }
 
     // Arithmetic operations:
 
-#ifdef JS_CODEGEN_X86
-    void adcl_im(int32_t imm, const void* addr)
-    {
-        spew("adcl       %d, %p", imm, addr);
-        if (CAN_SIGN_EXTEND_8_32(imm)) {
-            m_formatter.oneByteOp(OP_GROUP1_EvIb, addr, GROUP1_OP_ADC);
-            m_formatter.immediate8s(imm);
-        } else {
-            m_formatter.oneByteOp(OP_GROUP1_EvIz, addr, GROUP1_OP_ADC);
-            m_formatter.immediate32(imm);
-        }
-    }
-#endif
-
     void addl_rr(RegisterID src, RegisterID dst)
     {
         spew("addl       %s, %s", GPReg32Name(src), GPReg32Name(dst));
         m_formatter.oneByteOp(OP_ADD_GvEv, src, dst);
     }
 
     void addw_rr(RegisterID src, RegisterID dst)
     {
@@ -354,74 +340,16 @@ public:
             m_formatter.oneByteOp(OP_GROUP1_EvIb, offset, base, index, scale, GROUP1_OP_ADD);
             m_formatter.immediate8s(imm);
         } else {
             m_formatter.oneByteOp(OP_GROUP1_EvIz, offset, base, index, scale, GROUP1_OP_ADD);
             m_formatter.immediate32(imm);
         }
     }
 
-#ifdef JS_CODEGEN_X64
-    void addq_rr(RegisterID src, RegisterID dst)
-    {
-        spew("addq       %s, %s", GPReg64Name(src), GPReg64Name(dst));
-        m_formatter.oneByteOp64(OP_ADD_GvEv, src, dst);
-    }
-
-    void addq_mr(int32_t offset, RegisterID base, RegisterID dst)
-    {
-        spew("addq       " MEM_ob ", %s", ADDR_ob(offset, base), GPReg64Name(dst));
-        m_formatter.oneByteOp64(OP_ADD_GvEv, offset, base, dst);
-    }
-
-    void addq_mr(const void* addr, RegisterID dst)
-    {
-        spew("addq       %p, %s", addr, GPReg64Name(dst));
-        m_formatter.oneByteOp64(OP_ADD_GvEv, addr, dst);
-    }
-
-    void addq_ir(int32_t imm, RegisterID dst)
-    {
-        spew("addq       $%d, %s", imm, GPReg64Name(dst));
-        if (CAN_SIGN_EXTEND_8_32(imm)) {
-            m_formatter.oneByteOp64(OP_GROUP1_EvIb, dst, GROUP1_OP_ADD);
-            m_formatter.immediate8s(imm);
-        } else {
-            if (dst == rax)
-                m_formatter.oneByteOp64(OP_ADD_EAXIv);
-            else
-                m_formatter.oneByteOp64(OP_GROUP1_EvIz, dst, GROUP1_OP_ADD);
-            m_formatter.immediate32(imm);
-        }
-    }
-
-    void addq_im(int32_t imm, int32_t offset, RegisterID base)
-    {
-        spew("addq       $%d, " MEM_ob, imm, ADDR_ob(offset, base));
-        if (CAN_SIGN_EXTEND_8_32(imm)) {
-            m_formatter.oneByteOp64(OP_GROUP1_EvIb, offset, base, GROUP1_OP_ADD);
-            m_formatter.immediate8s(imm);
-        } else {
-            m_formatter.oneByteOp64(OP_GROUP1_EvIz, offset, base, GROUP1_OP_ADD);
-            m_formatter.immediate32(imm);
-        }
-    }
-
-    void addq_im(int32_t imm, const void* addr)
-    {
-        spew("addq       $%d, %p", imm, addr);
-        if (CAN_SIGN_EXTEND_8_32(imm)) {
-            m_formatter.oneByteOp64(OP_GROUP1_EvIb, addr, GROUP1_OP_ADD);
-            m_formatter.immediate8s(imm);
-        } else {
-            m_formatter.oneByteOp64(OP_GROUP1_EvIz, addr, GROUP1_OP_ADD);
-            m_formatter.immediate32(imm);
-        }
-    }
-#endif
     void addl_im(int32_t imm, const void* addr)
     {
         spew("addl       $%d, %p", imm, addr);
         if (CAN_SIGN_EXTEND_8_32(imm)) {
             m_formatter.oneByteOp(OP_GROUP1_EvIb, addr, GROUP1_OP_ADD);
             m_formatter.immediate8s(imm);
         } else {
             m_formatter.oneByteOp(OP_GROUP1_EvIz, addr, GROUP1_OP_ADD);
@@ -864,81 +792,16 @@ public:
             m_formatter.oneByteOp(OP_GROUP1_EvIb, offset, base, index, scale, GROUP1_OP_AND);
             m_formatter.immediate8s(imm);
         } else {
             m_formatter.oneByteOp(OP_GROUP1_EvIz, offset, base, index, scale, GROUP1_OP_AND);
             m_formatter.immediate16(imm);
         }
     }
 
-#ifdef JS_CODEGEN_X64
-    void andq_rr(RegisterID src, RegisterID dst)
-    {
-        spew("andq       %s, %s", GPReg64Name(src), GPReg64Name(dst));
-        m_formatter.oneByteOp64(OP_AND_GvEv, src, dst);
-    }
-
-    void andq_mr(int32_t offset, RegisterID base, RegisterID dst)
-    {
-        spew("andq       " MEM_ob ", %s", ADDR_ob(offset, base), GPReg64Name(dst));
-        m_formatter.oneByteOp64(OP_AND_GvEv, offset, base, dst);
-    }
-
-    void andq_mr(int32_t offset, RegisterID base, RegisterID index, int scale, RegisterID dst)
-    {
-        spew("andq       " MEM_obs ", %s", ADDR_obs(offset, base, index, scale), GPReg64Name(dst));
-        m_formatter.oneByteOp64(OP_AND_GvEv, offset, base, index, scale, dst);
-    }
-
-    void andq_mr(const void* addr, RegisterID dst)
-    {
-        spew("andq       %p, %s", addr, GPReg64Name(dst));
-        m_formatter.oneByteOp64(OP_AND_GvEv, addr, dst);
-    }
-
-    void orq_mr(int32_t offset, RegisterID base, RegisterID dst)
-    {
-        spew("orq        " MEM_ob ", %s", ADDR_ob(offset, base), GPReg64Name(dst));
-        m_formatter.oneByteOp64(OP_OR_GvEv, offset, base, dst);
-    }
-
-    void orq_mr(const void* addr, RegisterID dst)
-    {
-        spew("orq        %p, %s", addr, GPReg64Name(dst));
-        m_formatter.oneByteOp64(OP_OR_GvEv, addr, dst);
-    }
-
-    void andq_ir(int32_t imm, RegisterID dst)
-    {
-        spew("andq       $0x%" PRIx64 ", %s", int64_t(imm), GPReg64Name(dst));
-        if (CAN_SIGN_EXTEND_8_32(imm)) {
-            m_formatter.oneByteOp64(OP_GROUP1_EvIb, dst, GROUP1_OP_AND);
-            m_formatter.immediate8s(imm);
-        } else {
-            if (dst == rax)
-                m_formatter.oneByteOp64(OP_AND_EAXIv);
-            else
-                m_formatter.oneByteOp64(OP_GROUP1_EvIz, dst, GROUP1_OP_AND);
-            m_formatter.immediate32(imm);
-        }
-    }
-#else
-    void andl_im(int32_t imm, const void* addr)
-    {
-        spew("andl       $0x%x, %p", imm, addr);
-        if (CAN_SIGN_EXTEND_8_32(imm)) {
-            m_formatter.oneByteOp(OP_GROUP1_EvIb, addr, GROUP1_OP_AND);
-            m_formatter.immediate8s(imm);
-        } else {
-            m_formatter.oneByteOp(OP_GROUP1_EvIz, addr, GROUP1_OP_AND);
-            m_formatter.immediate32(imm);
-        }
-    }
-#endif
-
     void fld_m(int32_t offset, RegisterID base)
     {
         spew("fld        " MEM_ob, ADDR_ob(offset, base));
         m_formatter.oneByteOp(OP_FPU6, offset, base, FPU6_OP_FLD);
     }
     void fld32_m(int32_t offset, RegisterID base)
     {
         spew("fld        " MEM_ob, ADDR_ob(offset, base));
@@ -1105,63 +968,16 @@ public:
             m_formatter.oneByteOp(OP_GROUP1_EvIb, offset, base, index, scale, GROUP1_OP_OR);
             m_formatter.immediate8s(imm);
         } else {
             m_formatter.oneByteOp(OP_GROUP1_EvIz, offset, base, index, scale, GROUP1_OP_OR);
             m_formatter.immediate16(imm);
         }
     }
 
-#ifdef JS_CODEGEN_X64
-    void negq_r(RegisterID dst)
-    {
-        spew("negq       %s", GPReg64Name(dst));
-        m_formatter.oneByteOp64(OP_GROUP3_Ev, dst, GROUP3_OP_NEG);
-    }
-
-    void orq_rr(RegisterID src, RegisterID dst)
-    {
-        spew("orq        %s, %s", GPReg64Name(src), GPReg64Name(dst));
-        m_formatter.oneByteOp64(OP_OR_GvEv, src, dst);
-    }
-
-    void orq_ir(int32_t imm, RegisterID dst)
-    {
-        spew("orq        $0x%" PRIx64 ", %s", int64_t(imm), GPReg64Name(dst));
-        if (CAN_SIGN_EXTEND_8_32(imm)) {
-            m_formatter.oneByteOp64(OP_GROUP1_EvIb, dst, GROUP1_OP_OR);
-            m_formatter.immediate8s(imm);
-        } else {
-            if (dst == rax)
-                m_formatter.oneByteOp64(OP_OR_EAXIv);
-            else
-                m_formatter.oneByteOp64(OP_GROUP1_EvIz, dst, GROUP1_OP_OR);
-            m_formatter.immediate32(imm);
-        }
-    }
-
-    void notq_r(RegisterID dst)
-    {
-        spew("notq       %s", GPReg64Name(dst));
-        m_formatter.oneByteOp64(OP_GROUP3_Ev, dst, GROUP3_OP_NOT);
-    }
-#else
-    void orl_im(int32_t imm, const void* addr)
-    {
-        spew("orl        $0x%x, %p", imm, addr);
-        if (CAN_SIGN_EXTEND_8_32(imm)) {
-            m_formatter.oneByteOp(OP_GROUP1_EvIb, addr, GROUP1_OP_OR);
-            m_formatter.immediate8s(imm);
-        } else {
-            m_formatter.oneByteOp(OP_GROUP1_EvIz, addr, GROUP1_OP_OR);
-            m_formatter.immediate32(imm);
-        }
-    }
-#endif
-
     void subl_rr(RegisterID src, RegisterID dst)
     {
         spew("subl       %s, %s", GPReg32Name(src), GPReg32Name(dst));
         m_formatter.oneByteOp(OP_SUB_GvEv, src, dst);
     }
 
     void subw_rr(RegisterID src, RegisterID dst)
     {
@@ -1278,69 +1094,16 @@ public:
             m_formatter.oneByteOp(OP_GROUP1_EvIb, offset, base, index, scale, GROUP1_OP_SUB);
             m_formatter.immediate8s(imm);
         } else {
             m_formatter.oneByteOp(OP_GROUP1_EvIz, offset, base, index, scale, GROUP1_OP_SUB);
             m_formatter.immediate16(imm);
         }
     }
 
-#ifdef JS_CODEGEN_X64
-    void subq_rr(RegisterID src, RegisterID dst)
-    {
-        spew("subq       %s, %s", GPReg64Name(src), GPReg64Name(dst));
-        m_formatter.oneByteOp64(OP_SUB_GvEv, src, dst);
-    }
-
-    void subq_rm(RegisterID src, int32_t offset, RegisterID base)
-    {
-        spew("subq       %s, " MEM_ob, GPReg64Name(src), ADDR_ob(offset, base));
-        m_formatter.oneByteOp64(OP_SUB_EvGv, offset, base, src);
-    }
-
-    void subq_mr(int32_t offset, RegisterID base, RegisterID dst)
-    {
-        spew("subq       " MEM_ob ", %s", ADDR_ob(offset, base), GPReg64Name(dst));
-        m_formatter.oneByteOp64(OP_SUB_GvEv, offset, base, dst);
-    }
-
-    void subq_mr(const void* addr, RegisterID dst)
-    {
-        spew("subq       %p, %s", addr, GPReg64Name(dst));
-        m_formatter.oneByteOp64(OP_SUB_GvEv, addr, dst);
-    }
-
-    void subq_ir(int32_t imm, RegisterID dst)
-    {
-        spew("subq       $%d, %s", imm, GPReg64Name(dst));
-        if (CAN_SIGN_EXTEND_8_32(imm)) {
-            m_formatter.oneByteOp64(OP_GROUP1_EvIb, dst, GROUP1_OP_SUB);
-            m_formatter.immediate8s(imm);
-        } else {
-            if (dst == rax)
-                m_formatter.oneByteOp64(OP_SUB_EAXIv);
-            else
-                m_formatter.oneByteOp64(OP_GROUP1_EvIz, dst, GROUP1_OP_SUB);
-            m_formatter.immediate32(imm);
-        }
-    }
-#else
-    void subl_im(int32_t imm, const void* addr)
-    {
-        spew("subl       $%d, %p", imm, addr);
-        if (CAN_SIGN_EXTEND_8_32(imm)) {
-            m_formatter.oneByteOp(OP_GROUP1_EvIb, addr, GROUP1_OP_SUB);
-            m_formatter.immediate8s(imm);
-        } else {
-            m_formatter.oneByteOp(OP_GROUP1_EvIz, addr, GROUP1_OP_SUB);
-            m_formatter.immediate32(imm);
-        }
-    }
-#endif
-
     void xorl_rr(RegisterID src, RegisterID dst)
     {
         spew("xorl       %s, %s", GPReg32Name(src), GPReg32Name(dst));
         m_formatter.oneByteOp(OP_XOR_GvEv, src, dst);
     }
 
     void xorw_rr(RegisterID src, RegisterID dst)
     {
@@ -1457,39 +1220,16 @@ public:
             if (dst == rax)
                 m_formatter.oneByteOp(OP_XOR_EAXIv);
             else
                 m_formatter.oneByteOp(OP_GROUP1_EvIz, dst, GROUP1_OP_XOR);
             m_formatter.immediate16(imm);
         }
     }
 
-#ifdef JS_CODEGEN_X64
-    void xorq_rr(RegisterID src, RegisterID dst)
-    {
-        spew("xorq       %s, %s", GPReg64Name(src), GPReg64Name(dst));
-        m_formatter.oneByteOp64(OP_XOR_GvEv, src, dst);
-    }
-
-    void xorq_ir(int32_t imm, RegisterID dst)
-    {
-        spew("xorq       $0x%" PRIx64 ", %s", int64_t(imm), GPReg64Name(dst));
-        if (CAN_SIGN_EXTEND_8_32(imm)) {
-            m_formatter.oneByteOp64(OP_GROUP1_EvIb, dst, GROUP1_OP_XOR);
-            m_formatter.immediate8s(imm);
-        } else {
-            if (dst == rax)
-                m_formatter.oneByteOp64(OP_XOR_EAXIv);
-            else
-                m_formatter.oneByteOp64(OP_GROUP1_EvIz, dst, GROUP1_OP_XOR);
-            m_formatter.immediate32(imm);
-        }
-    }
-#endif
-
     void sarl_ir(int32_t imm, RegisterID dst)
     {
         MOZ_ASSERT(imm < 32);
         spew("sarl       $%d, %s", imm, GPReg32Name(dst));
         if (imm == 1)
             m_formatter.oneByteOp(OP_GROUP2_Ev1, dst, GROUP2_OP_SAR);
         else {
             m_formatter.oneByteOp(OP_GROUP2_EvIb, dst, GROUP2_OP_SAR);
@@ -1534,60 +1274,16 @@ public:
     }
 
     void shll_CLr(RegisterID dst)
     {
         spew("shll       %%cl, %s", GPReg32Name(dst));
         m_formatter.oneByteOp(OP_GROUP2_EvCL, dst, GROUP2_OP_SHL);
     }
 
-#ifdef JS_CODEGEN_X64
-    void sarq_CLr(RegisterID dst)
-    {
-        spew("sarq       %%cl, %s", GPReg64Name(dst));
-        m_formatter.oneByteOp64(OP_GROUP2_EvCL, dst, GROUP2_OP_SAR);
-    }
-
-    void sarq_ir(int32_t imm, RegisterID dst)
-    {
-        MOZ_ASSERT(imm < 64);
-        spew("sarq       $%d, %s", imm, GPReg64Name(dst));
-        if (imm == 1)
-            m_formatter.oneByteOp64(OP_GROUP2_Ev1, dst, GROUP2_OP_SAR);
-        else {
-            m_formatter.oneByteOp64(OP_GROUP2_EvIb, dst, GROUP2_OP_SAR);
-            m_formatter.immediate8u(imm);
-        }
-    }
-
-    void shlq_ir(int32_t imm, RegisterID dst)
-    {
-        MOZ_ASSERT(imm < 64);
-        spew("shlq       $%d, %s", imm, GPReg64Name(dst));
-        if (imm == 1)
-            m_formatter.oneByteOp64(OP_GROUP2_Ev1, dst, GROUP2_OP_SHL);
-        else {
-            m_formatter.oneByteOp64(OP_GROUP2_EvIb, dst, GROUP2_OP_SHL);
-            m_formatter.immediate8u(imm);
-        }
-    }
-
-    void shrq_ir(int32_t imm, RegisterID dst)
-    {
-        MOZ_ASSERT(imm < 64);
-        spew("shrq       $%d, %s", imm, GPReg64Name(dst));
-        if (imm == 1)
-            m_formatter.oneByteOp64(OP_GROUP2_Ev1, dst, GROUP2_OP_SHR);
-        else {
-            m_formatter.oneByteOp64(OP_GROUP2_EvIb, dst, GROUP2_OP_SHR);
-            m_formatter.immediate8u(imm);
-        }
-    }
-#endif
-
     void bsr_rr(RegisterID src, RegisterID dst)
     {
         spew("bsr        %s, %s", GPReg32Name(src), GPReg32Name(dst));
         m_formatter.twoByteOp(OP2_BSR_GvEv, src, dst);
     }
 
     void imull_rr(RegisterID src, RegisterID dst)
     {
@@ -1614,24 +1310,16 @@ public:
             m_formatter.oneByteOp(OP_IMUL_GvEvIb, src, dst);
             m_formatter.immediate8s(value);
         } else {
             m_formatter.oneByteOp(OP_IMUL_GvEvIz, src, dst);
             m_formatter.immediate32(value);
         }
     }
 
-#ifdef JS_CODEGEN_X64
-    void imulq_rr(RegisterID src, RegisterID dst)
-    {
-        spew("imulq      %s, %s", GPReg64Name(src), GPReg64Name(dst));
-        m_formatter.twoByteOp64(OP2_IMUL_GvEv, src, dst);
-    }
-#endif
-
     void mull_r(RegisterID multiplier)
     {
         spew("mull       %s", GPReg32Name(multiplier));
         m_formatter.oneByteOp(OP_GROUP3_Ev, multiplier, GROUP3_OP_MUL);
     }
 
     void idivl_r(RegisterID divisor)
     {
@@ -1844,95 +1532,16 @@ public:
 
     void cmpl_i32m(int32_t rhs, const void* addr)
     {
         spew("cmpl       $0x%04x, %p", rhs, addr);
         m_formatter.oneByteOp(OP_GROUP1_EvIz, addr, GROUP1_OP_CMP);
         m_formatter.immediate32(rhs);
     }
 
-#ifdef JS_CODEGEN_X64
-    void cmpq_rr(RegisterID rhs, RegisterID lhs)
-    {
-        spew("cmpq       %s, %s", GPReg64Name(rhs), GPReg64Name(lhs));
-        m_formatter.oneByteOp64(OP_CMP_GvEv, rhs, lhs);
-    }
-
-    void cmpq_rm(RegisterID rhs, int32_t offset, RegisterID base)
-    {
-        spew("cmpq       %s, " MEM_ob, GPReg64Name(rhs), ADDR_ob(offset, base));
-        m_formatter.oneByteOp64(OP_CMP_EvGv, offset, base, rhs);
-    }
-
-    void cmpq_mr(int32_t offset, RegisterID base, RegisterID lhs)
-    {
-        spew("cmpq       " MEM_ob ", %s", ADDR_ob(offset, base), GPReg64Name(lhs));
-        m_formatter.oneByteOp64(OP_CMP_GvEv, offset, base, lhs);
-    }
-
-    void cmpq_ir(int32_t rhs, RegisterID lhs)
-    {
-        if (rhs == 0) {
-            testq_rr(lhs, lhs);
-            return;
-        }
-
-        spew("cmpq       $0x%" PRIx64 ", %s", int64_t(rhs), GPReg64Name(lhs));
-        if (CAN_SIGN_EXTEND_8_32(rhs)) {
-            m_formatter.oneByteOp64(OP_GROUP1_EvIb, lhs, GROUP1_OP_CMP);
-            m_formatter.immediate8s(rhs);
-        } else {
-            if (lhs == rax)
-                m_formatter.oneByteOp64(OP_CMP_EAXIv);
-            else
-                m_formatter.oneByteOp64(OP_GROUP1_EvIz, lhs, GROUP1_OP_CMP);
-            m_formatter.immediate32(rhs);
-        }
-    }
-
-    void cmpq_im(int32_t rhs, int32_t offset, RegisterID base)
-    {
-        spew("cmpq       $0x%" PRIx64 ", " MEM_ob, int64_t(rhs), ADDR_ob(offset, base));
-        if (CAN_SIGN_EXTEND_8_32(rhs)) {
-            m_formatter.oneByteOp64(OP_GROUP1_EvIb, offset, base, GROUP1_OP_CMP);
-            m_formatter.immediate8s(rhs);
-        } else {
-            m_formatter.oneByteOp64(OP_GROUP1_EvIz, offset, base, GROUP1_OP_CMP);
-            m_formatter.immediate32(rhs);
-        }
-    }
-
-    void cmpq_im(int32_t rhs, int32_t offset, RegisterID base, RegisterID index, int scale)
-    {
-        spew("cmpq       $0x%x, " MEM_obs, rhs, ADDR_obs(offset, base, index, scale));
-        if (CAN_SIGN_EXTEND_8_32(rhs)) {
-            m_formatter.oneByteOp64(OP_GROUP1_EvIb, offset, base, index, scale, GROUP1_OP_CMP);
-            m_formatter.immediate8s(rhs);
-        } else {
-            m_formatter.oneByteOp64(OP_GROUP1_EvIz, offset, base, index, scale, GROUP1_OP_CMP);
-            m_formatter.immediate32(rhs);
-        }
-    }
-    void cmpq_im(int32_t rhs, const void* addr)
-    {
-        spew("cmpq       $0x%" PRIx64 ", %p", int64_t(rhs), addr);
-        if (CAN_SIGN_EXTEND_8_32(rhs)) {
-            m_formatter.oneByteOp64(OP_GROUP1_EvIb, addr, GROUP1_OP_CMP);
-            m_formatter.immediate8s(rhs);
-        } else {
-            m_formatter.oneByteOp64(OP_GROUP1_EvIz, addr, GROUP1_OP_CMP);
-            m_formatter.immediate32(rhs);
-        }
-    }
-    void cmpq_rm(RegisterID rhs, const void* addr)
-    {
-        spew("cmpq       %s, %p", GPReg64Name(rhs), addr);
-        m_formatter.oneByteOp64(OP_CMP_EvGv, addr, rhs);
-    }
-#endif
     void cmpl_rm(RegisterID rhs, const void* addr)
     {
         spew("cmpl       %s, %p", GPReg32Name(rhs), addr);
         m_formatter.oneByteOp(OP_CMP_EvGv, addr, rhs);
     }
 
     void cmpl_rm_disp32(RegisterID rhs, const void* addr)
     {
@@ -2044,54 +1653,16 @@ public:
 
     void testl_i32m(int32_t rhs, int32_t offset, RegisterID base, RegisterID index, int scale)
     {
         spew("testl      $0x%4x, " MEM_obs, rhs, ADDR_obs(offset, base, index, scale));
         m_formatter.oneByteOp(OP_GROUP3_EvIz, offset, base, index, scale, GROUP3_OP_TEST);
         m_formatter.immediate32(rhs);
     }
 
-#ifdef JS_CODEGEN_X64
-    void testq_rr(RegisterID rhs, RegisterID lhs)
-    {
-        spew("testq      %s, %s", GPReg64Name(rhs), GPReg64Name(lhs));
-        m_formatter.oneByteOp64(OP_TEST_EvGv, lhs, rhs);
-    }
-
-    void testq_ir(int32_t rhs, RegisterID lhs)
-    {
-        // If the mask fits in a 32-bit immediate, we can use testl with a
-        // 32-bit subreg.
-        if (CAN_ZERO_EXTEND_32_64(rhs)) {
-            testl_ir(rhs, lhs);
-            return;
-        }
-        spew("testq      $0x%" PRIx64 ", %s", int64_t(rhs), GPReg64Name(lhs));
-        if (lhs == rax)
-            m_formatter.oneByteOp64(OP_TEST_EAXIv);
-        else
-            m_formatter.oneByteOp64(OP_GROUP3_EvIz, lhs, GROUP3_OP_TEST);
-        m_formatter.immediate32(rhs);
-    }
-
-    void testq_i32m(int32_t rhs, int32_t offset, RegisterID base)
-    {
-        spew("testq      $0x%" PRIx64 ", " MEM_ob, int64_t(rhs), ADDR_ob(offset, base));
-        m_formatter.oneByteOp64(OP_GROUP3_EvIz, offset, base, GROUP3_OP_TEST);
-        m_formatter.immediate32(rhs);
-    }
-
-    void testq_i32m(int32_t rhs, int32_t offset, RegisterID base, RegisterID index, int scale)
-    {
-        spew("testq      $0x%4x, " MEM_obs, rhs, ADDR_obs(offset, base, index, scale));
-        m_formatter.oneByteOp64(OP_GROUP3_EvIz, offset, base, index, scale, GROUP3_OP_TEST);
-        m_formatter.immediate32(rhs);
-    }
-#endif
-
     void testw_rr(RegisterID rhs, RegisterID lhs)
     {
         spew("testw      %s, %s", GPReg16Name(rhs), GPReg16Name(lhs));
         m_formatter.prefix(PRE_OPERAND_SIZE);
         m_formatter.oneByteOp(OP_TEST_EvGv, lhs, rhs);
     }
 
     void testb_ir(int32_t rhs, RegisterID lhs)
@@ -2182,34 +1753,16 @@ public:
         m_formatter.oneByteOp(OP_XCHG_GvEv, offset, base, src);
     }
     void xchgl_rm(RegisterID src, int32_t offset, RegisterID base, RegisterID index, int scale)
     {
         spew("xchgl      %s, " MEM_obs, GPReg32Name(src), ADDR_obs(offset, base, index, scale));
         m_formatter.oneByteOp(OP_XCHG_GvEv, offset, base, index, scale, src);
     }
 
-#ifdef JS_CODEGEN_X64
-    void xchgq_rr(RegisterID src, RegisterID dst)
-    {
-        spew("xchgq      %s, %s", GPReg64Name(src), GPReg64Name(dst));
-        m_formatter.oneByteOp64(OP_XCHG_GvEv, src, dst);
-    }
-    void xchgq_rm(RegisterID src, int32_t offset, RegisterID base)
-    {
-        spew("xchgq      %s, " MEM_ob, GPReg64Name(src), ADDR_ob(offset, base));
-        m_formatter.oneByteOp64(OP_XCHG_GvEv, offset, base, src);
-    }
-    void xchgq_rm(RegisterID src, int32_t offset, RegisterID base, RegisterID index, int scale)
-    {
-        spew("xchgq      %s, " MEM_obs, GPReg64Name(src), ADDR_obs(offset, base, index, scale));
-        m_formatter.oneByteOp64(OP_XCHG_GvEv, offset, base, index, scale, src);
-    }
-#endif
-
     void movl_rr(RegisterID src, RegisterID dst)
     {
         spew("movl       %s, %s", GPReg32Name(src), GPReg32Name(dst));
         m_formatter.oneByteOp(OP_MOV_GvEv, src, dst);
     }
 
     void movw_rm(RegisterID src, int32_t offset, RegisterID base)
     {
@@ -2407,53 +1960,16 @@ public:
         m_formatter.oneByteOp(OP_MOV_OvEAX);
 #ifdef JS_CODEGEN_X64
         m_formatter.immediate64(reinterpret_cast<int64_t>(addr));
 #else
         m_formatter.immediate32(reinterpret_cast<int32_t>(addr));
 #endif
     }
 
-#ifdef JS_CODEGEN_X64
-    void movq_rr(RegisterID src, RegisterID dst)
-    {
-        spew("movq       %s, %s", GPReg64Name(src), GPReg64Name(dst));
-        m_formatter.oneByteOp64(OP_MOV_GvEv, src, dst);
-    }
-
-    void movq_rm(RegisterID src, int32_t offset, RegisterID base)
-    {
-        spew("movq       %s, " MEM_ob, GPReg64Name(src), ADDR_ob(offset, base));
-        m_formatter.oneByteOp64(OP_MOV_EvGv, offset, base, src);
-    }
-
-    void movq_rm_disp32(RegisterID src, int32_t offset, RegisterID base)
-    {
-        spew("movq       %s, " MEM_o32b, GPReg64Name(src), ADDR_o32b(offset, base));
-        m_formatter.oneByteOp64_disp32(OP_MOV_EvGv, offset, base, src);
-    }
-
-    void movq_rm(RegisterID src, int32_t offset, RegisterID base, RegisterID index, int scale)
-    {
-        spew("movq       %s, " MEM_obs, GPReg64Name(src), ADDR_obs(offset, base, index, scale));
-        m_formatter.oneByteOp64(OP_MOV_EvGv, offset, base, index, scale, src);
-    }
-
-    void movq_rm(RegisterID src, const void* addr)
-    {
-        if (src == rax && !IsAddressImmediate(addr)) {
-            movq_EAXm(addr);
-            return;
-        }
-
-        spew("movq       %s, %p", GPReg64Name(src), addr);
-        m_formatter.oneByteOp64(OP_MOV_EvGv, addr, src);
-    }
-#endif
-
     void vmovq_rm(XMMRegisterID src, int32_t offset, RegisterID base)
     {
         // vmovq_rm can be encoded either as a true vmovq or as a vmovd with a
         // REX prefix modifying it to be 64-bit. We choose the vmovq encoding
         // because it's smaller (when it doesn't need a REX prefix for other
         // reasons) and because it works on 32-bit x86 too.
         twoByteOpSimd("vmovq", VEX_PD, OP2_MOVQ_WdVd, offset, base, invalid_xmm, src);
     }
@@ -2468,71 +1984,16 @@ public:
         twoByteOpSimd("vmovq", VEX_PD, OP2_MOVQ_WdVd, offset, base, index, scale, invalid_xmm, src);
     }
 
     void vmovq_rm(XMMRegisterID src, const void* addr)
     {
         twoByteOpSimd("vmovq", VEX_PD, OP2_MOVQ_WdVd, addr, invalid_xmm, src);
     }
 
-#ifdef JS_CODEGEN_X64
-    void movq_mEAX(const void* addr)
-    {
-        if (IsAddressImmediate(addr)) {
-            movq_mr(addr, rax);
-            return;
-        }
-
-        spew("movq       %p, %%rax", addr);
-        m_formatter.oneByteOp64(OP_MOV_EAXOv);
-        m_formatter.immediate64(reinterpret_cast<int64_t>(addr));
-    }
-
-    void movq_EAXm(const void* addr)
-    {
-        if (IsAddressImmediate(addr)) {
-            movq_rm(rax, addr);
-            return;
-        }
-
-        spew("movq       %%rax, %p", addr);
-        m_formatter.oneByteOp64(OP_MOV_OvEAX);
-        m_formatter.immediate64(reinterpret_cast<int64_t>(addr));
-    }
-
-    void movq_mr(int32_t offset, RegisterID base, RegisterID dst)
-    {
-        spew("movq       " MEM_ob ", %s", ADDR_ob(offset, base), GPReg64Name(dst));
-        m_formatter.oneByteOp64(OP_MOV_GvEv, offset, base, dst);
-    }
-
-    void movq_mr_disp32(int32_t offset, RegisterID base, RegisterID dst)
-    {
-        spew("movq       " MEM_o32b ", %s", ADDR_o32b(offset, base), GPReg64Name(dst));
-        m_formatter.oneByteOp64_disp32(OP_MOV_GvEv, offset, base, dst);
-    }
-
-    void movq_mr(int32_t offset, RegisterID base, RegisterID index, int scale, RegisterID dst)
-    {
-        spew("movq       " MEM_obs ", %s", ADDR_obs(offset, base, index, scale), GPReg64Name(dst));
-        m_formatter.oneByteOp64(OP_MOV_GvEv, offset, base, index, scale, dst);
-    }
-
-    void movq_mr(const void* addr, RegisterID dst)
-    {
-        if (dst == rax && !IsAddressImmediate(addr)) {
-            movq_mEAX(addr);
-            return;
-        }
-
-        spew("movq       %p, %s", addr, GPReg64Name(dst));
-        m_formatter.oneByteOp64(OP_MOV_GvEv, addr, dst);
-    }
-#endif
-
     void vmovq_mr(int32_t offset, RegisterID base, XMMRegisterID dst)
     {
         // vmovq_mr can be encoded either as a true vmovq or as a vmovd with a
         // REX prefix modifying it to be 64-bit. We choose the vmovq encoding
         // because it's smaller (when it doesn't need a REX prefix for other
         // reasons) and because it works on 32-bit x86 too.
         twoByteOpSimd("vmovq", VEX_SS, OP2_MOVQ_VdWd, offset, base, invalid_xmm, dst);
     }
@@ -2547,105 +2008,16 @@ public:
         twoByteOpSimd("vmovq", VEX_SS, OP2_MOVQ_VdWd, offset, base, index, scale, invalid_xmm, dst);
     }
 
     void vmovq_mr(const void* addr, XMMRegisterID dst)
     {
         twoByteOpSimd("vmovq", VEX_SS, OP2_MOVQ_VdWd, addr, invalid_xmm, dst);
     }
 
-#ifdef JS_CODEGEN_X64
-    void leaq_mr(int32_t offset, RegisterID base, RegisterID index, int scale, RegisterID dst)
-    {
-        spew("leaq       " MEM_obs ", %s", ADDR_obs(offset, base, index, scale), GPReg64Name(dst)),
-        m_formatter.oneByteOp64(OP_LEA, offset, base, index, scale, dst);
-    }
-
-    void movq_i32m(int32_t imm, int32_t offset, RegisterID base)
-    {
-        spew("movq       $%d, " MEM_ob, imm, ADDR_ob(offset, base));
-        m_formatter.oneByteOp64(OP_GROUP11_EvIz, offset, base, GROUP11_MOV);
-        m_formatter.immediate32(imm);
-    }
-
-    void movq_i32m(int32_t imm, int32_t offset, RegisterID base, RegisterID index, int scale)
-    {
-        spew("movq       $%d, " MEM_obs, imm, ADDR_obs(offset, base, index, scale));
-        m_formatter.oneByteOp64(OP_GROUP11_EvIz, offset, base, index, scale, GROUP11_MOV);
-        m_formatter.immediate32(imm);
-    }
-    void movq_i32m(int32_t imm, const void* addr)
-    {
-        spew("movq       $%d, %p", imm, addr);
-        m_formatter.oneByteOp64(OP_GROUP11_EvIz, addr, GROUP11_MOV);
-        m_formatter.immediate32(imm);
-    }
-
-    // Note that this instruction sign-extends its 32-bit immediate field to 64
-    // bits and loads the 64-bit value into a 64-bit register.
-    //
-    // Note also that this is similar to the movl_i32r instruction, except that
-    // movl_i32r *zero*-extends its 32-bit immediate, and it has smaller code
-    // size, so it's preferred for values which could use either.
-    void movq_i32r(int32_t imm, RegisterID dst) {
-        spew("movq       $%d, %s", imm, GPRegName(dst));
-        m_formatter.oneByteOp64(OP_GROUP11_EvIz, dst, GROUP11_MOV);
-        m_formatter.immediate32(imm);
-    }
-
-    void movq_i64r(int64_t imm, RegisterID dst)
-    {
-        spew("movabsq    $0x%" PRIx64 ", %s", imm, GPReg64Name(dst));
-        m_formatter.oneByteOp64(OP_MOV_EAXIv, dst);
-        m_formatter.immediate64(imm);
-    }
-
-    void movslq_rr(RegisterID src, RegisterID dst)
-    {
-        spew("movslq     %s, %s", GPReg32Name(src), GPReg64Name(dst));
-        m_formatter.oneByteOp64(OP_MOVSXD_GvEv, src, dst);
-    }
-    void movslq_mr(int32_t offset, RegisterID base, RegisterID dst)
-    {
-        spew("movslq     " MEM_ob ", %s", ADDR_ob(offset, base), GPReg64Name(dst));
-        m_formatter.oneByteOp64(OP_MOVSXD_GvEv, offset, base, dst);
-    }
-    void movslq_mr(int32_t offset, RegisterID base, RegisterID index, int scale, RegisterID dst)
-    {
-        spew("movslq     " MEM_obs ", %s", ADDR_obs(offset, base, index, scale), GPReg64Name(dst));
-        m_formatter.oneByteOp64(OP_MOVSXD_GvEv, offset, base, index, scale, dst);
-    }
-
-    MOZ_WARN_UNUSED_RESULT JmpSrc
-    movl_ripr(RegisterID dst)
-    {
-        m_formatter.oneByteRipOp(OP_MOV_GvEv, 0, (RegisterID)dst);
-        JmpSrc label(m_formatter.size());
-        spew("movl       " MEM_o32r ", %s", ADDR_o32r(label.offset()), GPReg32Name(dst));
-        return label;
-    }
-
-    MOZ_WARN_UNUSED_RESULT JmpSrc
-    movl_rrip(RegisterID src)
-    {
-        m_formatter.oneByteRipOp(OP_MOV_EvGv, 0, (RegisterID)src);
-        JmpSrc label(m_formatter.size());
-        spew("movl       %s, " MEM_o32r "", GPReg32Name(src), ADDR_o32r(label.offset()));
-        return label;
-    }
-
-    MOZ_WARN_UNUSED_RESULT JmpSrc
-    movq_ripr(RegisterID dst)
-    {
-        m_formatter.oneByteRipOp64(OP_MOV_GvEv, 0, dst);
-        JmpSrc label(m_formatter.size());
-        spew("movq       " MEM_o32r ", %s", ADDR_o32r(label.offset()), GPRegName(dst));
-        return label;
-    }
-#endif
     void movl_rm(RegisterID src, const void* addr)
     {
         if (src == rax
 #ifdef JS_CODEGEN_X64
             && !IsAddressImmediate(addr)
 #endif
             ) {
             movl_EAXm(addr);
@@ -2825,32 +2197,16 @@ public:
         m_formatter.oneByteOp(OP_LEA, offset, base, index, scale, dst);
     }
 
     void leal_mr(int32_t offset, RegisterID base, RegisterID dst)
     {
         spew("leal       " MEM_ob ", %s", ADDR_ob(offset, base), GPReg32Name(dst));
         m_formatter.oneByteOp(OP_LEA, offset, base, dst);
     }
-#ifdef JS_CODEGEN_X64
-    void leaq_mr(int32_t offset, RegisterID base, RegisterID dst)
-    {
-        spew("leaq       " MEM_ob ", %s", ADDR_ob(offset, base), GPReg64Name(dst));
-        m_formatter.oneByteOp64(OP_LEA, offset, base, dst);
-    }
-
-    MOZ_WARN_UNUSED_RESULT JmpSrc
-    leaq_rip(RegisterID dst)
-    {
-        m_formatter.oneByteRipOp64(OP_LEA, 0, dst);
-        JmpSrc label(m_formatter.size());
-        spew("leaq       " MEM_o32r ", %s", ADDR_o32r(label.offset()), GPRegName(dst));
-        return label;
-    }
-#endif
 
     // Flow control:
 
     MOZ_WARN_UNUSED_RESULT JmpSrc
     call()
     {
         m_formatter.oneByteOp(OP_CALL_rel32);
         JmpSrc r = m_formatter.immediateRel32();
@@ -2919,30 +2275,16 @@ public:
         m_formatter.oneByteOp(OP_GROUP5_Ev, offset, base, GROUP5_OP_JMPN);
     }
 
     void jmp_m(int32_t offset, RegisterID base, RegisterID index, int scale) {
         spew("jmp        *" MEM_obs, ADDR_obs(offset, base, index, scale));
         m_formatter.oneByteOp(OP_GROUP5_Ev, offset, base, index, scale, GROUP5_OP_JMPN);
     }
 
-#ifdef JS_CODEGEN_X64
-    void jmp_rip(int ripOffset) {
-        // rip-relative addressing.
-        spew("jmp        *%d(%%rip)", ripOffset);
-        m_formatter.oneByteRipOp(OP_GROUP5_Ev, ripOffset, GROUP5_OP_JMPN);
-    }
-
-    void immediate64(int64_t imm)
-    {
-        spew(".quad      %lld", (long long)imm);
-        m_formatter.immediate64(imm);
-    }
-#endif
-
     void jCC_i(Condition cond, JmpDst dst)
     {
         int32_t diff = dst.offset() - m_formatter.size();
         spew("j%s        .Llabel%d", CCName(cond), dst.offset());
 
         // The jump immediate is an offset from the end of the jump instruction.
         // A conditional jump instruction is either 1 byte opcode and 1 byte
         // offset, or 2 bytes opcode and 4 bytes offset.
@@ -3094,27 +2436,16 @@ public:
         twoByteOpSimd("vcvttps2dq", VEX_SS, OP2_CVTTPS2DQ_VdqWps, src, invalid_xmm, dst);
     }
 
     void vcvtdq2ps_rr(XMMRegisterID src, XMMRegisterID dst)
     {
         twoByteOpSimd("vcvtdq2ps", VEX_PS, OP2_CVTDQ2PS_VpsWdq, src, invalid_xmm, dst);
     }
 
-#ifdef JS_CODEGEN_X64
-    void vcvtsq2sd_rr(RegisterID src1, XMMRegisterID src0, XMMRegisterID dst)
-    {
-        twoByteOpInt64Simd("vcvtsi2sd", VEX_SD, OP2_CVTSI2SD_VsdEd, src1, src0, dst);
-    }
-    void vcvtsq2ss_rr(RegisterID src1, XMMRegisterID src0, XMMRegisterID dst)
-    {
-        twoByteOpInt64Simd("vcvtsi2ss", VEX_SS, OP2_CVTSI2SD_VsdEd, src1, src0, dst);
-    }
-#endif
-
     void vcvtsi2sd_mr(int32_t offset, RegisterID base, XMMRegisterID src0, XMMRegisterID dst)
     {
         twoByteOpSimd("vcvtsi2sd", VEX_SD, OP2_CVTSI2SD_VsdEd, offset, base, src0, dst);
     }
 
     void vcvtsi2sd_mr(int32_t offset, RegisterID base, RegisterID index, int scale, XMMRegisterID src0, XMMRegisterID dst)
     {
         twoByteOpSimd("vcvtsi2sd", VEX_SD, OP2_CVTSI2SD_VsdEd, offset, base, index, scale, src0, dst);
@@ -3125,52 +2456,26 @@ public:
         twoByteOpSimd("vcvtsi2ss", VEX_SS, OP2_CVTSI2SD_VsdEd, offset, base, src0, dst);
     }
 
     void vcvtsi2ss_mr(int32_t offset, RegisterID base, RegisterID index, int scale, XMMRegisterID src0, XMMRegisterID dst)
     {
         twoByteOpSimd("vcvtsi2ss", VEX_SS, OP2_CVTSI2SD_VsdEd, offset, base, index, scale, src0, dst);
     }
 
-#ifdef JS_CODEGEN_X86
-    void vcvtsi2sd_mr(const void* address, XMMRegisterID src0, XMMRegisterID dst)
-    {
-        twoByteOpSimd("vcvtsi2sd", VEX_SD, OP2_CVTSI2SD_VsdEd, address, src0, dst);
-    }
-#endif
-
-#ifdef JS_CODEGEN_X64
-    void vcvtsi2sdq_rr(RegisterID src, XMMRegisterID dst)
-    {
-        twoByteOpInt64Simd("vcvtsi2sdq", VEX_SD, OP2_CVTSI2SD_VsdEd, src, invalid_xmm, dst);
-    }
-#endif
-
     void vcvttsd2si_rr(XMMRegisterID src, RegisterID dst)
     {
         twoByteOpSimdInt32("vcvttsd2si", VEX_SD, OP2_CVTTSD2SI_GdWsd, src, dst);
     }
 
     void vcvttss2si_rr(XMMRegisterID src, RegisterID dst)
     {
         twoByteOpSimdInt32("vcvttss2si", VEX_SS, OP2_CVTTSD2SI_GdWsd, src, dst);
     }
 
-#ifdef JS_CODEGEN_X64
-    void vcvttsd2sq_rr(XMMRegisterID src, RegisterID dst)
-    {
-        twoByteOpSimdInt64("vcvttsd2si", VEX_SD, OP2_CVTTSD2SI_GdWsd, src, dst);
-    }
-
-    void vcvttss2sq_rr(XMMRegisterID src, RegisterID dst)
-    {
-        twoByteOpSimdInt64("vcvttss2si", VEX_SS, OP2_CVTTSD2SI_GdWsd, src, dst);
-    }
-#endif
-
     void vunpcklps_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst)
     {
         twoByteOpSimd("vunpcklps", VEX_PS, OP2_UNPCKLPS_VsdWsd, src1, src0, dst);
     }
     void vunpcklps_mr(int32_t offset, RegisterID base, XMMRegisterID src0, XMMRegisterID dst)
     {
         twoByteOpSimd("vunpcklps", VEX_PS, OP2_UNPCKLPS_VsdWsd, offset, base, src0, dst);
     }
@@ -3392,32 +2697,16 @@ public:
         twoByteOpSimd_disp32("vmovd", VEX_PD, OP2_MOVD_EdVd, offset, base, invalid_xmm, src);
     }
 
     void vmovd_rm(XMMRegisterID src, const void* address)
     {
         twoByteOpSimd("vmovd", VEX_PD, OP2_MOVD_EdVd, address, invalid_xmm, src);
     }
 
-#ifdef JS_CODEGEN_X64
-    void vmovq_rr(XMMRegisterID src, RegisterID dst)
-    {
-        // While this is called "vmovq", it actually uses the vmovd encoding
-        // with a REX prefix modifying it to be 64-bit.
-        twoByteOpSimdInt64("vmovq", VEX_PD, OP2_MOVD_EdVd, (XMMRegisterID)dst, (RegisterID)src);
-    }
-
-    void vmovq_rr(RegisterID src, XMMRegisterID dst)
-    {
-        // While this is called "vmovq", it actually uses the vmovd encoding
-        // with a REX prefix modifying it to be 64-bit.
-        twoByteOpInt64Simd("vmovq", VEX_PD, OP2_MOVD_VdEd, src, invalid_xmm, dst);
-    }
-#endif
-
     void vmovsd_rm(XMMRegisterID src, int32_t offset, RegisterID base)
     {
         twoByteOpSimd("vmovsd", VEX_SD, OP2_MOVSD_WsdVsd, offset, base, invalid_xmm, src);
     }
 
     void vmovsd_rm_disp32(XMMRegisterID src, int32_t offset, RegisterID base)
     {
         twoByteOpSimd_disp32("vmovsd", VEX_SD, OP2_MOVSD_WsdVsd, offset, base, invalid_xmm, src);
@@ -3532,48 +2821,16 @@ public:
     {
         twoByteOpSimd("vmovdqu", VEX_SS, OP2_MOVDQ_WdqVdq, address, invalid_xmm, src);
     }
 
     void vmovups_rm(XMMRegisterID src, const void* address)
     {
         twoByteOpSimd("vmovups", VEX_PS, OP2_MOVPS_WpsVps, address, invalid_xmm, src);
     }
-#ifdef JS_CODEGEN_X64
-    MOZ_WARN_UNUSED_RESULT JmpSrc
-    vmovsd_ripr(XMMRegisterID dst)
-    {
-        return twoByteRipOpSimd("vmovsd", VEX_SD, OP2_MOVSD_VsdWsd, invalid_xmm, dst);
-    }
-    MOZ_WARN_UNUSED_RESULT JmpSrc
-    vmovss_ripr(XMMRegisterID dst)
-    {
-        return twoByteRipOpSimd("vmovss", VEX_SS, OP2_MOVSD_VsdWsd, invalid_xmm, dst);
-    }
-    MOZ_WARN_UNUSED_RESULT JmpSrc
-    vmovsd_rrip(XMMRegisterID src)
-    {
-        return twoByteRipOpSimd("vmovsd", VEX_SD, OP2_MOVSD_WsdVsd, invalid_xmm, src);
-    }
-    MOZ_WARN_UNUSED_RESULT JmpSrc
-    vmovss_rrip(XMMRegisterID src)
-    {
-        return twoByteRipOpSimd("vmovss", VEX_SS, OP2_MOVSD_WsdVsd, invalid_xmm, src);
-    }
-    MOZ_WARN_UNUSED_RESULT JmpSrc
-    vmovdqa_rrip(XMMRegisterID src)
-    {
-        return twoByteRipOpSimd("vmovdqa", VEX_PD, OP2_MOVDQ_WdqVdq, invalid_xmm, src);
-    }
-    MOZ_WARN_UNUSED_RESULT JmpSrc
-    vmovaps_rrip(XMMRegisterID src)
-    {
-        return twoByteRipOpSimd("vmovdqa", VEX_PS, OP2_MOVAPS_WsdVsd, invalid_xmm, src);
-    }
-#endif
 
     void vmovaps_rr(XMMRegisterID src, XMMRegisterID dst)
     {
 #ifdef JS_CODEGEN_X64
         // There are two opcodes that can encode this instruction. If we have
         // one register in [xmm8,xmm15] and one in [xmm0,xmm7], use the
         // opcode which swaps the operands, as that way we can get a two-byte
         // VEX in that case.
@@ -3636,40 +2893,16 @@ public:
         if (src >= xmm8 && dst < xmm8) {
             twoByteOpSimd("vmovapd", VEX_PD, OP2_MOVAPS_WsdVsd, dst, invalid_xmm, src);
             return;
         }
 #endif
         twoByteOpSimd("vmovapd", VEX_PD, OP2_MOVAPD_VsdWsd, src, invalid_xmm, dst);
     }
 
-#ifdef JS_CODEGEN_X64
-    MOZ_WARN_UNUSED_RESULT JmpSrc
-    vmovaps_ripr(XMMRegisterID dst)
-    {
-        return twoByteRipOpSimd("vmovaps", VEX_PS, OP2_MOVAPS_VsdWsd, invalid_xmm, dst);
-    }
-
-    MOZ_WARN_UNUSED_RESULT JmpSrc
-    vmovdqa_ripr(XMMRegisterID dst)
-    {
-        return twoByteRipOpSimd("vmovdqa", VEX_PD, OP2_MOVDQ_VdqWdq, invalid_xmm, dst);
-    }
-#else
-    void vmovaps_mr(const void* address, XMMRegisterID dst)
-    {
-        twoByteOpSimd("vmovaps", VEX_PS, OP2_MOVAPS_VsdWsd, address, invalid_xmm, dst);
-    }
-
-    void vmovdqa_mr(const void* address, XMMRegisterID dst)
-    {
-        twoByteOpSimd("vmovdqa", VEX_PD, OP2_MOVDQ_VdqWdq, address, invalid_xmm, dst);
-    }
-#endif // JS_CODEGEN_X64
-
     void vmovdqu_rm(XMMRegisterID src, int32_t offset, RegisterID base)
     {
         twoByteOpSimd("vmovdqu", VEX_SS, OP2_MOVDQ_WdqVdq, offset, base, invalid_xmm, src);
     }
 
     void vmovdqu_rm_disp32(XMMRegisterID src, int32_t offset, RegisterID base)
     {
         twoByteOpSimd_disp32("vmovdqu", VEX_SS, OP2_MOVDQ_WdqVdq, offset, base, invalid_xmm, src);
@@ -4032,30 +3265,16 @@ threeByteOpImmSimd("vblendps", VEX_PD, O
 
     void ret_i(int32_t imm)
     {
         spew("ret        $%d", imm);
         m_formatter.oneByteOp(OP_RET_Iz);
         m_formatter.immediate16u(imm);
     }
 
-#ifdef JS_CODEGEN_X86
-    void pusha()
-    {
-        spew("pusha");
-        m_formatter.oneByteOp(OP_PUSHA);
-    }
-
-    void popa()
-    {
-        spew("popa");
-        m_formatter.oneByteOp(OP_POPA);
-    }
-#endif
-
     void mfence() {
         spew("mfence");
         m_formatter.twoByteOp(OP_FENCE, (RegisterID)0, 6);
     }
 
     // Assembler admin methods:
 
     JmpDst label()
@@ -4189,17 +3408,17 @@ threeByteOpImmSimd("vblendps", VEX_PD, O
         SetRel32(code + from.offset(), code + to.offset());
     }
 
     void executableCopy(void* buffer)
     {
         memcpy(buffer, m_formatter.buffer(), size());
     }
 
-  private:
+  protected:
     static bool CAN_SIGN_EXTEND_8_32(int32_t value) { return value == (int32_t)(int8_t)value; }
     static bool CAN_SIGN_EXTEND_16_32(int32_t value) { return value == (int32_t)(int16_t)value; }
     static bool CAN_ZERO_EXTEND_8_32(int32_t value) { return value == (int32_t)(uint8_t)value; }
     static bool CAN_ZERO_EXTEND_8H_32(int32_t value) { return value == (value & 0xff00); }
     static bool CAN_ZERO_EXTEND_16_32(int32_t value) { return value == (int32_t)(uint16_t)value; }
     static bool CAN_ZERO_EXTEND_32_64(int32_t value) { return value >= 0; }
 
     // Methods for encoding SIMD instructions via either legacy SSE encoding or
@@ -4245,46 +3464,16 @@ threeByteOpImmSimd("vblendps", VEX_PD, O
     }
 
     const char* legacySSEOpName(const char* name)
     {
         MOZ_ASSERT(name[0] == 'v');
         return name + 1;
     }
 
- #ifdef JS_CODEGEN_X64
-    MOZ_WARN_UNUSED_RESULT JmpSrc
-    twoByteRipOpSimd(const char* name, VexOperandType ty, TwoByteOpcodeID opcode,
-                     XMMRegisterID src0, XMMRegisterID dst)
-    {
-        if (useLegacySSEEncoding(src0, dst)) {
-            m_formatter.legacySSEPrefix(ty);
-            m_formatter.twoByteRipOp(opcode, 0, dst);
-            JmpSrc label(m_formatter.size());
-            if (IsXMMReversedOperands(opcode))
-                spew("%-11s%s, " MEM_o32r "", legacySSEOpName(name), XMMRegName(dst), ADDR_o32r(label.offset()));
-            else
-                spew("%-11s" MEM_o32r ", %s", legacySSEOpName(name), ADDR_o32r(label.offset()), XMMRegName(dst));
-            return label;
-        }
-
-        m_formatter.twoByteRipOpVex(ty, opcode, 0, src0, dst);
-        JmpSrc label(m_formatter.size());
-        if (src0 == invalid_xmm) {
-            if (IsXMMReversedOperands(opcode))
-                spew("%-11s%s, " MEM_o32r "", name, XMMRegName(dst), ADDR_o32r(label.offset()));
-            else
-                spew("%-11s" MEM_o32r ", %s", name, ADDR_o32r(label.offset()), XMMRegName(dst));
-        } else {
-            spew("%-11s" MEM_o32r ", %s, %s", name, ADDR_o32r(label.offset()), XMMRegName(src0), XMMRegName(dst));
-        }
-        return label;
-    }
-#endif
-
     void twoByteOpSimd(const char* name, VexOperandType ty, TwoByteOpcodeID opcode,
                        XMMRegisterID rm, XMMRegisterID src0, XMMRegisterID dst)
     {
         if (useLegacySSEEncoding(src0, dst)) {
             if (IsXMMReversedOperands(opcode))
                 spew("%-11s%s, %s", legacySSEOpName(name), XMMRegName(dst), XMMRegName(rm));
             else
                 spew("%-11s%s, %s", legacySSEOpName(name), XMMRegName(rm), XMMRegName(dst));
@@ -4485,42 +3674,16 @@ threeByteOpImmSimd("vblendps", VEX_PD, O
             else
                 spew("%-11s%s, %s", name, GPReg32Name(rm), XMMRegName(dst));
         } else {
             spew("%-11s%s, %s, %s", name, GPReg32Name(rm), XMMRegName(src0), XMMRegName(dst));
         }
         m_formatter.twoByteOpVex(ty, opcode, rm, src0, dst);
     }
 
-#ifdef JS_CODEGEN_X64
-    void twoByteOpInt64Simd(const char* name, VexOperandType ty, TwoByteOpcodeID opcode,
-                            RegisterID rm, XMMRegisterID src0, XMMRegisterID dst)
-    {
-        if (useLegacySSEEncoding(src0, dst)) {
-            if (IsXMMReversedOperands(opcode))
-                spew("%-11s%s, %s", legacySSEOpName(name), XMMRegName(dst), GPRegName(rm));
-            else
-                spew("%-11s%s, %s", legacySSEOpName(name), GPRegName(rm), XMMRegName(dst));
-            m_formatter.legacySSEPrefix(ty);
-            m_formatter.twoByteOp64(opcode, rm, dst);
-            return;
-        }
-
-        if (src0 == invalid_xmm) {
-            if (IsXMMReversedOperands(opcode))
-                spew("%-11s%s, %s", name, XMMRegName(dst), GPRegName(rm));
-            else
-                spew("%-11s%s, %s", name, GPRegName(rm), XMMRegName(dst));
-        } else {
-            spew("%-11s%s, %s, %s", name, GPRegName(rm), XMMRegName(src0), XMMRegName(dst));
-        }
-        m_formatter.twoByteOpVex64(ty, opcode, rm, src0, dst);
-    }
-#endif
-
     void twoByteOpSimdInt32(const char* name, VexOperandType ty, TwoByteOpcodeID opcode,
                             XMMRegisterID rm, RegisterID dst)
     {
         if (useLegacySSEEncodingForOtherOutput()) {
             if (IsXMMReversedOperands(opcode))
                 spew("%-11s%s, %s", legacySSEOpName(name), GPReg32Name(dst), XMMRegName(rm));
             else if (opcode == OP2_MOVD_EdVd)
                 spew("%-11s%s, %s", legacySSEOpName(name), XMMRegName((XMMRegisterID)dst), GPReg32Name((RegisterID)rm));
@@ -4551,42 +3714,16 @@ threeByteOpImmSimd("vblendps", VEX_PD, O
             return;
         }
 
         spew("%-11s$0x%x, %s, %s", name, imm, XMMRegName(rm), GPReg32Name(dst));
         m_formatter.twoByteOpVex(ty, opcode, (RegisterID)rm, invalid_xmm, dst);
         m_formatter.immediate8u(imm);
     }
 
-#ifdef JS_CODEGEN_X64
-    void twoByteOpSimdInt64(const char* name, VexOperandType ty, TwoByteOpcodeID opcode,
-                            XMMRegisterID rm, RegisterID dst)
-    {
-        if (useLegacySSEEncodingForOtherOutput()) {
-            if (IsXMMReversedOperands(opcode))
-                spew("%-11s%s, %s", legacySSEOpName(name), GPRegName(dst), XMMRegName(rm));
-            else if (opcode == OP2_MOVD_EdVd)
-                spew("%-11s%s, %s", legacySSEOpName(name), XMMRegName((XMMRegisterID)dst), GPRegName((RegisterID)rm));
-            else
-                spew("%-11s%s, %s", legacySSEOpName(name), XMMRegName(rm), GPRegName(dst));
-            m_formatter.legacySSEPrefix(ty);
-            m_formatter.twoByteOp64(opcode, (RegisterID)rm, dst);
-            return;
-        }
-
-        if (IsXMMReversedOperands(opcode))
-            spew("%-11s%s, %s", name, GPRegName(dst), XMMRegName(rm));
-        else if (opcode == OP2_MOVD_EdVd)
-            spew("%-11s%s, %s", name, XMMRegName((XMMRegisterID)dst), GPRegName((RegisterID)rm));
-        else
-            spew("%-11s%s, %s", name, XMMRegName(rm), GPRegName(dst));
-        m_formatter.twoByteOpVex64(ty, opcode, (RegisterID)rm, invalid_xmm, (XMMRegisterID)dst);
-    }
-#endif
-
     void twoByteOpSimdFlags(const char* name, VexOperandType ty, TwoByteOpcodeID opcode,
                             XMMRegisterID rm, XMMRegisterID reg)
     {
         if (useLegacySSEEncodingForOtherOutput()) {
             spew("%-11s%s, %s", legacySSEOpName(name), XMMRegName(rm), XMMRegName(reg));
             m_formatter.legacySSEPrefix(ty);
             m_formatter.twoByteOp(opcode, (RegisterID)rm, reg);
             return;
--- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp
+++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp
@@ -3639,10 +3639,23 @@ CodeGeneratorX86Shared::visitAtomicTyped
 
 void
 CodeGeneratorX86Shared::visitMemoryBarrier(LMemoryBarrier* ins)
 {
     if (ins->type() & MembarStoreLoad)
         masm.storeLoadFence();
 }
 
+void
+CodeGeneratorX86Shared::setReturnDoubleRegs(LiveRegisterSet* regs)
+{
+    MOZ_ASSERT(ReturnFloat32Reg.encoding() == X86Encoding::xmm0);
+    MOZ_ASSERT(ReturnDoubleReg.encoding() == X86Encoding::xmm0);
+    MOZ_ASSERT(ReturnInt32x4Reg.encoding() == X86Encoding::xmm0);
+    MOZ_ASSERT(ReturnFloat32x4Reg.encoding() == X86Encoding::xmm0);
+    regs->add(ReturnFloat32Reg);
+    regs->add(ReturnDoubleReg);
+    regs->add(ReturnInt32x4Reg);
+    regs->add(ReturnFloat32x4Reg);
+}
+
 } // namespace jit
 } // namespace js
--- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.h
+++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.h
@@ -295,16 +295,18 @@ class CodeGeneratorX86Shared : public Co
     // Generating a result.
     template<typename S, typename T>
     void atomicBinopToTypedIntArray(AtomicOp op, Scalar::Type arrayType, const S& value,
                                     const T& mem, Register temp1, Register temp2, AnyRegister output);
 
     // Generating no result.
     template<typename S, typename T>
     void atomicBinopToTypedIntArray(AtomicOp op, Scalar::Type arrayType, const S& value, const T& mem);
+
+    void setReturnDoubleRegs(LiveRegisterSet* regs);
 };
 
 // An out-of-line bailout thunk.
 class OutOfLineBailout : public OutOfLineCodeBase<CodeGeneratorX86Shared>
 {
     LSnapshot* snapshot_;
 
   public:
--- a/js/src/jit/x86-shared/Encoding-x86-shared.h
+++ b/js/src/jit/x86-shared/Encoding-x86-shared.h
@@ -171,32 +171,36 @@ enum TwoByteOpcodeID {
     OP2_SQRTSD_VsdWsd   = 0x51,
     OP2_SQRTSS_VssWss   = 0x51,
     OP2_SQRTPS_VpsWps   = 0x51,
     OP2_RSQRTPS_VpsWps  = 0x52,
     OP2_RCPPS_VpsWps    = 0x53,
     OP2_ANDPD_VpdWpd    = 0x54,
     OP2_ORPD_VpdWpd     = 0x56,
     OP2_XORPD_VpdWpd    = 0x57,
+    OP2_PUNPCKLDQ       = 0x62,
     OP2_PCMPGTD_VdqWdq  = 0x66,
     OP2_MOVD_VdEd       = 0x6E,
     OP2_MOVDQ_VsdWsd    = 0x6F,
     OP2_MOVDQ_VdqWdq    = 0x6F,
     OP2_PSHUFD_VdqWdqIb = 0x70,
     OP2_PSLLD_UdqIb     = 0x72,
     OP2_PSRAD_UdqIb     = 0x72,
     OP2_PSRLD_UdqIb     = 0x72,
     OP2_PSRLDQ_Vd       = 0x73,
     OP2_PCMPEQW         = 0x75,
     OP2_PCMPEQD_VdqWdq  = 0x76,
+    OP2_HADDPD          = 0x7C,
     OP2_MOVD_EdVd       = 0x7E,
     OP2_MOVQ_VdWd       = 0x7E,
     OP2_MOVDQ_WdqVdq    = 0x7F,
     OP2_JCC_rel32       = 0x80,
     OP_SETCC            = 0x90,
+    OP2_SHLD            = 0xA4,
+    OP2_SHRD            = 0xAC,
     OP_FENCE            = 0xAE,
     OP2_IMUL_GvEv       = 0xAF,
     OP2_CMPXCHG_GvEb    = 0xB0,
     OP2_CMPXCHG_GvEw    = 0xB1,
     OP2_BSR_GvEv        = 0xBD,
     OP2_MOVSX_GvEb      = 0xBE,
     OP2_MOVSX_GvEw      = 0xBF,
     OP2_MOVZX_GvEb      = 0xB6,
--- a/js/src/jit/x86/Assembler-x86.h
+++ b/js/src/jit/x86/Assembler-x86.h
@@ -46,16 +46,17 @@ static MOZ_CONSTEXPR_VAR Register FrameP
 static MOZ_CONSTEXPR_VAR Register ReturnReg = eax;
 static MOZ_CONSTEXPR_VAR FloatRegister ReturnFloat32Reg = FloatRegister(X86Encoding::xmm0, FloatRegisters::Single);
 static MOZ_CONSTEXPR_VAR FloatRegister ReturnDoubleReg = FloatRegister(X86Encoding::xmm0, FloatRegisters::Double);
 static MOZ_CONSTEXPR_VAR FloatRegister ReturnInt32x4Reg = FloatRegister(X86Encoding::xmm0, FloatRegisters::Int32x4);
 static MOZ_CONSTEXPR_VAR FloatRegister ReturnFloat32x4Reg = FloatRegister(X86Encoding::xmm0, FloatRegisters::Float32x4);
 static MOZ_CONSTEXPR_VAR FloatRegister ScratchFloat32Reg = FloatRegister(X86Encoding::xmm7, FloatRegisters::Single);
 static MOZ_CONSTEXPR_VAR FloatRegister ScratchDoubleReg = FloatRegister(X86Encoding::xmm7, FloatRegisters::Double);
 static MOZ_CONSTEXPR_VAR FloatRegister ScratchSimdReg = xmm7;
+static MOZ_CONSTEXPR_VAR FloatRegister ScratchInt32x4Reg = FloatRegister(X86Encoding::xmm7, FloatRegisters::Int32x4);
 
 // Avoid ebp, which is the FramePointer, which is unavailable in some modes.
 static MOZ_CONSTEXPR_VAR Register ArgumentsRectifierReg = esi;
 static MOZ_CONSTEXPR_VAR Register CallTempReg0 = edi;
 static MOZ_CONSTEXPR_VAR Register CallTempReg1 = eax;
 static MOZ_CONSTEXPR_VAR Register CallTempReg2 = ebx;
 static MOZ_CONSTEXPR_VAR Register CallTempReg3 = ecx;
 static MOZ_CONSTEXPR_VAR Register CallTempReg4 = esi;
@@ -370,16 +371,76 @@ class Assembler : public AssemblerX86Sha
         masm.cmpl_rm_disp32(rhs.encoding(), (void*)-1);
         append(AsmJSAbsoluteLink(CodeOffsetLabel(masm.currentOffset()), lhs.kind()));
     }
     void cmpl(Imm32 rhs, AsmJSAbsoluteAddress lhs) {
         JmpSrc src = masm.cmpl_im_disp32(rhs.value, (void*)-1);
         append(AsmJSAbsoluteLink(CodeOffsetLabel(src.offset()), lhs.kind()));
     }
 
+    void adcl(Imm32 imm, Register dest) {
+        masm.adcl_ir(imm.value, dest.encoding());
+    }
+
+    void mull(Register multiplier) {
+        masm.mull_r(multiplier.encoding());
+    }
+
+    void shldl(const Imm32 imm, Register src, Register dest) {
+        masm.shldl_irr(imm.value, src.encoding(), dest.encoding());
+    }
+    void shrdl(const Imm32 imm, Register src, Register dest) {
+        masm.shrdl_irr(imm.value, src.encoding(), dest.encoding());
+    }
+
+    void vhaddpd(FloatRegister src, FloatRegister dest) {
+        MOZ_ASSERT(HasSSE2());
+        MOZ_ASSERT(src.size() == 16);
+        MOZ_ASSERT(dest.size() == 16);
+        masm.vhaddpd_rr(src.encoding(), dest.encoding());
+    }
+    void vsubpd(const Operand& src1, FloatRegister src0, FloatRegister dest) {
+        MOZ_ASSERT(HasSSE2());
+        MOZ_ASSERT(src0.size() == 16);
+        MOZ_ASSERT(dest.size() == 16);
+        switch (src1.kind()) {
+          case Operand::MEM_REG_DISP:
+            masm.vsubpd_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding());
+            break;
+          case Operand::MEM_ADDRESS32:
+            masm.vsubpd_mr(src1.address(), src0.encoding(), dest.encoding());
+            break;
+          default:
+            MOZ_CRASH("unexpected operand kind");
+        }
+    }
+
+    void vpunpckldq(FloatRegister src1, FloatRegister src0, FloatRegister dest) {
+        MOZ_ASSERT(HasSSE2());
+        MOZ_ASSERT(src0.size() == 16);
+        MOZ_ASSERT(src1.size() == 16);
+        MOZ_ASSERT(dest.size() == 16);
+        masm.vpunpckldq_rr(src1.encoding(), src0.encoding(), dest.encoding());
+    }
+    void vpunpckldq(const Operand& src1, FloatRegister src0, FloatRegister dest) {
+        MOZ_ASSERT(HasSSE2());
+        MOZ_ASSERT(src0.size() == 16);
+        MOZ_ASSERT(dest.size() == 16);
+        switch (src1.kind()) {
+          case Operand::MEM_REG_DISP:
+            masm.vpunpckldq_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding());
+            break;
+          case Operand::MEM_ADDRESS32:
+            masm.vpunpckldq_mr(src1.address(), src0.encoding(), dest.encoding());
+            break;
+          default:
+            MOZ_CRASH("unexpected operand kind");
+        }
+    }
+
     void jmp(ImmPtr target, Relocation::Kind reloc = Relocation::HARDCODED) {
         JmpSrc src = masm.jmp();
         addPendingJump(src, target, reloc);
     }
     void j(Condition cond, ImmPtr target,
            Relocation::Kind reloc = Relocation::HARDCODED) {
         JmpSrc src = masm.jCC(static_cast<X86Encoding::Condition>(cond));
         addPendingJump(src, target, reloc);
new file mode 100644
--- /dev/null
+++ b/js/src/jit/x86/BaseAssembler-x86.h
@@ -0,0 +1,170 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef jit_x86_BaseAssembler_x86_h
+#define jit_x86_BaseAssembler_x86_h
+
+#include "jit/x86-shared/BaseAssembler-x86-shared.h"
+
+namespace js {
+namespace jit {
+
+namespace X86Encoding {
+
+class BaseAssemblerX86 : public BaseAssembler
+{
+  public:
+
+    // Arithmetic operations:
+
+    void adcl_ir(int32_t imm, RegisterID dst)
+    {
+        spew("adcl       $%d, %s", imm, GPReg32Name(dst));
+        MOZ_ASSERT(CAN_SIGN_EXTEND_8_32(imm));
+        m_formatter.oneByteOp(OP_GROUP1_EvIb, dst, GROUP1_OP_ADC);
+        m_formatter.immediate8s(imm);
+    }
+
+    void adcl_im(int32_t imm, const void* addr)
+    {
+        spew("adcl       %d, %p", imm, addr);
+        if (CAN_SIGN_EXTEND_8_32(imm)) {
+            m_formatter.oneByteOp(OP_GROUP1_EvIb, addr, GROUP1_OP_ADC);
+            m_formatter.immediate8s(imm);
+        } else {
+            m_formatter.oneByteOp(OP_GROUP1_EvIz, addr, GROUP1_OP_ADC);
+            m_formatter.immediate32(imm);
+        }
+    }
+
+    using BaseAssembler::andl_im;
+    void andl_im(int32_t imm, const void* addr)
+    {
+        spew("andl       $0x%x, %p", imm, addr);
+        if (CAN_SIGN_EXTEND_8_32(imm)) {
+            m_formatter.oneByteOp(OP_GROUP1_EvIb, addr, GROUP1_OP_AND);
+            m_formatter.immediate8s(imm);
+        } else {
+            m_formatter.oneByteOp(OP_GROUP1_EvIz, addr, GROUP1_OP_AND);
+            m_formatter.immediate32(imm);
+        }
+    }
+
+    using BaseAssembler::orl_im;
+    void orl_im(int32_t imm, const void* addr)
+    {
+        spew("orl        $0x%x, %p", imm, addr);
+        if (CAN_SIGN_EXTEND_8_32(imm)) {
+            m_formatter.oneByteOp(OP_GROUP1_EvIb, addr, GROUP1_OP_OR);
+            m_formatter.immediate8s(imm);
+        } else {
+            m_formatter.oneByteOp(OP_GROUP1_EvIz, addr, GROUP1_OP_OR);
+            m_formatter.immediate32(imm);
+        }
+    }
+
+    using BaseAssembler::subl_im;
+    void subl_im(int32_t imm, const void* addr)
+    {
+        spew("subl       $%d, %p", imm, addr);
+        if (CAN_SIGN_EXTEND_8_32(imm)) {
+            m_formatter.oneByteOp(OP_GROUP1_EvIb, addr, GROUP1_OP_SUB);
+            m_formatter.immediate8s(imm);
+        } else {
+            m_formatter.oneByteOp(OP_GROUP1_EvIz, addr, GROUP1_OP_SUB);
+            m_formatter.immediate32(imm);
+        }
+    }
+
+    void shldl_irr(int32_t imm, RegisterID src, RegisterID dst)
+    {
+        MOZ_ASSERT(imm < 32);
+        spew("shldl      $%d, %s, %s", imm, GPReg32Name(src), GPReg32Name(dst));
+        m_formatter.twoByteOp8(OP2_SHLD, dst, src);
+        m_formatter.immediate8u(imm);
+    }
+
+    void shrdl_irr(int32_t imm, RegisterID src, RegisterID dst)
+    {
+        MOZ_ASSERT(imm < 32);
+        spew("shrdl      $%d, %s, %s", imm, GPReg32Name(src), GPReg32Name(dst));
+        m_formatter.twoByteOp8(OP2_SHRD, dst, src);
+        m_formatter.immediate8u(imm);
+    }
+
+    // SSE operations:
+
+    using BaseAssembler::vcvtsi2sd_mr;
+    void vcvtsi2sd_mr(const void* address, XMMRegisterID src0, XMMRegisterID dst)
+    {
+        twoByteOpSimd("vcvtsi2sd", VEX_SD, OP2_CVTSI2SD_VsdEd, address, src0, dst);
+    }
+
+    using BaseAssembler::vmovaps_mr;
+    void vmovaps_mr(const void* address, XMMRegisterID dst)
+    {
+        twoByteOpSimd("vmovaps", VEX_PS, OP2_MOVAPS_VsdWsd, address, invalid_xmm, dst);
+    }
+
+    using BaseAssembler::vmovdqa_mr;
+    void vmovdqa_mr(const void* address, XMMRegisterID dst)
+    {
+        twoByteOpSimd("vmovdqa", VEX_PD, OP2_MOVDQ_VdqWdq, address, invalid_xmm, dst);
+    }
+
+    void vhaddpd_rr(XMMRegisterID src, XMMRegisterID dst)
+    {
+        twoByteOpSimdFlags("vhaddpd", VEX_PD, OP2_HADDPD, src, dst);
+    }
+
+    void vsubpd_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst)
+    {
+        twoByteOpSimd("vsubpd", VEX_PD, OP2_SUBPS_VpsWps, src1, src0, dst);
+    }
+    void vsubpd_mr(int32_t offset, RegisterID base, XMMRegisterID src0, XMMRegisterID dst)
+    {
+        twoByteOpSimd("vsubpd", VEX_PD, OP2_SUBPS_VpsWps, offset, base, src0, dst);
+    }
+    void vsubpd_mr(const void* address, XMMRegisterID src0, XMMRegisterID dst)
+    {
+        twoByteOpSimd("vsubpd", VEX_PD, OP2_SUBPS_VpsWps, address, src0, dst);
+    }
+
+    void vpunpckldq_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) {
+        twoByteOpSimd("vpunpckldq", VEX_PD, OP2_PUNPCKLDQ, src1, src0, dst);
+    }
+    void vpunpckldq_mr(int32_t offset, RegisterID base, XMMRegisterID src0, XMMRegisterID dst)
+    {
+        twoByteOpSimd("vpunpckldq", VEX_PD, OP2_PUNPCKLDQ, offset, base, src0, dst);
+    }
+    void vpunpckldq_mr(const void* addr, XMMRegisterID src0, XMMRegisterID dst)
+    {
+        twoByteOpSimd("vpunpckldq", VEX_PD, OP2_PUNPCKLDQ, addr, src0, dst);
+    }
+
+    // Misc instructions:
+
+    void pusha()
+    {
+        spew("pusha");
+        m_formatter.oneByteOp(OP_PUSHA);
+    }
+
+    void popa()
+    {
+        spew("popa");
+        m_formatter.oneByteOp(OP_POPA);
+    }
+};
+
+typedef BaseAssemblerX86 BaseAssemblerSpecific;
+
+} // namespace X86Encoding
+
+} // namespace jit
+} // namespace js
+
+#endif /* jit_x86_BaseAssembler_x86_h */
--- a/js/src/jit/x86/CodeGenerator-x86.cpp
+++ b/js/src/jit/x86/CodeGenerator-x86.cpp
@@ -4,17 +4,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jit/x86/CodeGenerator-x86.h"
 
 #include "mozilla/Casting.h"
 #include "mozilla/DebugOnly.h"
 
-#include "jsmath.h"
 #include "jsnum.h"
 
 #include "jit/IonCaches.h"
 #include "jit/MIR.h"
 #include "jit/MIRGraph.h"
 #include "js/Conversions.h"
 #include "vm/Shape.h"
 
@@ -1100,23 +1099,8 @@ CodeGeneratorX86::visitOutOfLineTruncate
         masm.storeCallResult(output);
         masm.pop(input);
 
         restoreVolatile(output);
     }
 
     masm.jump(ool->rejoin());
 }
-
-void
-CodeGeneratorX86::visitRandom(LRandom* ins)
-{
-    Register temp = ToRegister(ins->temp());
-    Register temp2 = ToRegister(ins->temp2());
-
-    masm.loadJSContext(temp);
-
-    masm.setupUnalignedABICall(temp2);
-    masm.passABIArg(temp);
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, math_random_no_outparam), MoveOp::DOUBLE);
-
-    MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
-}
--- a/js/src/jit/x86/CodeGenerator-x86.h
+++ b/js/src/jit/x86/CodeGenerator-x86.h
@@ -67,18 +67,16 @@ class CodeGeneratorX86 : public CodeGene
     void visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar* ins);
     void visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar* ins);
     void visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr* ins);
     void visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc* ins);
 
     void visitOutOfLineTruncate(OutOfLineTruncate* ool);
     void visitOutOfLineTruncateFloat32(OutOfLineTruncateFloat32* ool);
 
-    void visitRandom(LRandom* ins);
-
   private:
     void asmJSAtomicComputeAddress(Register addrTemp, Register ptrReg, bool boundsCheck,
                                    int32_t offset, int32_t endOffset);
 };
 
 typedef CodeGeneratorX86 CodeGeneratorSpecific;
 
 } // namespace jit
--- a/js/src/jit/x86/LIR-x86.h
+++ b/js/src/jit/x86/LIR-x86.h
@@ -117,29 +117,12 @@ class LAsmJSLoadFuncPtr : public LInstru
     MAsmJSLoadFuncPtr* mir() const {
         return mir_->toAsmJSLoadFuncPtr();
     }
     const LAllocation* index() {
         return getOperand(0);
     }
 };
 
-// Math.random().
-class LRandom : public LCallInstructionHelper<1, 0, 2>
-{
-  public:
-    LIR_HEADER(Random)
-    LRandom(const LDefinition& temp, const LDefinition& temp2) {
-        setTemp(0, temp);
-        setTemp(1, temp2);
-    }
-    const LDefinition* temp() {
-        return getTemp(0);
-    }
-    const LDefinition* temp2() {
-        return getTemp(1);
-    }
-};
-
 } // namespace jit
 } // namespace js
 
 #endif /* jit_x86_LIR_x86_h */
--- a/js/src/jit/x86/Lowering-x86.cpp
+++ b/js/src/jit/x86/Lowering-x86.cpp
@@ -436,11 +436,16 @@ LIRGeneratorX86::visitSubstr(MSubstr* in
                                          tempByteOpRegister());
     define(lir, ins);
     assignSafepoint(lir, ins);
 }
 
 void
 LIRGeneratorX86::visitRandom(MRandom* ins)
 {
-    LRandom* lir = new(alloc()) LRandom(tempFixed(CallTempReg0), tempFixed(CallTempReg1));
-    defineReturn(lir, ins);
+    // eax and edx are necessary for mull.
+    LRandom *lir = new(alloc()) LRandom(tempFixed(eax),
+                                        tempFixed(edx),
+                                        temp(),
+                                        temp(),
+                                        temp());
+    defineFixed(lir, ins, LFloatReg(ReturnDoubleReg));
 }
--- a/js/src/jit/x86/MacroAssembler-x86-inl.h
+++ b/js/src/jit/x86/MacroAssembler-x86-inl.h
@@ -25,38 +25,87 @@ MacroAssembler::andPtr(Register src, Reg
 
 void
 MacroAssembler::andPtr(Imm32 imm, Register dest)
 {
     andl(imm, dest);
 }
 
 void
+MacroAssembler::and64(Imm64 imm, Register64 dest)
+{
+    andl(Imm32(imm.value & 0xFFFFFFFFL), dest.low);
+    andl(Imm32((imm.value >> 32) & 0xFFFFFFFFL), dest.high);
+}
+
+void
 MacroAssembler::orPtr(Register src, Register dest)
 {
     orl(src, dest);
 }
 
 void
 MacroAssembler::orPtr(Imm32 imm, Register dest)
 {
     orl(imm, dest);
 }
 
 void
+MacroAssembler::or64(Register64 src, Register64 dest)
+{
+    orl(src.low, dest.low);
+    orl(src.high, dest.high);
+}
+
+void
 MacroAssembler::xorPtr(Register src, Register dest)
 {
     xorl(src, dest);
 }
 
 void
 MacroAssembler::xorPtr(Imm32 imm, Register dest)
 {
     xorl(imm, dest);
 }
 
+// ===============================================================
+// Shift functions
+
+void
+MacroAssembler::lshiftPtr(Imm32 imm, Register dest)
+{
+    shll(imm, dest);
+}
+
+void
+MacroAssembler::lshift64(Imm32 imm, Register64 dest)
+{
+    shldl(imm, dest.low, dest.high);
+    shll(imm, dest.low);
+}
+
+void
+MacroAssembler::rshiftPtr(Imm32 imm, Register dest)
+{
+    shrl(imm, dest);
+}
+
+void
+MacroAssembler::rshiftPtrArithmetic(Imm32 imm, Register dest)
+{
+    sarl(imm, dest);
+}
+
+void
+MacroAssembler::rshift64(Imm32 imm, Register64 dest)
+{
+    shrdl(imm, dest.high, dest.low);
+    shrl(imm, dest.high);
+}
+
 //}}} check_macroassembler_style
 // ===============================================================
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_x86_MacroAssembler_x86_inl_h */
--- a/js/src/jit/x86/MacroAssembler-x86.cpp
+++ b/js/src/jit/x86/MacroAssembler-x86.cpp
@@ -1,30 +1,87 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jit/x86/MacroAssembler-x86.h"
 
+#include "mozilla/Alignment.h"
 #include "mozilla/Casting.h"
 
 #include "jit/Bailouts.h"
 #include "jit/BaselineFrame.h"
 #include "jit/JitFrames.h"
 #include "jit/MacroAssembler.h"
 #include "jit/MoveEmitter.h"
 
 #include "jsscriptinlines.h"
 #include "jit/MacroAssembler-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
+// vpunpckldq requires 16-byte boundary for memory operand.
+// See convertUInt64ToDouble for the details.
+MOZ_ALIGNED_DECL(static const uint64_t, 16) TO_DOUBLE[4] = {
+    0x4530000043300000LL,
+    0x0LL,
+    0x4330000000000000LL,
+    0x4530000000000000LL
+};
+
+void
+MacroAssemblerX86::convertUInt64ToDouble(Register64 src, Register temp, FloatRegister dest)
+{
+    // Following operation uses entire 128-bit of dest XMM register.
+    // Currently higher 64-bit is free when we have access to lower 64-bit.
+    MOZ_ASSERT(dest.size() == 8);
+    FloatRegister dest128 = FloatRegister(dest.encoding(), FloatRegisters::Int32x4);
+
+    // Assume that src is represented as following:
+    //   src      = 0x HHHHHHHH LLLLLLLL
+
+    // Move src to dest (=dest128) and ScratchInt32x4Reg (=scratch):
+    //   dest     = 0x 00000000 00000000  00000000 LLLLLLLL
+    //   scratch  = 0x 00000000 00000000  00000000 HHHHHHHH
+    vmovd(src.low, dest128);
+    vmovd(src.high, ScratchInt32x4Reg);
+
+    // Unpack and interleave dest and scratch to dest:
+    //   dest     = 0x 00000000 00000000  HHHHHHHH LLLLLLLL
+    vpunpckldq(ScratchInt32x4Reg, dest128, dest128);
+
+    // Unpack and interleave dest and a constant C1 to dest:
+    //   C1       = 0x 00000000 00000000  45300000 43300000
+    //   dest     = 0x 45300000 HHHHHHHH  43300000 LLLLLLLL
+    // here, each 64-bit part of dest represents following double:
+    //   HI(dest) = 0x 1.00000HHHHHHHH * 2**84 == 2**84 + 0x HHHHHHHH 00000000
+    //   LO(dest) = 0x 1.00000LLLLLLLL * 2**52 == 2**52 + 0x 00000000 LLLLLLLL
+    movePtr(ImmPtr(TO_DOUBLE), temp);
+    vpunpckldq(Operand(temp, 0), dest128, dest128);
+
+    // Subtract a constant C2 from dest, for each 64-bit part:
+    //   C2       = 0x 45300000 00000000  43300000 00000000
+    // here, each 64-bit part of C2 represents following double:
+    //   HI(C2)   = 0x 1.0000000000000 * 2**84 == 2**84
+    //   LO(C2)   = 0x 1.0000000000000 * 2**52 == 2**52
+    // after the operation each 64-bit part of dest represents following:
+    //   HI(dest) = double(0x HHHHHHHH 00000000)
+    //   LO(dest) = double(0x 00000000 LLLLLLLL)
+    vsubpd(Operand(temp, sizeof(uint64_t) * 2), dest128, dest128);
+
+    // Add HI(dest) and LO(dest) in double and store it into LO(dest),
+    //   LO(dest) = double(0x HHHHHHHH 00000000) + double(0x 00000000 LLLLLLLL)
+    //            = double(0x HHHHHHHH LLLLLLLL)
+    //            = double(src)
+    vhaddpd(dest128, dest128);
+}
+
 MacroAssemblerX86::Double*
 MacroAssemblerX86::getDouble(double d)
 {
     if (!doubleMap_.initialized()) {
         enoughMemory_ &= doubleMap_.init();
         if (!enoughMemory_)
             return nullptr;
     }
--- a/js/src/jit/x86/MacroAssembler-x86.h
+++ b/js/src/jit/x86/MacroAssembler-x86.h
@@ -235,16 +235,24 @@ class MacroAssemblerX86 : public MacroAs
     void pushValue(JSValueType type, Register reg) {
         push(ImmTag(JSVAL_TYPE_TO_TAG(type)));
         push(reg);
     }
     void pushValue(const Address& addr) {
         push(tagOf(addr));
         push(payloadOfAfterStackPush(addr));
     }
+    void push64(Register64 src) {
+        push(src.high);
+        push(src.low);
+    }
+    void pop64(Register64 dest) {
+        pop(dest.low);
+        pop(dest.high);
+    }
     void storePayload(const Value& val, Operand dest) {
         jsval_layout jv = JSVAL_TO_IMPL(val);
         if (val.isMarkable())
             movl(ImmGCPtr((gc::Cell*)jv.s.payload.ptr), ToPayload(dest));
         else
             movl(Imm32(jv.s.payload.i32), ToPayload(dest));
     }
     void storePayload(Register src, Operand dest) {
@@ -602,31 +610,67 @@ class MacroAssemblerX86 : public MacroAs
         add32(imm, Operand(dest));
     }
     void addPtr(Imm32 imm, const Operand& dest) {
         add32(imm, dest);
     }
     void addPtr(const Address& src, Register dest) {
         addl(Operand(src), dest);
     }
+    void add64(Imm32 imm, Register64 dest) {
+        addl(imm, dest.low);
+        adcl(Imm32(0), dest.high);
+    }
     void subPtr(Imm32 imm, Register dest) {
         sub32(imm, dest);
     }
     void subPtr(Register src, Register dest) {
         sub32(src, dest);
     }
     void subPtr(const Address& addr, Register dest) {
         sub32(Operand(addr), dest);
     }
     void subPtr(Register src, const Address& dest) {
         sub32(src, Operand(dest));
     }
     void mulBy3(const Register& src, const Register& dest) {
         lea(Operand(src, src, TimesTwo), dest);
     }
+    // Note: this function clobbers eax and edx.
+    void mul64(Imm64 imm, const Register64& dest) {
+        // LOW32  = LOW(LOW(dest) * LOW(imm));
+        // HIGH32 = LOW(HIGH(dest) * LOW(imm)) [multiply imm into upper bits]
+        //        + LOW(LOW(dest) * HIGH(imm)) [multiply dest into upper bits]
+        //        + HIGH(LOW(dest) * LOW(imm)) [carry]
+
+        MOZ_ASSERT(dest.low != eax && dest.low != edx);
+        MOZ_ASSERT(dest.high != eax && dest.high != edx);
+
+        // HIGH(dest) = LOW(HIGH(dest) * LOW(imm));
+        movl(Imm32(imm.value & 0xFFFFFFFFL), edx);
+        imull(edx, dest.high);
+
+        // edx:eax = LOW(dest) * LOW(imm);
+        movl(Imm32(imm.value & 0xFFFFFFFFL), edx);
+        movl(dest.low, eax);
+        mull(edx);
+
+        // HIGH(dest) += edx;
+        addl(edx, dest.high);
+
+        // HIGH(dest) += LOW(LOW(dest) * HIGH(imm));
+        if (((imm.value >> 32) & 0xFFFFFFFFL) == 5)
+            leal(Operand(dest.low, dest.low, TimesFour), edx);
+        else
+            MOZ_CRASH("Unsupported imm");
+        addl(edx, dest.high);
+
+        // LOW(dest) = eax;
+        movl(eax, dest.low);
+    }
 
     void branch32(Condition cond, AbsoluteAddress lhs, Imm32 rhs, Label* label) {
         cmp32(Operand(lhs), rhs);
         j(cond, label);
     }
     void branch32(Condition cond, AsmJSAbsoluteAddress lhs, Imm32 rhs, Label* label) {
         cmpl(rhs, lhs);
         j(cond, label);
@@ -707,28 +751,44 @@ class MacroAssemblerX86 : public MacroAs
         testPtr(Operand(lhs), imm);
         j(cond, label);
     }
     void decBranchPtr(Condition cond, Register lhs, Imm32 imm, Label* label) {
         subPtr(imm, lhs);
         j(cond, label);
     }
 
+    void branchTest64(Condition cond, Register64 lhs, Register64 rhs, Register temp, Label* label) {
+        if (cond == Assembler::Zero) {
+            MOZ_ASSERT(lhs.low == rhs.low);
+            MOZ_ASSERT(lhs.high == rhs.high);
+            movl(lhs.low, temp);
+            orl(lhs.high, temp);
+            branchTestPtr(cond, temp, temp, label);
+        } else {
+            MOZ_CRASH("Unsupported condition");
+        }
+    }
+
     void movePtr(ImmWord imm, Register dest) {
         movl(Imm32(imm.value), dest);
     }
     void movePtr(ImmPtr imm, Register dest) {
         movl(imm, dest);
     }
     void movePtr(AsmJSImmPtr imm, Register dest) {
         mov(imm, dest);
     }
     void movePtr(ImmGCPtr imm, Register dest) {
         movl(imm, dest);
     }
+    void move64(Register64 src, Register64 dest) {
+        movl(src.low, dest.low);
+        movl(src.high, dest.high);
+    }
     void loadPtr(const Address& address, Register dest) {
         movl(Operand(address), dest);
     }
     void loadPtr(const Operand& src, Register dest) {
         movl(src, dest);
     }
     void loadPtr(const BaseIndex& src, Register dest) {
         movl(Operand(src), dest);
@@ -737,16 +797,20 @@ class MacroAssemblerX86 : public MacroAs
         movl(Operand(address), dest);
     }
     void loadPrivate(const Address& src, Register dest) {
         movl(payloadOf(src), dest);
     }
     void load32(AbsoluteAddress address, Register dest) {
         movl(Operand(address), dest);
     }
+    void load64(const Address& address, Register64 dest) {
+        movl(Operand(address), dest.low);
+        movl(Operand(Address(address.base, address.offset + 4)), dest.high);
+    }
     template <typename T>
     void storePtr(ImmWord imm, T address) {
         movl(Imm32(imm.value), Operand(address));
     }
     template <typename T>
     void storePtr(ImmPtr imm, T address) {
         storePtr(ImmWord(uintptr_t(imm.value)), address);
     }
@@ -764,16 +828,20 @@ class MacroAssemblerX86 : public MacroAs
         movl(src, dest);
     }
     void storePtr(Register src, AbsoluteAddress address) {
         movl(src, Operand(address));
     }
     void store32(Register src, AbsoluteAddress address) {
         movl(src, Operand(address));
     }
+    void store64(Register64 src, Address address) {
+        movl(src.low, Operand(address));
+        movl(src.high, Operand(Address(address.base, address.offset + 4)));
+    }
 
     void setStackArg(Register reg, uint32_t arg) {
         movl(reg, Operand(esp, arg * sizeof(intptr_t)));
     }
 
     // Type testing instructions can take a tag in a register or a
     // ValueOperand.
     template <typename T>
@@ -1049,26 +1117,16 @@ class MacroAssemblerX86 : public MacroAs
             return;
           case 1:
             store8(value.payloadReg(), address);
             return;
           default: MOZ_CRASH("Bad payload width");
         }
     }
 
-    void rshiftPtr(Imm32 imm, Register dest) {
-        shrl(imm, dest);
-    }
-    void rshiftPtrArithmetic(Imm32 imm, Register dest) {
-        sarl(imm, dest);
-    }
-    void lshiftPtr(Imm32 imm, Register dest) {
-        shll(imm, dest);
-    }
-
     void loadInstructionPointerAfterCall(Register dest) {
         movl(Operand(StackPointer, 0x0), dest);
     }
 
     // Note: this function clobbers the source register.
     void convertUInt32ToDouble(Register src, FloatRegister dest) {
         // src is [0, 2^32-1]
         subl(Imm32(0x80000000), src);
@@ -1082,16 +1140,23 @@ class MacroAssemblerX86 : public MacroAs
     }
 
     // Note: this function clobbers the source register.
     void convertUInt32ToFloat32(Register src, FloatRegister dest) {
         convertUInt32ToDouble(src, dest);
         convertDoubleToFloat32(dest, dest);
     }
 
+    void convertUInt64ToDouble(Register64 src, Register temp, FloatRegister dest);
+
+    void mulDoublePtr(ImmPtr imm, Register temp, FloatRegister dest) {
+        movl(imm, temp);
+        vmulsd(Operand(temp, 0), dest, dest);
+    }
+
     void inc64(AbsoluteAddress dest) {
         addl(Imm32(1), Operand(dest));
         Label noOverflow;
         j(NonZero, &noOverflow);
         addl(Imm32(1), Operand(dest.offset(4)));
         bind(&noOverflow);
     }
 
--- a/media/mtransport/build/moz.build
+++ b/media/mtransport/build/moz.build
@@ -1,14 +1,16 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+include("/ipc/chromium/chromium-config.mozbuild")
+
 EXPORTS.mtransport += [
     '../dtlsidentity.h',
     '../m_cpp_utils.h',
     '../nricectx.h',
     '../nricemediastream.h',
     '../nriceresolverfake.h',
     '../rlogringbuffer.h',
     '../runnable_utils.h',
--- a/media/mtransport/moz.build
+++ b/media/mtransport/moz.build
@@ -1,14 +1,16 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+include("/ipc/chromium/chromium-config.mozbuild")
+
 DIRS += [
     '/media/mtransport/third_party',
     '/media/mtransport/build',
     '/media/mtransport/testlib',
 ]
 if CONFIG['OS_TARGET'] != 'WINNT' and CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk':
     DIRS += [
         '/media/mtransport/standalone',
--- a/media/mtransport/nr_socket_prsock.cpp
+++ b/media/mtransport/nr_socket_prsock.cpp
@@ -101,16 +101,64 @@ nrappkit copyright:
 #include "nsISocketTransportService.h"
 #include "nsNetCID.h"
 #include "nsISupportsImpl.h"
 #include "nsServiceManagerUtils.h"
 #include "nsComponentManagerUtils.h"
 #include "nsXPCOM.h"
 #include "nsXULAppAPI.h"
 #include "runnable_utils.h"
+#include "mozilla/SyncRunnable.h"
+#include "nsTArray.h"
+#include "mozilla/dom/TCPSocketBinding.h"
+#include "nsITCPSocketCallback.h"
+
+#if defined(MOZILLA_INTERNAL_API) && !defined(MOZILLA_XPCOMRT_API)
+// csi_platform.h deep in nrappkit defines LOG_INFO and LOG_WARNING
+#ifdef LOG_INFO
+#define LOG_TEMP_INFO LOG_INFO
+#undef LOG_INFO
+#endif
+#ifdef LOG_WARNING
+#define LOG_TEMP_WARNING LOG_WARNING
+#undef LOG_WARNING
+#endif
+#if defined(LOG_DEBUG)
+#define LOG_TEMP_DEBUG LOG_DEBUG
+#undef LOG_DEBUG
+#endif
+#undef strlcpy
+
+// TCPSocketChild.h doesn't include TypedArray.h
+namespace mozilla {
+namespace dom {
+class ArrayBuffer;
+}
+}
+#include "mozilla/dom/network/TCPSocketChild.h"
+
+#ifdef LOG_TEMP_INFO
+#define LOG_INFO LOG_TEMP_INFO
+#endif
+#ifdef LOG_TEMP_WARNING
+#define LOG_WARNING LOG_TEMP_WARNING
+#endif
+
+#ifdef LOG_TEMP_DEBUG
+#define LOG_DEBUG LOG_TEMP_DEBUG
+#endif
+#ifdef XP_WIN
+#ifdef LOG_DEBUG
+#undef LOG_DEBUG
+#endif
+// cloned from csi_platform.h.  Win32 doesn't like how we hide symbols
+#define LOG_DEBUG 7
+#endif
+#endif
+
 
 extern "C" {
 #include "nr_api.h"
 #include "async_wait.h"
 #include "nr_socket.h"
 #include "nr_socket_local.h"
 #include "stun_hint.h"
 }
@@ -198,16 +246,41 @@ private:
 static StaticRefPtr<SingletonThreadHolder> sThread;
 
 static void ClearSingletonOnShutdown()
 {
   ClearOnShutdown(&sThread);
 }
 #endif
 
+static nsIThread* GetIOThreadAndAddUse_s()
+{
+  // Always runs on STS thread!
+#if defined(MOZILLA_INTERNAL_API) && !defined(MOZILLA_XPCOMRT_API)
+  // We need to safely release this on shutdown to avoid leaks
+  if (!sThread) {
+    sThread = new SingletonThreadHolder(NS_LITERAL_CSTRING("mtransport"));
+    NS_DispatchToMainThread(mozilla::WrapRunnableNM(&ClearSingletonOnShutdown));
+  }
+  // Mark that we're using the shared thread and need it to stick around
+  sThread->AddUse();
+  return sThread->GetThread();
+#else
+  static nsCOMPtr<nsIThread> sThread;
+  if (!sThread) {
+    (void) NS_NewNamedThread("mtransport", getter_AddRefs(sThread));
+  }
+  return sThread;
+#endif
+}
+
+NrSocketIpc::NrSocketIpc(nsIEventTarget *aThread)
+  : io_thread_(aThread)
+{}
+
 static TimeStamp nr_socket_short_term_violation_time;
 static TimeStamp nr_socket_long_term_violation_time;
 
 TimeStamp NrSocketBase::short_term_violation_time() {
   return nr_socket_short_term_violation_time;
 }
 
 TimeStamp NrSocketBase::long_term_violation_time() {
@@ -919,133 +992,111 @@ int NrSocket::accept(nr_transport_addr *
 abort:
   if (_status) {
     delete sock;
   }
 
   return(_status);
 }
 
-NS_IMPL_ISUPPORTS(NrSocketIpcProxy, nsIUDPSocketInternal)
+NS_IMPL_ISUPPORTS(NrUdpSocketIpcProxy, nsIUDPSocketInternal)
 
 nsresult
-NrSocketIpcProxy::Init(const nsRefPtr<NrSocketIpc>& socket)
+NrUdpSocketIpcProxy::Init(const nsRefPtr<NrUdpSocketIpc>& socket)
 {
   nsresult rv;
   sts_thread_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
   if (NS_FAILED(rv)) {
     MOZ_ASSERT(false, "Failed to get STS thread");
     return rv;
   }
 
   socket_ = socket;
   return NS_OK;
 }
 
-NrSocketIpcProxy::~NrSocketIpcProxy()
+NrUdpSocketIpcProxy::~NrUdpSocketIpcProxy()
 {
   // Send our ref to STS to be released
   RUN_ON_THREAD(sts_thread_,
                 mozilla::WrapRelease(socket_.forget()),
                 NS_DISPATCH_NORMAL);
 }
 
 // IUDPSocketInternal interfaces
 // callback while error happened in UDP socket operation
-NS_IMETHODIMP NrSocketIpcProxy::CallListenerError(const nsACString &message,
-                                                  const nsACString &filename,
-                                                  uint32_t line_number) {
+NS_IMETHODIMP NrUdpSocketIpcProxy::CallListenerError(const nsACString &message,
+                                                     const nsACString &filename,
+                                                     uint32_t line_number) {
   return socket_->CallListenerError(message, filename, line_number);
 }
 
 // callback while receiving UDP packet
-NS_IMETHODIMP NrSocketIpcProxy::CallListenerReceivedData(const nsACString &host,
-                                                         uint16_t port,
-                                                         const uint8_t *data,
-                                                         uint32_t data_length) {
+NS_IMETHODIMP NrUdpSocketIpcProxy::CallListenerReceivedData(const nsACString &host,
+                                                            uint16_t port,
+                                                            const uint8_t *data,
+                                                            uint32_t data_length) {
   return socket_->CallListenerReceivedData(host, port, data, data_length);
 }
 
 // callback while UDP socket is opened
-NS_IMETHODIMP NrSocketIpcProxy::CallListenerOpened() {
+NS_IMETHODIMP NrUdpSocketIpcProxy::CallListenerOpened() {
   return socket_->CallListenerOpened();
 }
 
 // callback while UDP socket is closed
-NS_IMETHODIMP NrSocketIpcProxy::CallListenerClosed() {
+NS_IMETHODIMP NrUdpSocketIpcProxy::CallListenerClosed() {
   return socket_->CallListenerClosed();
 }
 
-// NrSocketIpc Implementation
-NrSocketIpc::NrSocketIpc()
-    : err_(false),
-      state_(NR_INIT),
-      io_thread_(GetIOThreadAndAddUse_s()),
-      monitor_("NrSocketIpc") {
+// NrUdpSocketIpc Implementation
+NrUdpSocketIpc::NrUdpSocketIpc()
+  : NrSocketIpc(GetIOThreadAndAddUse_s()),
+    monitor_("NrUdpSocketIpc"),
+    err_(false),
+    state_(NR_INIT) {
 }
 
-NrSocketIpc::~NrSocketIpc()
+NrUdpSocketIpc::~NrUdpSocketIpc()
 {
   // also guarantees socket_child_ is released from the io_thread, and
   // tells the SingletonThreadHolder we're done with it
 
 #if defined(MOZILLA_INTERNAL_API) && !defined(MOZILLA_XPCOMRT_API)
   // close(), but transfer the socket_child_ reference to die as well
   RUN_ON_THREAD(io_thread_,
-                mozilla::WrapRunnableNM(&NrSocketIpc::release_child_i,
+                mozilla::WrapRunnableNM(&NrUdpSocketIpc::release_child_i,
                                         socket_child_.forget().take(),
                                         sts_thread_),
                 NS_DISPATCH_NORMAL);
 #endif
 }
 
-/* static */
-nsIThread* NrSocketIpc::GetIOThreadAndAddUse_s()
-{
-  // Always runs on STS thread!
-#if defined(MOZILLA_INTERNAL_API) && !defined(MOZILLA_XPCOMRT_API)
-  // We need to safely release this on shutdown to avoid leaks
-  if (!sThread) {
-    sThread = new SingletonThreadHolder(NS_LITERAL_CSTRING("mtransport"));
-    NS_DispatchToMainThread(mozilla::WrapRunnableNM(&ClearSingletonOnShutdown));
-  }
-  // Mark that we're using the shared thread and need it to stick around
-  sThread->AddUse();
-  return sThread->GetThread();
-#else
-  static nsCOMPtr<nsIThread> sThread;
-  if (!sThread) {
-    (void) NS_NewNamedThread("mtransport", getter_AddRefs(sThread));
-  }
-  return sThread;
-#endif
-}
-
 // IUDPSocketInternal interfaces
 // callback while error happened in UDP socket operation
-NS_IMETHODIMP NrSocketIpc::CallListenerError(const nsACString &message,
-                                             const nsACString &filename,
-                                             uint32_t line_number) {
+NS_IMETHODIMP NrUdpSocketIpc::CallListenerError(const nsACString &message,
+                                                const nsACString &filename,
+                                                uint32_t line_number) {
   ASSERT_ON_THREAD(io_thread_);
 
   r_log(LOG_GENERIC, LOG_ERR, "UDP socket error:%s at %s:%d",
         message.BeginReading(), filename.BeginReading(), line_number );
 
   ReentrantMonitorAutoEnter mon(monitor_);
   err_ = true;
   monitor_.NotifyAll();
 
   return NS_OK;
 }
 
 // callback while receiving UDP packet
-NS_IMETHODIMP NrSocketIpc::CallListenerReceivedData(const nsACString &host,
-                                                    uint16_t port,
-                                                    const uint8_t *data,
-                                                    uint32_t data_length) {
+NS_IMETHODIMP NrUdpSocketIpc::CallListenerReceivedData(const nsACString &host,
+                                                       uint16_t port,
+                                                       const uint8_t *data,
+                                                       uint32_t data_length) {
   ASSERT_ON_THREAD(io_thread_);
 
   PRNetAddr addr;
   memset(&addr, 0, sizeof(addr));
 
   {
     ReentrantMonitorAutoEnter mon(monitor_);
 
@@ -1062,25 +1113,25 @@ NS_IMETHODIMP NrSocketIpc::CallListenerR
       return NS_OK;
     }
   }
 
   nsAutoPtr<DataBuffer> buf(new DataBuffer(data, data_length));
   RefPtr<nr_udp_message> msg(new nr_udp_message(addr, buf));
 
   RUN_ON_THREAD(sts_thread_,
-                mozilla::WrapRunnable(nsRefPtr<NrSocketIpc>(this),
-                                      &NrSocketIpc::recv_callback_s,
+                mozilla::WrapRunnable(nsRefPtr<NrUdpSocketIpc>(this),
+                                      &NrUdpSocketIpc::recv_callback_s,
                                       msg),
                 NS_DISPATCH_NORMAL);
   return NS_OK;
 }
 
 // callback while UDP socket is opened
-NS_IMETHODIMP NrSocketIpc::CallListenerOpened() {
+NS_IMETHODIMP NrUdpSocketIpc::CallListenerOpened() {
   ASSERT_ON_THREAD(io_thread_);
   ReentrantMonitorAutoEnter mon(monitor_);
 
   uint16_t port;
   if (NS_FAILED(socket_child_->GetLocalPort(&port))) {
     err_ = true;
     MOZ_ASSERT(false, "Failed to get local port");
     return NS_OK;
@@ -1124,68 +1175,64 @@ NS_IMETHODIMP NrSocketIpc::CallListenerO
   }
 
   mon.NotifyAll();
 
   return NS_OK;
 }
 
 // callback while UDP socket is closed
-NS_IMETHODIMP NrSocketIpc::CallListenerClosed() {
+NS_IMETHODIMP NrUdpSocketIpc::CallListenerClosed() {
   ASSERT_ON_THREAD(io_thread_);
 
   ReentrantMonitorAutoEnter mon(monitor_);
 
   MOZ_ASSERT(state_ == NR_CONNECTED || state_ == NR_CLOSING);
   state_ = NR_CLOSED;
 
   return NS_OK;
 }
 
-// nr_socket public APIs
-int NrSocketIpc::create(nr_transport_addr *addr) {
+//
+// NrSocketBase methods.
+//
+int NrUdpSocketIpc::create(nr_transport_addr *addr) {
   ASSERT_ON_THREAD(sts_thread_);
 
   int r, _status;
   nsresult rv;
   int32_t port;
   nsCString host;
 
   ReentrantMonitorAutoEnter mon(monitor_);
 
   if (state_ != NR_INIT) {
     ABORT(R_INTERNAL);
   }
 
-  // Bug 950660: Remote TCP socket is not supported yet.
-  if (NS_WARN_IF(addr->protocol != IPPROTO_UDP)) {
-    MOZ_ASSERT(false, "NrSocket over TCP is not e10s ready, see Bug 950660");
-    ABORT(R_INTERNAL);
-  }
-
   sts_thread_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
   if (NS_FAILED(rv)) {
     MOZ_ASSERT(false, "Failed to get STS thread");
     ABORT(R_INTERNAL);
   }
 
   if ((r=nr_transport_addr_get_addrstring_and_port(addr, &host, &port))) {
     ABORT(r);
   }
 
-  // wildcard address will be resolved at NrSocketIpc::CallListenerVoid
+  // wildcard address will be resolved at NrUdpSocketIpc::CallListenerVoid
   if ((r=nr_transport_addr_copy(&my_addr_, addr))) {
     ABORT(r);
   }
 
   state_ = NR_CONNECTING;
 
   RUN_ON_THREAD(io_thread_,
-                mozilla::WrapRunnable(nsRefPtr<NrSocketIpc>(this),
-                                      &NrSocketIpc::create_i,
+                mozilla::WrapRunnable(nsRefPtr<NrUdpSocketIpc>(this),
+                                      &NrUdpSocketIpc::create_i,
                                       host, static_cast<uint16_t>(port)),
                 NS_DISPATCH_NORMAL);
 
   // Wait until socket creation complete.
   mon.Wait();
 
   if (err_) {
     ABORT(R_INTERNAL);
@@ -1193,17 +1240,17 @@ int NrSocketIpc::create(nr_transport_add
 
   state_ = NR_CONNECTED;
 
   _status = 0;
 abort:
   return(_status);
 }
 
-int NrSocketIpc::sendto(const void *msg, size_t len, int flags,
+int NrUdpSocketIpc::sendto(const void *msg, size_t len, int flags,
                         nr_transport_addr *to) {
   ASSERT_ON_THREAD(sts_thread_);
 
   ReentrantMonitorAutoEnter mon(monitor_);
 
   //If send err happened before, simply return the error.
   if (err_) {
     return R_IO_ERROR;
@@ -1217,40 +1264,40 @@ int NrSocketIpc::sendto(const void *msg,
   net::NetAddr addr;
   if ((r=nr_transport_addr_to_netaddr(to, &addr))) {
     return r;
   }
 
   nsAutoPtr<DataBuffer> buf(new DataBuffer(static_cast<const uint8_t*>(msg), len));
 
   RUN_ON_THREAD(io_thread_,
-                mozilla::WrapRunnable(nsRefPtr<NrSocketIpc>(this),
-                                      &NrSocketIpc::sendto_i,
+                mozilla::WrapRunnable(nsRefPtr<NrUdpSocketIpc>(this),
+                                      &NrUdpSocketIpc::sendto_i,
                                       addr, buf),
                 NS_DISPATCH_NORMAL);
   return 0;
 }
 
-void NrSocketIpc::close() {
+void NrUdpSocketIpc::close() {
   ASSERT_ON_THREAD(sts_thread_);
 
   ReentrantMonitorAutoEnter mon(monitor_);
   state_ = NR_CLOSING;
 
   RUN_ON_THREAD(io_thread_,
-                mozilla::WrapRunnable(nsRefPtr<NrSocketIpc>(this),
-                                      &NrSocketIpc::close_i),
+                mozilla::WrapRunnable(nsRefPtr<NrUdpSocketIpc>(this),
+                                      &NrUdpSocketIpc::close_i),
                 NS_DISPATCH_NORMAL);
 
   //remove all enqueued messages
   std::queue<RefPtr<nr_udp_message> > empty;
   std::swap(received_msgs_, empty);
 }
 
-int NrSocketIpc::recvfrom(void *buf, size_t maxlen, size_t *len, int flags,
+int NrUdpSocketIpc::recvfrom(void *buf, size_t maxlen, size_t *len, int flags,
                           nr_transport_addr *from) {
   ASSERT_ON_THREAD(sts_thread_);
 
   ReentrantMonitorAutoEnter mon(monitor_);
 
   int r, _status;
   uint32_t consumed_len;
 
@@ -1284,77 +1331,78 @@ int NrSocketIpc::recvfrom(void *buf, siz
     *len = consumed_len;
   }
 
   _status = 0;
 abort:
   return(_status);
 }
 
-int NrSocketIpc::getaddr(nr_transport_addr *addrp) {
+int NrUdpSocketIpc::getaddr(nr_transport_addr *addrp) {
   ASSERT_ON_THREAD(sts_thread_);
 
   ReentrantMonitorAutoEnter mon(monitor_);
 
   if (state_ != NR_CONNECTED) {
     return R_INTERNAL;
   }
 
   return nr_transport_addr_copy(addrp, &my_addr_);
 }
 
-int NrSocketIpc::connect(nr_transport_addr *addr) {
+int NrUdpSocketIpc::connect(nr_transport_addr *addr) {
   MOZ_ASSERT(false);
   return R_INTERNAL;
 }
 
-int NrSocketIpc::write(const void *msg, size_t len, size_t *written) {
+int NrUdpSocketIpc::write(const void *msg, size_t len, size_t *written) {
   MOZ_ASSERT(false);
   return R_INTERNAL;
 }
 
-int NrSocketIpc::read(void* buf, size_t maxlen, size_t *len) {
+int NrUdpSocketIpc::read(void* buf, size_t maxlen, size_t *len) {
   MOZ_ASSERT(false);
   return R_INTERNAL;
 }
 
-int NrSocketIpc::listen(int backlog) {
+int NrUdpSocketIpc::listen(int backlog) {
   MOZ_ASSERT(false);
   return R_INTERNAL;
 }
 
-int NrSocketIpc::accept(nr_transport_addr *addrp, nr_socket **sockp) {
+int NrUdpSocketIpc::accept(nr_transport_addr *addrp, nr_socket **sockp) {
   MOZ_ASSERT(false);
   return R_INTERNAL;
 }
 
 // IO thread executors
-void NrSocketIpc::create_i(const nsACString &host, const uint16_t port) {
+void NrUdpSocketIpc::create_i(const nsACString &host, const uint16_t port) {
   ASSERT_ON_THREAD(io_thread_);
 
   nsresult rv;
   nsCOMPtr<nsIUDPSocketChild> socketChild = do_CreateInstance("@mozilla.org/udp-socket-child;1", &rv);
   if (NS_FAILED(rv)) {
+    ReentrantMonitorAutoEnter mon(monitor_);
     err_ = true;
     MOZ_ASSERT(false, "Failed to create UDPSocketChild");
     return;
   }
 
   // This can spin the event loop; don't do that with the monitor held
   socketChild->SetBackgroundSpinsEvents();
 
   ReentrantMonitorAutoEnter mon(monitor_);
   if (!socket_child_) {
     socket_child_ = socketChild;
     socket_child_->SetFilterName(nsCString("stun"));
   } else {
     socketChild = nullptr;
   }
 
-  nsRefPtr<NrSocketIpcProxy> proxy(new NrSocketIpcProxy);
+  nsRefPtr<NrUdpSocketIpcProxy> proxy(new NrUdpSocketIpcProxy);
   rv = proxy->Init(this);
   if (NS_FAILED(rv)) {
     err_ = true;
     mon.NotifyAll();
     return;
   }
 
   // XXX bug 1126232 - don't use null Principal!
@@ -1363,65 +1411,64 @@ void NrSocketIpc::create_i(const nsACStr
                                     /* loopback = */ false))) {
     err_ = true;
     MOZ_ASSERT(false, "Failed to create UDP socket");
     mon.NotifyAll();
     return;
   }
 }
 
-void NrSocketIpc::sendto_i(const net::NetAddr &addr, nsAutoPtr<DataBuffer> buf) {
+void NrUdpSocketIpc::sendto_i(const net::NetAddr &addr, nsAutoPtr<DataBuffer> buf) {
   ASSERT_ON_THREAD(io_thread_);
 
+  ReentrantMonitorAutoEnter mon(monitor_);
+
   if (!socket_child_) {
     MOZ_ASSERT(false);
     err_ = true;
     return;
   }
-
-  ReentrantMonitorAutoEnter mon(monitor_);
-
   if (NS_FAILED(socket_child_->SendWithAddress(&addr,
                                                buf->data(),
                                                buf->len()))) {
     err_ = true;
   }
 }
 
-void NrSocketIpc::close_i() {
+void NrUdpSocketIpc::close_i() {
   ASSERT_ON_THREAD(io_thread_);
 
   if (socket_child_) {
     socket_child_->Close();
     socket_child_ = nullptr;
   }
 }
 
 #if defined(MOZILLA_INTERNAL_API) && !defined(MOZILLA_XPCOMRT_API)
 // close(), but transfer the socket_child_ reference to die as well
 // static
-void NrSocketIpc::release_child_i(nsIUDPSocketChild* aChild,
-                                  nsCOMPtr<nsIEventTarget> sts_thread) {
+void NrUdpSocketIpc::release_child_i(nsIUDPSocketChild* aChild,
+                                     nsCOMPtr<nsIEventTarget> sts_thread) {
   nsRefPtr<nsIUDPSocketChild> socket_child_ref =
     already_AddRefed<nsIUDPSocketChild>(aChild);
   if (socket_child_ref) {
     socket_child_ref->Close();
   }
   // Tell SingletonThreadHolder we're done with it
   RUN_ON_THREAD(sts_thread,
-                mozilla::WrapRunnableNM(&NrSocketIpc::release_use_s),
+                mozilla::WrapRunnableNM(&NrUdpSocketIpc::release_use_s),
                 NS_DISPATCH_NORMAL);
 }
 
-void NrSocketIpc::release_use_s() {
+void NrUdpSocketIpc::release_use_s() {
   sThread->ReleaseUse();
 }
 #endif
 
-void NrSocketIpc::recv_callback_s(RefPtr<nr_udp_message> msg) {
+void NrUdpSocketIpc::recv_callback_s(RefPtr<nr_udp_message> msg) {
   ASSERT_ON_THREAD(sts_thread_);
 
   {
     ReentrantMonitorAutoEnter mon(monitor_);
     if (state_ != NR_CONNECTED) {
       return;
     }
   }
@@ -1429,16 +1476,458 @@ void NrSocketIpc::recv_callback_s(RefPtr
   //enqueue received message
   received_msgs_.push(msg);
 
   if ((poll_flags() & PR_POLL_READ)) {
     fire_callback(NR_ASYNC_WAIT_READ);
   }
 }
 
+#if defined(MOZILLA_INTERNAL_API) && !defined(MOZILLA_XPCOMRT_API)
+// TCPSocket.
+class NrTcpSocketIpc::TcpSocketReadyRunner: public nsRunnable
+{
+public:
+  explicit TcpSocketReadyRunner(NrTcpSocketIpc *sck)
+    : socket_(sck) {}
+
+  NS_IMETHODIMP Run() {
+    socket_->maybe_post_socket_ready();
+    return NS_OK;
+  }
+
+private:
+  nsRefPtr<NrTcpSocketIpc> socket_;
+};
+
+
+NS_IMPL_ISUPPORTS(NrTcpSocketIpc,
+                  nsITCPSocketCallback)
+
+NrTcpSocketIpc::NrTcpSocketIpc(nsIThread* aThread)
+  : NrSocketIpc(static_cast<nsIEventTarget*>(aThread)),
+    mirror_state_(NR_INIT),
+    state_(NR_INIT),
+    buffered_bytes_(0),
+    tracking_number_(0) {
+}
+
+NrTcpSocketIpc::~NrTcpSocketIpc()
+{
+  // also guarantees socket_child_ is released from the io_thread
+
+  // close(), but transfer the socket_child_ reference to die as well
+  RUN_ON_THREAD(io_thread_,
+                mozilla::WrapRunnableNM(&NrTcpSocketIpc::release_child_i,
+                                        socket_child_.forget().take(),
+                                        sts_thread_),
+                NS_DISPATCH_NORMAL);
+}
+
+//
+// nsITCPSocketCallback methods
+//
+NS_IMETHODIMP NrTcpSocketIpc::UpdateReadyState(uint32_t aReadyState) {
+  NrSocketIpcState temp = NR_INIT;
+  switch (static_cast<dom::TCPReadyState>(aReadyState)) {
+    case dom::TCPReadyState::Connecting:
+      temp = NR_CONNECTING;
+      break;
+    case dom::TCPReadyState::Open:
+      temp = NR_CONNECTED;
+      break;
+    case dom::TCPReadyState::Closing:
+      temp = NR_CLOSING;
+      break;
+    case dom::TCPReadyState::Closed:
+      temp = NR_CLOSED;
+      break;
+    default:
+      MOZ_ASSERT(false, "Invalid ReadyState");
+      return NS_OK;
+  }
+  if (mirror_state_ != temp) {
+    mirror_state_ = temp;
+    RUN_ON_THREAD(sts_thread_,
+                  mozilla::WrapRunnable(nsRefPtr<NrTcpSocketIpc>(this),
+                                        &NrTcpSocketIpc::update_state_s,
+                                        temp),
+                  NS_DISPATCH_NORMAL);
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP NrTcpSocketIpc::UpdateBufferedAmount(uint32_t buffered_amount,
+                                                   uint32_t tracking_number) {
+  RUN_ON_THREAD(sts_thread_,
+                mozilla::WrapRunnable(nsRefPtr<NrTcpSocketIpc>(this),
+                                      &NrTcpSocketIpc::message_sent_s,
+                                      buffered_amount,
+                                      tracking_number),
+                NS_DISPATCH_NORMAL);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP NrTcpSocketIpc::FireDataArrayEvent(const nsAString& aType,
+                                                 const InfallibleTArray<uint8_t>& buffer) {
+  // Called when we received data.
+  uint8_t *buf = const_cast<uint8_t*>(buffer.Elements());
+
+  nsAutoPtr<DataBuffer> data_buf(new DataBuffer(buf, buffer.Length()));
+  nsRefPtr<nr_tcp_message> msg = new nr_tcp_message(data_buf);
+
+  RUN_ON_THREAD(sts_thread_,
+                mozilla::WrapRunnable(nsRefPtr<NrTcpSocketIpc>(this),
+                                      &NrTcpSocketIpc::recv_message_s,
+                                      msg),
+                NS_DISPATCH_NORMAL);
+  return NS_OK;
+}
+
+NS_IMETHODIMP NrTcpSocketIpc::FireErrorEvent(const nsAString &type,
+                                             const nsAString &name) {
+  r_log(LOG_GENERIC, LOG_ERR,
+        "Error from TCPSocketChild: type: %s, name: %s",
+        NS_LossyConvertUTF16toASCII(type).get(), NS_LossyConvertUTF16toASCII(name).get());
+  socket_child_ = nullptr;
+
+  mirror_state_ = NR_CLOSED;
+  RUN_ON_THREAD(sts_thread_,
+                mozilla::WrapRunnable(nsRefPtr<NrTcpSocketIpc>(this),
+                                      &NrTcpSocketIpc::update_state_s,
+                                      NR_CLOSED),
+                NS_DISPATCH_NORMAL);
+
+  return NS_OK;
+}
+
+// methods of nsITCPSocketCallback that we are not going to implement.
+
+NS_IMETHODIMP NrTcpSocketIpc::FireDataEvent(JSContext* aCx,
+                                            const nsAString &type,
+                                            const JS::HandleValue data) {
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP NrTcpSocketIpc::FireDataStringEvent(const nsAString &type,
+                                                  const nsACString &data) {
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP NrTcpSocketIpc::FireEvent(const nsAString &type) {
+  // XXX support type.mData == 'close' at least
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+//
+// NrSocketBase methods.
+//
+int NrTcpSocketIpc::create(nr_transport_addr *addr) {
+  int r, _status;
+  nsresult rv;
+  int32_t port;
+  nsCString host;
+
+  if (state_ != NR_INIT) {
+    ABORT(R_INTERNAL);
+  }
+
+  sts_thread_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+  if (NS_FAILED(rv)) {
+    MOZ_ASSERT(false, "Failed to get STS thread");
+    ABORT(R_INTERNAL);
+  }
+
+  // Sanity check
+  if ((r=nr_transport_addr_get_addrstring_and_port(addr, &host, &port))) {
+    ABORT(r);
+  }
+
+  if ((r=nr_transport_addr_copy(&my_addr_, addr))) {
+    ABORT(r);
+  }
+
+  _status = 0;
+abort:
+  return(_status);
+}
+
+int NrTcpSocketIpc::sendto(const void *msg, size_t len,
+                           int flags, nr_transport_addr *to) {
+  MOZ_ASSERT(false);
+  return R_INTERNAL;
+}
+
+int NrTcpSocketIpc::recvfrom(void * buf, size_t maxlen,
+                             size_t *len, int flags,
+                             nr_transport_addr *from) {
+  MOZ_ASSERT(false);
+  return R_INTERNAL;
+}
+
+int NrTcpSocketIpc::getaddr(nr_transport_addr *addrp) {
+  ASSERT_ON_THREAD(sts_thread_);
+  return nr_transport_addr_copy(addrp, &my_addr_);
+}
+
+void NrTcpSocketIpc::close() {
+  ASSERT_ON_THREAD(sts_thread_);
+
+  if (state_ == NR_CLOSED || state_ == NR_CLOSING) {
+    return;
+  }
+
+  state_ = NR_CLOSING;
+
+  RUN_ON_THREAD(io_thread_,
+                mozilla::WrapRunnable(nsRefPtr<NrTcpSocketIpc>(this),
+                                      &NrTcpSocketIpc::close_i),
+                NS_DISPATCH_NORMAL);
+
+  //remove all enqueued messages
+  std::queue<RefPtr<nr_tcp_message>> empty;
+  std::swap(msg_queue_, empty);
+}
+
+int NrTcpSocketIpc::connect(nr_transport_addr *addr) {
+  nsCString remote_addr, local_addr;
+  int32_t remote_port, local_port;
+  int r, _status;
+  if ((r=nr_transport_addr_get_addrstring_and_port(addr,
+                                                   &remote_addr,
+                                                   &remote_port))) {
+    ABORT(r);
+  }
+
+  if ((r=nr_transport_addr_get_addrstring_and_port(&my_addr_,
+                                                   &local_addr,
+                                                   &local_port))) {
+    MOZ_ASSERT(false); // shouldn't fail as it was sanity-checked in ::create()
+    ABORT(r);
+  }
+
+  state_ = mirror_state_ = NR_CONNECTING;
+  RUN_ON_THREAD(io_thread_,
+                mozilla::WrapRunnable(nsRefPtr<NrTcpSocketIpc>(this),
+                             &NrTcpSocketIpc::connect_i,
+                             remote_addr,
+                             static_cast<uint16_t>(remote_port),
+                             local_addr,
+                             static_cast<uint16_t>(local_port)),
+                NS_DISPATCH_NORMAL);
+
+  // Make caller wait for ready to write.
+  _status = R_WOULDBLOCK;
+ abort:
+  return _status;
+}
+
+int NrTcpSocketIpc::write(const void *msg, size_t len, size_t *written) {
+  ASSERT_ON_THREAD(sts_thread_);
+  int _status = 0;
+  if (state_ != NR_CONNECTED) {
+    ABORT(R_FAILED);
+  }
+
+  if (buffered_bytes_ + len >= nsITCPSocketCallback::BUFFER_SIZE) {
+    ABORT(R_WOULDBLOCK);
+  }
+
+  buffered_bytes_ += len;
+  {
+    InfallibleTArray<uint8_t>* arr = new InfallibleTArray<uint8_t>();
+    arr->AppendElements(static_cast<const uint8_t*>(msg), len);
+    // keep track of un-acknowleged writes by tracking number.
+    writes_in_flight_.push_back(len);
+    RUN_ON_THREAD(io_thread_,
+                  mozilla::WrapRunnable(nsRefPtr<NrTcpSocketIpc>(this),
+                                        &NrTcpSocketIpc::write_i,
+                                        nsAutoPtr<InfallibleTArray<uint8_t>>(arr),
+                                        ++tracking_number_),
+                  NS_DISPATCH_NORMAL);
+  }
+  *written = len;
+ abort:
+  return _status;
+}
+
+int NrTcpSocketIpc::read(void* buf, size_t maxlen, size_t *len) {
+  int _status = 0;
+  if (state_ != NR_CONNECTED) {
+    ABORT(R_FAILED);
+  }
+
+  if (msg_queue_.size() == 0) {
+    ABORT(R_WOULDBLOCK);
+  }
+
+  {
+    nsRefPtr<nr_tcp_message> msg(msg_queue_.front());
+    size_t consumed_len = std::min(maxlen, msg->unread_bytes());
+    memcpy(buf, msg->reading_pointer(), consumed_len);
+    if (consumed_len < msg->unread_bytes()) {
+      // There is still something left in buffer.
+      msg->read_bytes += consumed_len;
+    } else {
+      msg_queue_.pop();
+    }
+    *len = consumed_len;
+  }
+
+ abort:
+  return _status;
+}
+
+int NrTcpSocketIpc::listen(int backlog) {
+  MOZ_ASSERT(false);
+  return R_INTERNAL;
+}
+
+int NrTcpSocketIpc::accept(nr_transport_addr *addrp, nr_socket **sockp) {
+  MOZ_ASSERT(false);
+  return R_INTERNAL;
+}
+
+void NrTcpSocketIpc::connect_i(const nsACString &remote_addr,
+                               uint16_t remote_port,
+                               const nsACString &local_addr,
+                               uint16_t local_port) {
+  ASSERT_ON_THREAD(io_thread_);
+  mirror_state_ = NR_CONNECTING;
+
+  dom::TCPSocketChild* child = new dom::TCPSocketChild(NS_ConvertUTF8toUTF16(remote_addr), remote_port);
+  socket_child_ = child;
+
+  // XXX remove remote!
+  socket_child_->SendWindowlessOpenBind(this,
+                                        remote_addr, remote_port,
+                                        local_addr, local_port,
+                                        /* use ssl */ false);
+}
+
+void NrTcpSocketIpc::write_i(nsAutoPtr<InfallibleTArray<uint8_t>> arr,
+                             uint32_t tracking_number) {
+  ASSERT_ON_THREAD(io_thread_);
+  if (!socket_child_) {
+    return;
+  }
+  socket_child_->SendSendArray(*arr, tracking_number);
+}
+
+void NrTcpSocketIpc::close_i() {
+  ASSERT_ON_THREAD(io_thread_);
+  mirror_state_ = NR_CLOSING;
+  if (!socket_child_) {
+    return;
+  }
+  socket_child_->SendClose();
+}
+
+// close(), but transfer the socket_child_ reference to die as well
+// static
+void NrTcpSocketIpc::release_child_i(dom::TCPSocketChild* aChild,
+                                     nsCOMPtr<nsIEventTarget> sts_thread) {
+  nsRefPtr<dom::TCPSocketChild> socket_child_ref =
+    already_AddRefed<dom::TCPSocketChild>(aChild);
+  if (socket_child_ref) {
+    socket_child_ref->SendClose();
+  }
+  // io_thread_ is MainThread, so no use to release
+}
+
+void NrTcpSocketIpc::message_sent_s(uint32_t buffered_amount,
+                                    uint32_t tracking_number) {
+  ASSERT_ON_THREAD(sts_thread_);
+
+  size_t num_unacked_writes = tracking_number_ - tracking_number;
+  while (writes_in_flight_.size() > num_unacked_writes) {
+    writes_in_flight_.pop_front();
+  }
+
+  for (size_t unacked_write_len : writes_in_flight_) {
+    buffered_amount += unacked_write_len;
+  }
+
+  r_log(LOG_GENERIC, LOG_ERR,
+        "UpdateBufferedAmount: (tracking %u): %u, waiting: %s",
+        tracking_number, buffered_amount,
+        (poll_flags() & PR_POLL_WRITE) ? "yes" : "no");
+
+  buffered_bytes_ = buffered_amount;
+  maybe_post_socket_ready();
+}
+
+void NrTcpSocketIpc::recv_message_s(nr_tcp_message *msg) {
+  ASSERT_ON_THREAD(sts_thread_);
+  msg_queue_.push(msg);
+  maybe_post_socket_ready();
+}
+
+void NrTcpSocketIpc::update_state_s(NrSocketIpcState next_state) {
+  ASSERT_ON_THREAD(sts_thread_);
+  // only allow valid transitions
+  switch (state_) {
+    case NR_CONNECTING:
+      if (next_state == NR_CONNECTED) {
+        state_ = NR_CONNECTED;
+        maybe_post_socket_ready();
+      } else {
+        state_ = next_state; // all states are valid from CONNECTING
+      }
+      break;
+    case NR_CONNECTED:
+      if (next_state != NR_CONNECTING) {
+        state_ = next_state;
+      }
+      break;
+    case NR_CLOSING:
+      if (next_state == NR_CLOSED) {
+        state_ = next_state;
+      }
+      break;
+    case NR_CLOSED:
+      break;
+    default:
+      MOZ_CRASH("update_state_s while in illegal state");
+  }
+}
+
+void NrTcpSocketIpc::maybe_post_socket_ready() {
+  bool has_event = false;
+  if (state_ == NR_CONNECTED) {
+    if (poll_flags() & PR_POLL_WRITE) {
+      // This effectively polls via the event loop until the
+      // NR_ASYNC_WAIT_WRITE is no longer armed.
+      if (buffered_bytes_ < nsITCPSocketCallback::BUFFER_SIZE) {
+        r_log(LOG_GENERIC, LOG_INFO, "Firing write callback (%u)",
+              (uint32_t)buffered_bytes_);
+        fire_callback(NR_ASYNC_WAIT_WRITE);
+        has_event = true;
+      }
+    }
+    if (poll_flags() & PR_POLL_READ) {
+      if (msg_queue_.size()) {
+        r_log(LOG_GENERIC, LOG_INFO, "Firing read callback (%u)",
+              (uint32_t)msg_queue_.size());
+        fire_callback(NR_ASYNC_WAIT_READ);
+        has_event = true;
+      }
+    }
+  }
+
+  // If any event has been posted, we post a runnable to see
+  // if the events have to be posted again.
+  if (has_event) {
+    nsRefPtr<TcpSocketReadyRunner> runnable = new TcpSocketReadyRunner(this);
+    NS_DispatchToCurrentThread(runnable);
+  }
+}
+#endif
+
 }  // close namespace
 
 
 using namespace mozilla;
 
 // Bridge to the nr_socket interface
 static int nr_socket_local_destroy(void **objp);
 static int nr_socket_local_sendto(void *obj,const void *msg, size_t len,
@@ -1469,26 +1958,40 @@ static nr_socket_vtbl nr_socket_local_vt
   nr_socket_local_read,
   nr_socket_local_close,
   nr_socket_local_listen,
   nr_socket_local_accept
 };
 
 int nr_socket_local_create(void *obj, nr_transport_addr *addr, nr_socket **sockp) {
   RefPtr<NrSocketBase> sock;
+  int r, _status;
 
   // create IPC bridge for content process
   if (XRE_IsParentProcess()) {
     sock = new NrSocket();
   } else {
-    sock = new NrSocketIpc();
+    switch (addr->protocol) {
+      case IPPROTO_UDP:
+        sock = new NrUdpSocketIpc();
+        break;
+      case IPPROTO_TCP:
+#if defined(MOZILLA_INTERNAL_API) && !defined(MOZILLA_XPCOMRT_API)
+        {
+          nsCOMPtr<nsIThread> main_thread;
+          NS_GetMainThread(getter_AddRefs(main_thread));
+          sock = new NrTcpSocketIpc(main_thread.get());
+        }
+#else
+        ABORT(R_REJECTED);
+#endif
+        break;
+    }
   }
 
-  int r, _status;
-
   r = sock->create(addr);
   if (r)
     ABORT(r);
 
   r = nr_socket_create_int(static_cast<void *>(sock),
                            sock->vtbl(), sockp);
   if (r)
     ABORT(r);
--- a/media/mtransport/nr_socket_prsock.h
+++ b/media/mtransport/nr_socket_prsock.h
@@ -56,27 +56,36 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 #include "nsASocketHandler.h"
 #include "nsISocketTransportService.h"
 #include "nsXPCOM.h"
 #include "nsIEventTarget.h"
 #include "nsIUDPSocketChild.h"
 #include "nsProxyRelease.h"
 #include "nsThreadUtils.h"
 
+#include "nsITCPSocketCallback.h"
 #include "databuffer.h"
 #include "m_cpp_utils.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/ClearOnShutdown.h"
 
 // Stub declaration for nICEr type
 typedef struct nr_socket_vtbl_ nr_socket_vtbl;
 typedef struct nr_socket_ nr_socket;
 
+#if defined(MOZILLA_INTERNAL_API) && !defined(MOZILLA_XPCOMRT_API)
+namespace mozilla {
+namespace dom {
+class TCPSocketChild;
+}
+}
+#endif
+
 namespace mozilla {
 
 namespace net {
   union NetAddr;
 }
 
 class NrSocketBase {
 public:
@@ -204,29 +213,44 @@ public:
   enum NrSocketIpcState {
     NR_INIT,
     NR_CONNECTING,
     NR_CONNECTED,
     NR_CLOSING,
     NR_CLOSED,
   };
 
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(NrSocketIpc, override)
+  NrSocketIpc(nsIEventTarget* aThread);
+
+protected:
+  nsCOMPtr<nsIEventTarget> sts_thread_;
+  // Note: for UDP PBackground, this is a thread held by SingletonThreadHolder.
+  // For TCP PNecko, this is MainThread (and TCPSocket requires MainThread currently)
+  const nsCOMPtr<nsIEventTarget> io_thread_;
+  virtual ~NrSocketIpc() {};
+
+private:
+  DISALLOW_COPY_ASSIGN(NrSocketIpc);
+};
+
+class NrUdpSocketIpc : public NrSocketIpc {
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(NrUdpSocketIpc, override)
 
   NS_IMETHODIMP CallListenerError(const nsACString &message,
                                   const nsACString &filename,
                                   uint32_t line_number);
   NS_IMETHODIMP CallListenerReceivedData(const nsACString &host,
                                          uint16_t port,
                                          const uint8_t *data,
                                          uint32_t data_length);
   NS_IMETHODIMP CallListenerOpened();
   NS_IMETHODIMP CallListenerClosed();
 
-  NrSocketIpc();
+  NrUdpSocketIpc();
 
   // Implementations of the NrSocketBase APIs
   virtual int create(nr_transport_addr *addr) override;
   virtual int sendto(const void *msg, size_t len,
                      int flags, nr_transport_addr *to) override;
   virtual int recvfrom(void * buf, size_t maxlen,
                        size_t *len, int flags,
                        nr_transport_addr *from) override;
@@ -234,58 +258,142 @@ public:
   virtual void close() override;
   virtual int connect(nr_transport_addr *addr) override;
   virtual int write(const void *msg, size_t len, size_t *written) override;
   virtual int read(void* buf, size_t maxlen, size_t *len) override;
   virtual int listen(int backlog) override;
   virtual int accept(nr_transport_addr *addrp, nr_socket **sockp) override;
 
 private:
-  virtual ~NrSocketIpc();
+  virtual ~NrUdpSocketIpc();
 
-  DISALLOW_COPY_ASSIGN(NrSocketIpc);
-
-  static nsIThread* GetIOThreadAndAddUse_s();
+  DISALLOW_COPY_ASSIGN(NrUdpSocketIpc);
 
   // Main or private thread executors of the NrSocketBase APIs
   void create_i(const nsACString &host, const uint16_t port);
   void sendto_i(const net::NetAddr &addr, nsAutoPtr<DataBuffer> buf);
   void close_i();
 #if defined(MOZILLA_INTERNAL_API) && !defined(MOZILLA_XPCOMRT_API)
   static void release_child_i(nsIUDPSocketChild* aChild, nsCOMPtr<nsIEventTarget> ststhread);
   static void release_use_s();
 #endif
   // STS thread executor
   void recv_callback_s(RefPtr<nr_udp_message> msg);
 
+  ReentrantMonitor monitor_; // protects err_and state_
   bool err_;
   NrSocketIpcState state_;
-  std::queue<RefPtr<nr_udp_message> > received_msgs_;
+
+  std::queue<RefPtr<nr_udp_message>> received_msgs_;
 
   nsRefPtr<nsIUDPSocketChild> socket_child_; // only accessed from the io_thread
-  nsCOMPtr<nsIEventTarget> sts_thread_;
-  const nsCOMPtr<nsIEventTarget> io_thread_;
-  ReentrantMonitor monitor_;
 };
 
 // The socket child holds onto one of these, which just passes callbacks
 // through and makes sure the ref to the NrSocketIpc is released on STS.
-class NrSocketIpcProxy : public nsIUDPSocketInternal {
+class NrUdpSocketIpcProxy : public nsIUDPSocketInternal {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIUDPSOCKETINTERNAL
 
-  nsresult Init(const nsRefPtr<NrSocketIpc>& socket);
+  nsresult Init(const nsRefPtr<NrUdpSocketIpc>& socket);
+
+private:
+  virtual ~NrUdpSocketIpcProxy();
+
+  nsRefPtr<NrUdpSocketIpc> socket_;
+  nsCOMPtr<nsIEventTarget> sts_thread_;
+};
+
+struct nr_tcp_message {
+  explicit nr_tcp_message(nsAutoPtr<DataBuffer> &data)
+    : read_bytes(0)
+    , data(data) {
+  }
+
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nr_tcp_message);
+
+  const uint8_t *reading_pointer() const {
+    return data->data() + read_bytes;
+  }
+
+  size_t unread_bytes() const {
+    return data->len() - read_bytes;
+  }
+
+  size_t read_bytes;
 
 private:
-  virtual ~NrSocketIpcProxy();
+  ~nr_tcp_message() {}
+  DISALLOW_COPY_ASSIGN(nr_tcp_message);
+
+  nsAutoPtr<DataBuffer> data;
+};
+
+#if defined(MOZILLA_INTERNAL_API) && !defined(MOZILLA_XPCOMRT_API)
+class NrTcpSocketIpc : public NrSocketIpc,
+                       public nsITCPSocketCallback {
+public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSITCPSOCKETCALLBACK
+
+  explicit NrTcpSocketIpc(nsIThread* aThread);
+
+  // Implementations of the NrSocketBase APIs
+  virtual int create(nr_transport_addr *addr) override;
+  virtual int sendto(const void *msg, size_t len,
+                     int flags, nr_transport_addr *to) override;
+  virtual int recvfrom(void * buf, size_t maxlen,
+                       size_t *len, int flags,
+                       nr_transport_addr *from) override;
+  virtual int getaddr(nr_transport_addr *addrp) override;
+  virtual void close() override;
+  virtual int connect(nr_transport_addr *addr) override;
+  virtual int write(const void *msg, size_t len, size_t *written) override;
+  virtual int read(void* buf, size_t maxlen, size_t *len) override;
+  virtual int listen(int backlog) override;
+  virtual int accept(nr_transport_addr *addrp, nr_socket **sockp) override;
 
-  nsRefPtr<NrSocketIpc> socket_;
-  nsCOMPtr<nsIEventTarget> sts_thread_;
+private:
+  class TcpSocketReadyRunner;
+  DISALLOW_COPY_ASSIGN(NrTcpSocketIpc);
+  virtual ~NrTcpSocketIpc();
+
+  // Main thread executors of the NrSocketBase APIs
+  void connect_i(const nsACString &remote_addr,
+                 uint16_t remote_port,
+                 const nsACString &local_addr,
+                 uint16_t local_port);
+  void write_i(nsAutoPtr<InfallibleTArray<uint8_t>> buf,
+               uint32_t tracking_number);
+  void close_i();
+
+  static void release_child_i(dom::TCPSocketChild* aChild, nsCOMPtr<nsIEventTarget> ststhread);
+
+  // STS thread executor
+  void message_sent_s(uint32_t bufferedAmount, uint32_t tracking_number);
+  void recv_message_s(nr_tcp_message *msg);
+  void update_state_s(NrSocketIpcState next_state);
+  void maybe_post_socket_ready();
+
+  // Accessed from UpdateReadyState (not sts_thread) to avoid sending
+  // runnables when not needed
+  NrSocketIpcState mirror_state_;
+
+  // variables that can only be accessed on STS.
+  NrSocketIpcState state_;
+  std::queue<RefPtr<nr_tcp_message>> msg_queue_;
+  uint32_t buffered_bytes_;
+  uint32_t tracking_number_;
+  std::deque<size_t> writes_in_flight_;
+
+  // main thread.
+  nsRefPtr<dom::TCPSocketChild> socket_child_;
 };
+#endif
 
 int nr_netaddr_to_transport_addr(const net::NetAddr *netaddr,
                                  nr_transport_addr *addr,
                                  int protocol);
 int nr_praddr_to_transport_addr(const PRNetAddr *praddr,
                                 nr_transport_addr *addr,
                                 int protocol, int keep);
 int nr_transport_addr_get_addrstring_and_port(nr_transport_addr *addr,
--- a/media/mtransport/standalone/moz.build
+++ b/media/mtransport/standalone/moz.build
@@ -2,16 +2,17 @@
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 Library('mtransport_standalone')
 
 include('../common.build')
+include("/ipc/chromium/chromium-config.mozbuild")
 
 # These files cannot be built in unified mode because of the redefinition of
 # getLogModule, UNIMPLEMENTED, nr_socket_long_term_violation_time,
 # nr_socket_short_term_violation_time.
 SOURCES += mtransport_cppsrcs
 
 FORCE_STATIC_LIB = True
 
--- a/media/mtransport/testlib/moz.build
+++ b/media/mtransport/testlib/moz.build
@@ -1,15 +1,16 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 include('../common.build')
+include("/ipc/chromium/chromium-config.mozbuild")
 
 # These files cannot be built in unified mode because of the redefinition of
 # getLogModule, UNIMPLEMENTED, nr_socket_long_term_violation_time,
 # nr_socket_short_term_violation_time.
 SOURCES += mtransport_cppsrcs
 
 Library('mtransport_s')
 
--- a/media/mtransport/third_party/nICEr/src/stun/nr_socket_buffered_stun.c
+++ b/media/mtransport/third_party/nICEr/src/stun/nr_socket_buffered_stun.c
@@ -33,16 +33,17 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 
 #include <nr_api.h>
 
 #include <stdio.h>
 #include <string.h>
 #include <sys/types.h>
 #include <sys/queue.h>
 #include <assert.h>
+#include <inttypes.h>
 
 #include "p_buf.h"
 #include "nr_socket.h"
 #include "stun.h"
 #include "nr_socket_buffered_stun.h"
 
 #define NR_MAX_FRAME_SIZE 0xFFFF
 
@@ -369,18 +370,20 @@ static int nr_socket_buffered_stun_close
 
 static void nr_socket_buffered_stun_connected_cb(NR_SOCKET s, int how, void *arg)
 {
   nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)arg;
 
   assert(!sock->connected);
 
   sock->connected = 1;
-  if (sock->pending)
+  if (sock->pending) {
+    r_log(LOG_GENERIC, LOG_INFO, "Invoking writable_cb on connected (%u)", (uint32_t) sock->pending);
     nr_socket_buffered_stun_writable_cb(s, how, arg);
+  }
 }
 
 static int nr_socket_buffered_stun_connect(void *obj, nr_transport_addr *addr)
 {
   nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)obj;
   int r, _status;
 
   if ((r=nr_transport_addr_copy(&sock->remote_addr, addr)))
@@ -393,16 +396,17 @@ static int nr_socket_buffered_stun_conne
       if ((r=nr_socket_getfd(sock->inner, &fd)))
         ABORT(r);
 
       NR_ASYNC_WAIT(fd, NR_ASYNC_WAIT_WRITE, nr_socket_buffered_stun_connected_cb, sock);
       ABORT(R_WOULDBLOCK);
     }
     ABORT(r);
   } else {
+    r_log(LOG_GENERIC, LOG_INFO, "Connected without blocking");
     sock->connected = 1;
   }
 
   _status=0;
 abort:
   return(_status);
 }
 
@@ -427,48 +431,62 @@ static int nr_socket_buffered_stun_write
   int already_armed = 0;
   int r,_status;
   size_t written2 = 0;
   size_t original_len = len;
 
   /* Buffers are close to full, report error. Do this now so we never
      get partial writes */
   if ((sock->pending + len) > sock->max_pending) {
-    r_log(LOG_GENERIC, LOG_INFO, "Write buffer for %s full", sock->remote_addr.as_string);
+    r_log(LOG_GENERIC, LOG_INFO, "Write buffer for %s full (%u + %u > %u) - re-arming @%p",
+          sock->remote_addr.as_string, (uint32_t)sock->pending, (uint32_t)len, (uint32_t)sock->max_pending,
+          &(sock->pending));
     ABORT(R_WOULDBLOCK);
   }
 
 
   if (sock->connected && !sock->pending) {
     r = nr_socket_write(sock->inner, msg, len, &written2, 0);
     if (r) {
-      if (r != R_WOULDBLOCK)
+      if (r != R_WOULDBLOCK) {
+        r_log(LOG_GENERIC, LOG_ERR, "Write error for %s - %d",
+              sock->remote_addr.as_string, r);
         ABORT(r);
+      }
+      r_log(LOG_GENERIC, LOG_INFO, "Write of %" PRIu64 " blocked for %s",
+            (uint64_t) len, sock->remote_addr.as_string);
 
       written2=0;
     }
   } else {
     already_armed = 1;
   }
 
   /* Buffer what's left */
   len -= written2;
 
   if (len) {
     if ((r=nr_p_buf_write_to_chain(sock->p_bufs, &sock->pending_writes,
-                                     ((UCHAR *)msg) + written2, len)))
+                                   ((UCHAR *)msg) + written2, len))) {
+      r_log(LOG_GENERIC, LOG_ERR, "Write_to_chain error for %s - %d",
+            sock->remote_addr.as_string, r);
+
       ABORT(r);
+    }
 
     sock->pending += len;
   }
 
   if (sock->pending && !already_armed) {
       if ((r=nr_socket_buffered_stun_arm_writable_cb(sock)))
         ABORT(r);
   }
+  r_log(LOG_GENERIC, LOG_INFO, "Write buffer not empty for %s  %u - %s armed (@%p)",
+        sock->remote_addr.as_string, (uint32_t)sock->pending,
+        already_armed ? "already" : "", &sock->pending);
 
   *written = original_len;
 
   _status=0;
 abort:
   return _status;
 }
 
@@ -481,34 +499,42 @@ static void nr_socket_buffered_stun_writ
   /* Try to flush */
   STAILQ_FOREACH_SAFE(n1, &sock->pending_writes, entry, n2) {
     size_t written = 0;
 
     if ((r=nr_socket_write(sock->inner, n1->data + n1->r_offset,
                            n1->length - n1->r_offset,
                            &written, 0))) {
 
+      r_log(LOG_GENERIC, LOG_ERR, "Write error for %s - %d",
+            sock->remote_addr.as_string, r);
       ABORT(r);
     }
 
     n1->r_offset += written;
     assert(sock->pending >= written);
     sock->pending -= written;
 
     if (n1->r_offset < n1->length) {
       /* We wrote something, but not everything */
+      r_log(LOG_GENERIC, LOG_INFO, "Write in callback didn't write all (remaining %u of %u) for %s",
+            n1->length - n1->r_offset, n1->length,
+            sock->remote_addr.as_string);
       ABORT(R_WOULDBLOCK);
     }
 
     /* We are done with this p_buf */
     STAILQ_REMOVE_HEAD(&sock->pending_writes, entry);
     nr_p_buf_free(sock->p_bufs, n1);
   }
 
   assert(!sock->pending);
   _status=0;
 abort:
+  r_log(LOG_GENERIC, LOG_INFO, "Writable_cb %s (%u (%p) pending)",
+        sock->remote_addr.as_string, (uint32_t)sock->pending, &(sock->pending));
   if (_status && _status != R_WOULDBLOCK) {
+    r_log(LOG_GENERIC, LOG_ERR, "Failure in writable_cb: %d", _status);
     nr_socket_buffered_stun_failed(sock);
   } else if (sock->pending) {
     nr_socket_buffered_stun_arm_writable_cb(sock);
   }
 }
--- a/media/omx-plugin/lib/gb/libstagefright/moz.build
+++ b/media/omx-plugin/lib/gb/libstagefright/moz.build
@@ -4,26 +4,27 @@
 # 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/.
 DIST_INSTALL = False
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk':
     SOURCES += [
         'libstagefright.cpp',
     ]
+    # Some codec-related code uses multi-character constants; allow this.
+    # XXX: could instead use the FOURCC macro to define these constants.
+    if CONFIG['GNU_CC'] or CONFIG['CLANG_CL']:
+        SOURCES['libstagefright.cpp'].flags += ['-Wno-error=multichar']
 
 SharedLibrary('stagefright')
 
 LOCAL_INCLUDES += [
     '/media/omx-plugin/include/gb',
     '/media/omx-plugin/include/gb/media/stagefright/openmax',
 ]
 
 USE_LIBS += [
     '/media/omx-plugin/lib/gb/libutils/utils',
 ]
 
 # Don't use STL wrappers; this isn't Gecko code
 DISABLE_STL_WRAPPING = True
 NO_VISIBILITY_FLAGS = True
-
-# XXX: We should fix these warnings.
-ALLOW_COMPILER_WARNINGS = True
--- a/media/omx-plugin/lib/gb235/libstagefright/moz.build
+++ b/media/omx-plugin/lib/gb235/libstagefright/moz.build
@@ -4,26 +4,27 @@
 # 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/.
 DIST_INSTALL = False
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk':
     SOURCES += [
         'libstagefright.cpp',
     ]
+    # Some codec-related code uses multi-character constants; allow this.
+    # XXX: could instead use the FOURCC macro to define these constants.
+    if CONFIG['GNU_CC'] or CONFIG['CLANG_CL']:
+        SOURCES['libstagefright.cpp'].flags += ['-Wno-error=multichar']
 
 SharedLibrary('stagefright')
 
 LOCAL_INCLUDES += [
     '/media/omx-plugin/include/gb',
     '/media/omx-plugin/include/gb/media/stagefright/openmax',
 ]
 
 USE_LIBS += [
     '/media/omx-plugin/lib/gb/libutils/utils',
 ]
 
 # Don't use STL wrappers; this isn't Gecko code
 DISABLE_STL_WRAPPING = True
 NO_VISIBILITY_FLAGS = True
-
-# XXX: We should fix these warnings.
-ALLOW_COMPILER_WARNINGS = True
--- a/media/omx-plugin/lib/hc/libstagefright/moz.build
+++ b/media/omx-plugin/lib/hc/libstagefright/moz.build
@@ -4,26 +4,27 @@
 # 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/.
 DIST_INSTALL = False
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk':
     SOURCES += [
         'libstagefright.cpp',
     ]
+    # Some codec-related code uses multi-character constants; allow this.
+    # XXX: could instead use the FOURCC macro to define these constants.
+    if CONFIG['GNU_CC'] or CONFIG['CLANG_CL']:
+        SOURCES['libstagefright.cpp'].flags += ['-Wno-error=multichar']
 
 SharedLibrary('stagefright')
 
 LOCAL_INCLUDES += [
     '/media/omx-plugin/include/gb',
     '/media/omx-plugin/include/gb/media/stagefright/openmax',
 ]
 
 USE_LIBS += [
     '/media/omx-plugin/lib/gb/libutils/utils',
 ]
 
 # Don't use STL wrappers; this isn't Gecko code
 DISABLE_STL_WRAPPING = True
 NO_VISIBILITY_FLAGS = True
-
-# XXX: We should fix these warnings.
-ALLOW_COMPILER_WARNINGS = True
--- a/media/omx-plugin/lib/ics/libstagefright/moz.build
+++ b/media/omx-plugin/lib/ics/libstagefright/moz.build
@@ -4,26 +4,27 @@
 # 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/.
 DIST_INSTALL = False
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk':
     SOURCES += [
         'libstagefright.cpp',
     ]
+    # Some codec-related code uses multi-character constants; allow this.
+    # XXX: could instead use the FOURCC macro to define these constants.
+    if CONFIG['GNU_CC'] or CONFIG['CLANG_CL']:
+        SOURCES['libstagefright.cpp'].flags += ['-Wno-error=multichar']
 
 SharedLibrary('stagefright')
 
 LOCAL_INCLUDES += [
     '/media/omx-plugin/include/ics',
     '/media/omx-plugin/include/ics/media/stagefright/openmax',
 ]
 
 USE_LIBS += [
     '/media/omx-plugin/lib/ics/libutils/utils',
 ]
 
 # Don't use STL wrappers; this isn't Gecko code
 DISABLE_STL_WRAPPING = True
 NO_VISIBILITY_FLAGS = True
-
-# XXX: We should fix these warnings.
-ALLOW_COMPILER_WARNINGS = True
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -617,22 +617,16 @@ PeerConnectionConfiguration::AddIceServe
     }
     if (port == -1)
       port = (isStuns || isTurns)? 5349 : 3478;
 
     if (isTurn || isTurns) {
       NS_ConvertUTF16toUTF8 credential(aServer.mCredential);
       NS_ConvertUTF16toUTF8 username(aServer.mUsername);
 
-      // Bug 1039655 - TURN TCP is not e10s ready
-      if ((transport == kNrIceTransportTcp) &&
-          (!XRE_IsParentProcess())) {
-        continue;
-      }
-
       if (!addTurnServer(host.get(), port,
                          username.get(),
                          credential.get(),
                          (transport.IsEmpty() ?
                           kNrIceTransportUdp : transport.get()))) {
         return NS_ERROR_FAILURE;
       }
     } else {
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -731,18 +731,17 @@ pref("ui.scrollToClick", 0);
 
 // provide ability to turn on support for canvas focus rings
 pref("canvas.focusring.enabled", true);
 pref("canvas.customfocusring.enabled", false);
 pref("canvas.hitregions.enabled", false);
 pref("canvas.filters.enabled", false);
 // Add support for canvas path objects
 pref("canvas.path.enabled"