Merge inbound to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Fri, 19 Sep 2014 13:33:32 -0400
changeset 206235 b00bdb144e063af6e4d39fbdbbcd928f4152a129
parent 206191 74d4fc84e39ea461b8054355d277c817b51a72bb (current diff)
parent 206234 2d94eebed4edeffedd549c98a8f38ec5bf6b6bef (diff)
child 206245 a084c4cfd8a1ce35927526d38985ef805427c144
push id27516
push userryanvm@gmail.com
push dateFri, 19 Sep 2014 17:54:48 +0000
treeherdermozilla-central@b00bdb144e06 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone35.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to m-c. a=merge
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -1464,21 +1464,26 @@ notification[value="translation"] menuli
   list-style-image: url("chrome://browser/skin/places/star-icons.png");
   -moz-image-region: rect(0px 32px 16px 16px);
   width: 16px;
   height: 16px;
 }
 
 .ac-result-type-keyword,
 .autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage) {
-  list-style-image: url(moz-icon://stock/gtk-find?size=menu);
+  list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon);
   width: 16px;
   height: 16px;
 }
 
+.ac-result-type-keyword[selected="true"],
+.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage, selected) {
+  list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon-inverted);
+}
+
 .ac-result-type-tag,
 .autocomplete-treebody::-moz-tree-image(tag, treecolAutoCompleteImage) {
   list-style-image: url("chrome://browser/skin/places/tag.png");
   width: 16px;
   height: 16px;
 }
 
 .ac-comment {
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -2170,20 +2170,24 @@ toolbarbutton[sdk-button="true"][cui-are
 .ac-result-type-bookmark,
 .autocomplete-treebody::-moz-tree-image(bookmark, treecolAutoCompleteImage) {
   list-style-image: url("chrome://browser/skin/places/star-icons.png");
   -moz-image-region: rect(0, 48px, 16px, 32px);
 }
 
 .ac-result-type-keyword,
 .autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage) {
-  list-style-image: url(chrome://global/skin/icons/search-textbox.png);
-  margin: 2px;
-  width: 12px;
-  height: 12px;
+  list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon);
+  width: 16px;
+  height: 16px;
+}
+
+.ac-result-type-keyword[selected="true"],
+.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage, selected) {
+  list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon-inverted);
 }
 
 richlistitem[selected="true"][current="true"] > .ac-title-box > .ac-result-type-bookmark,
 .autocomplete-treebody::-moz-tree-image(selected, current, bookmark, treecolAutoCompleteImage) {
   list-style-image: url("chrome://browser/skin/places/star-icons.png");
   -moz-image-region: rect(0, 64px, 16px, 48px);
 }
 
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -1390,22 +1390,32 @@ html|*.urlbar-input:-moz-lwtheme::-moz-p
 
 richlistitem[selected="true"][current="true"] > .ac-title-box > .ac-result-type-bookmark,
 .autocomplete-treebody::-moz-tree-image(selected, current, bookmark, treecolAutoCompleteImage) {
   -moz-image-region: rect(0px 48px 16px 32px);
 }
 
 .ac-result-type-keyword,
 .autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage) {
-  list-style-image: url(chrome://global/skin/icons/Search-glass.png);
-  -moz-image-region: rect(0px 32px 16px 16px);
+  list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon);
   width: 16px;
   height: 16px;
 }
 
+%ifdef WINDOWS_AERO
+@media not all and (-moz-windows-default-theme) {
+%endif
+  .ac-result-type-keyword[selected="true"],
+  .autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage, selected) {
+    list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon-inverted);
+  }
+%ifdef WINDOWS_AERO
+}
+%endif
+
 .ac-result-type-tag,
 .autocomplete-treebody::-moz-tree-image(tag, treecolAutoCompleteImage) {
   list-style-image: url("chrome://browser/skin/places/tag.png");
   width: 16px;
   height: 16px;
 }
 
 .ac-comment {
--- a/content/base/src/DirectionalityUtils.cpp
+++ b/content/base/src/DirectionalityUtils.cpp
@@ -315,22 +315,25 @@ GetDirectionFromText(const char16_t* aTe
 
     if (NS_IS_HIGH_SURROGATE(ch) &&
         start < end &&
         NS_IS_LOW_SURROGATE(*start)) {
       ch = SURROGATE_TO_UCS4(ch, *start++);
       current++;
     }
 
-    Directionality dir = GetDirectionFromChar(ch);
-    if (dir != eDir_NotSet) {
-      if (aFirstStrong) {
-        *aFirstStrong = current;
+    // Just ignore lone surrogates
+    if (!IS_SURROGATE(ch)) {
+      Directionality dir = GetDirectionFromChar(ch);
+      if (dir != eDir_NotSet) {
+        if (aFirstStrong) {
+          *aFirstStrong = current;
+        }
+        return dir;
       }
-      return dir;
     }
   }
 
   if (aFirstStrong) {
     *aFirstStrong = UINT32_MAX;
   }
   return eDir_NotSet;
 }
--- a/content/media/MediaRecorder.cpp
+++ b/content/media/MediaRecorder.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MediaRecorder.h"
 #include "AudioNodeEngine.h"
 #include "AudioNodeStream.h"
 #include "DOMMediaStream.h"
 #include "EncodedBufferCache.h"
 #include "MediaEncoder.h"
+#include "mozilla/StaticPtr.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/AudioStreamTrack.h"
 #include "mozilla/dom/BlobEvent.h"
 #include "mozilla/dom/RecordErrorEvent.h"
 #include "mozilla/dom/VideoStreamTrack.h"
 #include "nsError.h"
 #include "nsIDocument.h"
@@ -30,16 +31,83 @@ PRLogModuleInfo* gMediaRecorderLog;
 #else
 #define LOG(type, msg)
 #endif
 
 namespace mozilla {
 
 namespace dom {
 
+/**
++ * MediaRecorderReporter measures memory being used by the Media Recorder.
++ *
++ * It is a singleton reporter and the single class object lives as long as at
++ * least one Recorder is registered. In MediaRecorder, the reporter is unregistered
++ * when it is destroyed.
++ */
+class MediaRecorderReporter MOZ_FINAL : public nsIMemoryReporter
+{
+public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+  MediaRecorderReporter() {};
+  static MediaRecorderReporter* UniqueInstance();
+  void InitMemoryReporter();
+
+  static void AddMediaRecorder(MediaRecorder *aRecorder)
+  {
+    GetRecorders().AppendElement(aRecorder);
+  }
+
+  static void RemoveMediaRecorder(MediaRecorder *aRecorder)
+  {
+    RecordersArray& recorders = GetRecorders();
+    recorders.RemoveElement(aRecorder);
+    if (recorders.IsEmpty()) {
+      sUniqueInstance = nullptr;
+    }
+  }
+
+  NS_METHOD
+  CollectReports(nsIHandleReportCallback* aHandleReport,
+                 nsISupports* aData, bool aAnonymize)
+  {
+    int64_t amount = 0;
+    RecordersArray& recorders = GetRecorders();
+    for (size_t i = 0; i < recorders.Length(); ++i) {
+      amount += recorders[i]->SizeOfExcludingThis(MallocSizeOf);
+    }
+
+  #define MEMREPORT(_path, _amount, _desc)                                    \
+    do {                                                                      \
+      nsresult rv;                                                            \
+      rv = aHandleReport->Callback(EmptyCString(), NS_LITERAL_CSTRING(_path), \
+                                   KIND_HEAP, UNITS_BYTES, _amount,           \
+                                   NS_LITERAL_CSTRING(_desc), aData);         \
+      NS_ENSURE_SUCCESS(rv, rv);                                              \
+    } while (0)
+
+    MEMREPORT("explicit/media/recorder", amount,
+              "Memory used by media recorder.");
+
+    return NS_OK;
+  }
+
+private:
+  MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
+  virtual ~MediaRecorderReporter();
+  static StaticRefPtr<MediaRecorderReporter> sUniqueInstance;
+  typedef nsTArray<MediaRecorder*> RecordersArray;
+  static RecordersArray& GetRecorders()
+  {
+    return UniqueInstance()->mRecorders;
+  }
+  RecordersArray mRecorders;
+};
+NS_IMPL_ISUPPORTS(MediaRecorderReporter, nsIMemoryReporter);
+
 NS_IMPL_CYCLE_COLLECTION_INHERITED(MediaRecorder, DOMEventTargetHelper,
                                    mDOMStream, mAudioNode)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MediaRecorder)
   NS_INTERFACE_MAP_ENTRY(nsIDocumentActivity)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(MediaRecorder, DOMEventTargetHelper)
@@ -327,16 +395,24 @@ public:
   bool IsEncoderError()
   {
     if (mEncoder && mEncoder->HasError()) {
       return true;
     }
     return false;
   }
 
+  size_t
+  SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+  {
+    size_t amount = mEncoder->SizeOfExcludingThis(aMallocSizeOf);
+    return amount;
+  }
+
+
 private:
   // Only DestroyRunnable is allowed to delete Session object.
   virtual ~Session()
   {
     LOG(PR_LOG_DEBUG, ("Session.~Session (%p)", this));
     CleanupStreams();
   }
   // Pull encoded media data from MediaEncoder and put into EncodedBufferCache.
@@ -676,28 +752,29 @@ MediaRecorder::Start(const Optional<int3
   if (aTimeSlice.WasPassed()) {
     if (aTimeSlice.Value() < 0) {
       aResult.Throw(NS_ERROR_INVALID_ARG);
       return;
     }
 
     timeSlice = aTimeSlice.Value();
   }
-
+  MediaRecorderReporter::AddMediaRecorder(this);
   mState = RecordingState::Recording;
   // Start a session.
   mSessions.AppendElement();
   mSessions.LastElement() = new Session(this, timeSlice);
   mSessions.LastElement()->Start();
 }
 
 void
 MediaRecorder::Stop(ErrorResult& aResult)
 {
   LOG(PR_LOG_DEBUG, ("MediaRecorder.Stop %p", this));
+  MediaRecorderReporter::RemoveMediaRecorder(this);
   if (mState == RecordingState::Inactive) {
     aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
   mState = RecordingState::Inactive;
   MOZ_ASSERT(mSessions.Length() > 0);
   mSessions.LastElement()->Stop();
 }
@@ -983,10 +1060,41 @@ MediaRecorder::GetSourcePrincipal()
   if (mDOMStream != nullptr) {
     return mDOMStream->GetPrincipal();
   }
   MOZ_ASSERT(mAudioNode != nullptr);
   nsIDocument* doc = mAudioNode->GetOwner()->GetExtantDoc();
   return doc ? doc->NodePrincipal() : nullptr;
 }
 
+size_t
+MediaRecorder::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+  size_t amount = 42;
+  for (size_t i = 0; i < mSessions.Length(); ++i) {
+    amount += mSessions[i]->SizeOfExcludingThis(aMallocSizeOf);
+  }
+  return amount;
+}
+
+StaticRefPtr<MediaRecorderReporter> MediaRecorderReporter::sUniqueInstance;
+
+MediaRecorderReporter* MediaRecorderReporter::UniqueInstance()
+{
+  if (!sUniqueInstance) {
+    sUniqueInstance = new MediaRecorderReporter();
+    sUniqueInstance->InitMemoryReporter();
+  }
+  return sUniqueInstance;
+ }
+
+void MediaRecorderReporter::InitMemoryReporter()
+{
+  RegisterWeakMemoryReporter(this);
+}
+
+MediaRecorderReporter::~MediaRecorderReporter()
+{
+  UnregisterWeakMemoryReporter(this);
+}
+
 }
 }
--- a/content/media/MediaRecorder.h
+++ b/content/media/MediaRecorder.h
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MediaRecorder_h
 #define MediaRecorder_h
 
 #include "mozilla/dom/MediaRecorderBinding.h"
 #include "mozilla/DOMEventTargetHelper.h"
+#include "mozilla/MemoryReporting.h"
 #include "nsIDocumentActivity.h"
 
 // Max size for allowing queue encoded data in memory
 #define MAX_ALLOW_MEMORY_BUFFER 1024000
 namespace mozilla {
 
 class AudioNodeStream;
 class DOMMediaStream;
@@ -85,16 +86,21 @@ public:
   // Construct a recorder with a Web Audio destination node as its source.
   static already_AddRefed<MediaRecorder>
   Constructor(const GlobalObject& aGlobal,
               AudioNode& aSrcAudioNode,
               uint32_t aSrcOutput,
               const MediaRecorderOptions& aInitDict,
               ErrorResult& aRv);
 
+  /*
+   * Measure the size of the buffer, and memory occupied by mAudioEncoder
+   * and mVideoEncoder
+   */
+  size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
   // EventHandler
   IMPL_EVENT_HANDLER(dataavailable)
   IMPL_EVENT_HANDLER(error)
   IMPL_EVENT_HANDLER(start)
   IMPL_EVENT_HANDLER(stop)
   IMPL_EVENT_HANDLER(warning)
 
   NS_DECL_NSIDOCUMENTACTIVITY
--- a/content/media/encoder/MediaEncoder.cpp
+++ b/content/media/encoder/MediaEncoder.cpp
@@ -3,16 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 #include "MediaEncoder.h"
 #include "MediaDecoder.h"
 #include "nsIPrincipal.h"
 #include "nsMimeTypes.h"
 #include "prlog.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/StaticPtr.h"
 
 #include "OggWriter.h"
 #ifdef MOZ_OPUS
 #include "OpusTrackEncoder.h"
 
 #endif
 
 #ifdef MOZ_VORBIS
@@ -199,16 +200,19 @@ MediaEncoder::GetEncodedData(nsTArray<ns
       rv = CopyMetadataToMuxer(mVideoEncoder.get());
       if (NS_FAILED(rv)) {
         LOG(PR_LOG_ERROR, ("Error! Fail to Set Video Metadata"));
         break;
       }
 
       rv = mWriter->GetContainerData(aOutputBufs,
                                      ContainerWriter::GET_HEADER);
+      if (aOutputBufs != nullptr) {
+        mSizeOfBuffer = aOutputBufs->SizeOfExcludingThis(MallocSizeOf);
+      }
       if (NS_FAILED(rv)) {
        LOG(PR_LOG_ERROR,("Error! writer fail to generate header!"));
        mState = ENCODE_ERROR;
        break;
       }
       LOG(PR_LOG_DEBUG, ("Finish ENCODE_METADDATA TimeStamp = %f", GetEncodeTimeStamp()));
       mState = ENCODE_TRACK;
       break;
@@ -231,30 +235,34 @@ MediaEncoder::GetEncodedData(nsTArray<ns
       }
       LOG(PR_LOG_DEBUG, ("Video encoded TimeStamp = %f", GetEncodeTimeStamp()));
       // In audio only or video only case, let unavailable track's flag to be true.
       bool isAudioCompleted = (mAudioEncoder && mAudioEncoder->IsEncodingComplete()) || !mAudioEncoder;
       bool isVideoCompleted = (mVideoEncoder && mVideoEncoder->IsEncodingComplete()) || !mVideoEncoder;
       rv = mWriter->GetContainerData(aOutputBufs,
                                      isAudioCompleted && isVideoCompleted ?
                                      ContainerWriter::FLUSH_NEEDED : 0);
+      if (aOutputBufs != nullptr) {
+        mSizeOfBuffer = aOutputBufs->SizeOfExcludingThis(MallocSizeOf);
+      }
       if (NS_SUCCEEDED(rv)) {
         // Successfully get the copy of final container data from writer.
         reloop = false;
       }
       mState = (mWriter->IsWritingComplete()) ? ENCODE_DONE : ENCODE_TRACK;
       LOG(PR_LOG_DEBUG, ("END ENCODE_TRACK TimeStamp = %f "
           "mState = %d aComplete %d vComplete %d",
           GetEncodeTimeStamp(), mState, isAudioCompleted, isVideoCompleted));
       break;
     }
 
     case ENCODE_DONE:
     case ENCODE_ERROR:
       LOG(PR_LOG_DEBUG, ("MediaEncoder has been shutdown."));
+      mSizeOfBuffer = 0;
       mShutdown = true;
       reloop = false;
       break;
     default:
       MOZ_CRASH("Invalid encode state");
     }
   }
 }
@@ -318,9 +326,26 @@ MediaEncoder::IsWebMEncoderEnabled()
 #ifdef MOZ_OMX_ENCODER
 bool
 MediaEncoder::IsOMXEncoderEnabled()
 {
   return Preferences::GetBool("media.encoder.omx.enabled");
 }
 #endif
 
+/*
+ * SizeOfExcludingThis measures memory being used by the Media Encoder.
+ * Currently it measures the size of the Encoder buffer and memory occupied
+ * by mAudioEncoder and mVideoEncoder.
+ */
+size_t
+MediaEncoder::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+  size_t amount = 0;
+  if (mState == ENCODE_TRACK) {
+    amount = mSizeOfBuffer +
+             (mAudioEncoder != nullptr ? mAudioEncoder->SizeOfExcludingThis(aMallocSizeOf) : 0) +
+             (mVideoEncoder != nullptr ? mVideoEncoder->SizeOfExcludingThis(aMallocSizeOf) : 0);
+  }
+  return amount;
 }
+
+}
--- a/content/media/encoder/MediaEncoder.h
+++ b/content/media/encoder/MediaEncoder.h
@@ -5,16 +5,18 @@
 
 #ifndef MediaEncoder_h_
 #define MediaEncoder_h_
 
 #include "mozilla/DebugOnly.h"
 #include "TrackEncoder.h"
 #include "ContainerWriter.h"
 #include "MediaStreamGraph.h"
+#include "nsIMemoryReporter.h"
+#include "mozilla/MemoryReporting.h"
 
 namespace mozilla {
 
 /**
  * MediaEncoder is the framework of encoding module, it controls and manages
  * procedures between ContainerWriter and TrackEncoder. ContainerWriter packs
  * the encoded track data with a specific container (e.g. ogg, mp4).
  * AudioTrackEncoder and VideoTrackEncoder are subclasses of TrackEncoder, and
@@ -61,16 +63,17 @@ public :
                AudioTrackEncoder* aAudioEncoder,
                VideoTrackEncoder* aVideoEncoder,
                const nsAString& aMIMEType)
     : mWriter(aWriter)
     , mAudioEncoder(aAudioEncoder)
     , mVideoEncoder(aVideoEncoder)
     , mStartTime(TimeStamp::Now())
     , mMIMEType(aMIMEType)
+    , mSizeOfBuffer(0)
     , mState(MediaEncoder::ENCODE_METADDATA)
     , mShutdown(false)
   {}
 
   ~MediaEncoder() {};
 
   /**
    * Notified by the control loop of MediaStreamGraph; aQueueMedia is the raw
@@ -135,26 +138,34 @@ public :
 #ifdef MOZ_WEBM_ENCODER
   static bool IsWebMEncoderEnabled();
 #endif
 
 #ifdef MOZ_OMX_ENCODER
   static bool IsOMXEncoderEnabled();
 #endif
 
+  MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
+  /*
+   * Measure the size of the buffer, and memory occupied by mAudioEncoder
+   * and mVideoEncoder
+   */
+  size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
 private:
   // Get encoded data from trackEncoder and write to muxer
   nsresult WriteEncodedDataToMuxer(TrackEncoder *aTrackEncoder);
   // Get metadata from trackEncoder and copy to muxer
   nsresult CopyMetadataToMuxer(TrackEncoder* aTrackEncoder);
   nsAutoPtr<ContainerWriter> mWriter;
   nsAutoPtr<AudioTrackEncoder> mAudioEncoder;
   nsAutoPtr<VideoTrackEncoder> mVideoEncoder;
   TimeStamp mStartTime;
   nsString mMIMEType;
+  int64_t mSizeOfBuffer;
   int mState;
   bool mShutdown;
   // Get duration from create encoder, for logging purpose
   double GetEncodeTimeStamp()
   {
     TimeDuration decodeTime;
     decodeTime = TimeStamp::Now() - mStartTime;
     return decodeTime.ToMilliseconds();
--- a/content/media/encoder/TrackEncoder.cpp
+++ b/content/media/encoder/TrackEncoder.cpp
@@ -168,16 +168,22 @@ AudioTrackEncoder::DeInterleaveTrackData
 {
   for (int32_t i = 0; i < aChannels; ++i) {
     for(int32_t j = 0; j < aDuration; ++j) {
       aOutput[i * aDuration + j] = aInput[i + j * aChannels];
     }
   }
 }
 
+size_t
+AudioTrackEncoder::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+  return mRawSegment.SizeOfExcludingThis(aMallocSizeOf);
+}
+
 void
 VideoTrackEncoder::NotifyQueuedTrackChanges(MediaStreamGraph* aGraph,
                                             TrackID aID,
                                             TrackRate aTrackRate,
                                             TrackTicks aTrackOffset,
                                             uint32_t aTrackEvents,
                                             const MediaSegment& aQueuedMedia)
 {
@@ -258,9 +264,15 @@ VideoTrackEncoder::NotifyEndOfStream()
          DEFAULT_FRAME_WIDTH, DEFAULT_FRAME_HEIGHT, DEFAULT_TRACK_RATE);
   }
 
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
   mEndOfStream = true;
   mReentrantMonitor.NotifyAll();
 }
 
+size_t
+VideoTrackEncoder::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+  return mRawSegment.SizeOfExcludingThis(aMallocSizeOf);
 }
+
+}
--- a/content/media/encoder/TrackEncoder.h
+++ b/content/media/encoder/TrackEncoder.h
@@ -163,16 +163,20 @@ public:
                                   AudioDataValue* aOutput);
 
   /**
    * De-interleaves the aInput data and stores the result into aOutput.
    * No up-mix or down-mix operations inside.
    */
   static void DeInterleaveTrackData(AudioDataValue* aInput, int32_t aDuration,
                                     int32_t aChannels, AudioDataValue* aOutput);
+  /**
+  * Measure size of mRawSegment
+  */
+  size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
 
 protected:
   /**
    * Number of samples per channel in a pcm buffer. This is also the value of
    * frame size required by audio encoder, and mReentrantMonitor will be
    * notified when at least this much data has been added to mRawSegment.
    */
   virtual int GetPacketDuration() { return 0; }
@@ -236,16 +240,20 @@ public:
    * Notified by the same callbcak of MediaEncoder when it has received a track
    * change from MediaStreamGraph. Called on the MediaStreamGraph thread.
    */
   void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
                                 TrackRate aTrackRate,
                                 TrackTicks aTrackOffset,
                                 uint32_t aTrackEvents,
                                 const MediaSegment& aQueuedMedia) MOZ_OVERRIDE;
+  /**
+  * Measure size of mRawSegment
+  */
+  size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
 
 protected:
   /**
    * Initialized the video encoder. In order to collect the value of width and
    * height of source frames, this initialization is delayed until we have
    * received the first valid video frame from MediaStreamGraph;
    * mReentrantMonitor will be notified after it has successfully initialized,
    * and this method is called on the MediaStramGraph thread.
copy from content/media/mediasource/SourceBuffer.cpp
copy to content/media/mediasource/ContainerParser.cpp
--- a/content/media/mediasource/SourceBuffer.cpp
+++ b/content/media/mediasource/ContainerParser.cpp
@@ -1,113 +1,81 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "SourceBuffer.h"
+#include "ContainerParser.h"
 
-#include "AsyncEventRunner.h"
-#include "MediaSourceUtils.h"
-#include "TrackBuffer.h"
 #include "WebMBufferedParser.h"
 #include "mozilla/Endian.h"
-#include "mozilla/ErrorResult.h"
-#include "mozilla/FloatingPoint.h"
-#include "mozilla/Preferences.h"
-#include "mozilla/dom/MediaSourceBinding.h"
-#include "mozilla/dom/TimeRanges.h"
 #include "mp4_demuxer/BufferStream.h"
 #include "mp4_demuxer/MoofParser.h"
-#include "nsError.h"
-#include "nsIEventTarget.h"
-#include "nsIRunnable.h"
-#include "nsThreadUtils.h"
 #include "prlog.h"
 
-struct JSContext;
-class JSObject;
-
 #ifdef PR_LOGGING
 extern PRLogModuleInfo* GetMediaSourceLog();
 extern PRLogModuleInfo* GetMediaSourceAPILog();
 
 #define MSE_DEBUG(...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG, (__VA_ARGS__))
 #define MSE_DEBUGV(...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG+1, (__VA_ARGS__))
 #define MSE_API(...) PR_LOG(GetMediaSourceAPILog(), PR_LOG_DEBUG, (__VA_ARGS__))
 #else
 #define MSE_DEBUG(...)
 #define MSE_DEBUGV(...)
 #define MSE_API(...)
 #endif
 
 namespace mozilla {
 
-class ContainerParser {
-public:
-  virtual ~ContainerParser() {}
-
-  // Return true if aData starts with an initialization segment.
-  // The base implementation exists only for debug logging and is expected
-  // to be called first from the overriding implementation.
-  virtual bool IsInitSegmentPresent(const uint8_t* aData, uint32_t aLength)
-  {
-    MSE_DEBUG("ContainerParser(%p)::IsInitSegmentPresent aLength=%u [%x%x%x%x]",
-              this, aLength,
-              aLength > 0 ? aData[0] : 0,
-              aLength > 1 ? aData[1] : 0,
-              aLength > 2 ? aData[2] : 0,
-              aLength > 3 ? aData[3] : 0);
-    return false;
-  }
+bool
+ContainerParser::IsInitSegmentPresent(const uint8_t* aData, uint32_t aLength)
+{
+MSE_DEBUG("ContainerParser(%p)::IsInitSegmentPresent aLength=%u [%x%x%x%x]",
+            this, aLength,
+            aLength > 0 ? aData[0] : 0,
+            aLength > 1 ? aData[1] : 0,
+            aLength > 2 ? aData[2] : 0,
+            aLength > 3 ? aData[3] : 0);
+return false;
+}
 
-  // Return true if aData starts with a media segment.
-  // The base implementation exists only for debug logging and is expected
-  // to be called first from the overriding implementation.
-  virtual bool IsMediaSegmentPresent(const uint8_t* aData, uint32_t aLength)
-  {
-    MSE_DEBUG("ContainerParser(%p)::IsMediaSegmentPresent aLength=%u [%x%x%x%x]",
-              this, aLength,
-              aLength > 0 ? aData[0] : 0,
-              aLength > 1 ? aData[1] : 0,
-              aLength > 2 ? aData[2] : 0,
-              aLength > 3 ? aData[3] : 0);
-    return false;
-  }
+bool
+ContainerParser::IsMediaSegmentPresent(const uint8_t* aData, uint32_t aLength)
+{
+  MSE_DEBUG("ContainerParser(%p)::IsMediaSegmentPresent aLength=%u [%x%x%x%x]",
+            this, aLength,
+            aLength > 0 ? aData[0] : 0,
+            aLength > 1 ? aData[1] : 0,
+            aLength > 2 ? aData[2] : 0,
+            aLength > 3 ? aData[3] : 0);
+  return false;
+}
 
-  // Parse aData to extract the start and end frame times from the media
-  // segment.  aData may not start on a parser sync boundary.  Return true
-  // if aStart and aEnd have been updated.
-  virtual bool ParseStartAndEndTimestamps(const uint8_t* aData, uint32_t aLength,
-                                          int64_t& aStart, int64_t& aEnd)
-  {
-    return false;
-  }
+bool
+ContainerParser::ParseStartAndEndTimestamps(const uint8_t* aData, uint32_t aLength,
+                                            int64_t& aStart, int64_t& aEnd)
+{
+  return false;
+}
 
-  // Compare aLhs and rHs, considering any error that may exist in the
-  // timestamps from the format's base representation.  Return true if aLhs
-  // == aRhs within the error epsilon.
-  virtual bool TimestampsFuzzyEqual(int64_t aLhs, int64_t aRhs)
-  {
-    NS_WARNING("Using default ContainerParser::TimestampFuzzyEquals implementation");
-    return aLhs == aRhs;
-  }
+bool
+ContainerParser::TimestampsFuzzyEqual(int64_t aLhs, int64_t aRhs)
+{
+  NS_WARNING("Using default ContainerParser::TimestampFuzzyEquals implementation");
+  return aLhs == aRhs;
+}
 
-  virtual const nsTArray<uint8_t>& InitData()
-  {
-    MOZ_ASSERT(mInitData.Length() > 0);
-    return mInitData;
-  }
-
-  static ContainerParser* CreateForMIMEType(const nsACString& aType);
-
-protected:
-  nsTArray<uint8_t> mInitData;
-};
+const nsTArray<uint8_t>&
+ContainerParser::InitData()
+{
+  MOZ_ASSERT(mHasInitData);
+  return mInitData;
+}
 
 class WebMContainerParser : public ContainerParser {
 public:
   WebMContainerParser()
     : mParser(0), mOffset(0)
   {}
 
   static const unsigned NS_PER_USEC = 1000;
@@ -178,16 +146,17 @@ public:
       if (!mapping.IsEmpty()) {
         length = mapping[0].mSyncOffset;
         MOZ_ASSERT(length <= aLength);
       }
       MSE_DEBUG("WebMContainerParser(%p)::ParseStartAndEndTimestamps: Stashed init of %u bytes.",
                 this, length);
 
       mInitData.ReplaceElementsAt(0, mInitData.Length(), aData, length);
+      mHasInitData = true;
     }
     mOffset += aLength;
 
     if (mapping.IsEmpty()) {
       return false;
     }
 
     // Exclude frames that we don't enough data to cover the end of.
@@ -268,16 +237,17 @@ public:
     if (initSegment) {
       const MediaByteRange& range = mParser->mInitRange;
       MSE_DEBUG("MP4ContainerParser(%p)::ParseStartAndEndTimestamps: Stashed init of %u bytes.",
                 this, range.mEnd - range.mStart);
 
       mInitData.ReplaceElementsAt(0, mInitData.Length(),
                                   aData + range.mStart,
                                   range.mEnd - range.mStart);
+      mHasInitData = true;
     }
 
     mp4_demuxer::Interval<mp4_demuxer::Microseconds> compositionRange =
       mParser->GetCompositionRange(byteRanges);
 
     mStream->DiscardBefore(mParser->mOffset);
 
     if (compositionRange.IsNull()) {
@@ -290,451 +260,22 @@ public:
     return true;
   }
 
 private:
   nsRefPtr<mp4_demuxer::BufferStream> mStream;
   nsAutoPtr<mp4_demuxer::MoofParser> mParser;
 };
 
-
 /*static*/ ContainerParser*
 ContainerParser::CreateForMIMEType(const nsACString& aType)
 {
   if (aType.LowerCaseEqualsLiteral("video/webm") || aType.LowerCaseEqualsLiteral("audio/webm")) {
     return new WebMContainerParser();
   }
 
   if (aType.LowerCaseEqualsLiteral("video/mp4") || aType.LowerCaseEqualsLiteral("audio/mp4")) {
     return new MP4ContainerParser();
   }
   return new ContainerParser();
 }
 
-namespace dom {
-
-void
-SourceBuffer::SetMode(SourceBufferAppendMode aMode, ErrorResult& aRv)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MSE_API("SourceBuffer(%p)::SetMode(aMode=%d)", this, aMode);
-  if (!IsAttached() || mUpdating) {
-    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return;
-  }
-  MOZ_ASSERT(mMediaSource->ReadyState() != MediaSourceReadyState::Closed);
-  if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) {
-    mMediaSource->SetReadyState(MediaSourceReadyState::Open);
-  }
-  // TODO: Test append state.
-  // TODO: If aMode is "sequence", set sequence start time.
-  mAppendMode = aMode;
-}
-
-void
-SourceBuffer::SetTimestampOffset(double aTimestampOffset, ErrorResult& aRv)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MSE_API("SourceBuffer(%p)::SetTimestampOffset(aTimestampOffset=%f)", this, aTimestampOffset);
-  if (!IsAttached() || mUpdating) {
-    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return;
-  }
-  MOZ_ASSERT(mMediaSource->ReadyState() != MediaSourceReadyState::Closed);
-  if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) {
-    mMediaSource->SetReadyState(MediaSourceReadyState::Open);
-  }
-  // TODO: Test append state.
-  // TODO: If aMode is "sequence", set sequence start time.
-  mTimestampOffset = aTimestampOffset;
-}
-
-already_AddRefed<TimeRanges>
-SourceBuffer::GetBuffered(ErrorResult& aRv)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  if (!IsAttached()) {
-    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return nullptr;
-  }
-  nsRefPtr<TimeRanges> ranges = new TimeRanges();
-  double highestEndTime = mTrackBuffer->Buffered(ranges);
-  if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) {
-    // Set the end time on the last range to highestEndTime by adding a
-    // new range spanning the current end time to highestEndTime, which
-    // Normalize() will then merge with the old last range.
-    ranges->Add(ranges->GetEndTime(), highestEndTime);
-    ranges->Normalize();
-  }
-  MSE_DEBUGV("SourceBuffer(%p)::GetBuffered ranges=%s", this, DumpTimeRanges(ranges).get());
-  return ranges.forget();
-}
-
-void
-SourceBuffer::SetAppendWindowStart(double aAppendWindowStart, ErrorResult& aRv)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MSE_API("SourceBuffer(%p)::SetAppendWindowStart(aAppendWindowStart=%f)", this, aAppendWindowStart);
-  if (!IsAttached() || mUpdating) {
-    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return;
-  }
-  if (aAppendWindowStart < 0 || aAppendWindowStart >= mAppendWindowEnd) {
-    aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
-    return;
-  }
-  mAppendWindowStart = aAppendWindowStart;
-}
-
-void
-SourceBuffer::SetAppendWindowEnd(double aAppendWindowEnd, ErrorResult& aRv)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MSE_API("SourceBuffer(%p)::SetAppendWindowEnd(aAppendWindowEnd=%f)", this, aAppendWindowEnd);
-  if (!IsAttached() || mUpdating) {
-    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return;
-  }
-  if (IsNaN(aAppendWindowEnd) ||
-      aAppendWindowEnd <= mAppendWindowStart) {
-    aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
-    return;
-  }
-  mAppendWindowEnd = aAppendWindowEnd;
-}
-
-void
-SourceBuffer::AppendBuffer(const ArrayBuffer& aData, ErrorResult& aRv)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MSE_API("SourceBuffer(%p)::AppendBuffer(ArrayBuffer)", this);
-  aData.ComputeLengthAndData();
-  AppendData(aData.Data(), aData.Length(), aRv);
-}
-
-void
-SourceBuffer::AppendBuffer(const ArrayBufferView& aData, ErrorResult& aRv)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MSE_API("SourceBuffer(%p)::AppendBuffer(ArrayBufferView)", this);
-  aData.ComputeLengthAndData();
-  AppendData(aData.Data(), aData.Length(), aRv);
-}
-
-void
-SourceBuffer::Abort(ErrorResult& aRv)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MSE_API("SourceBuffer(%p)::Abort()", this);
-  if (!IsAttached()) {
-    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return;
-  }
-  if (mMediaSource->ReadyState() != MediaSourceReadyState::Open) {
-    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return;
-  }
-  if (mUpdating) {
-    // TODO: Abort segment parser loop, buffer append, and stream append loop algorithms.
-    AbortUpdating();
-  }
-  // TODO: Run reset parser algorithm.
-  mAppendWindowStart = 0;
-  mAppendWindowEnd = PositiveInfinity<double>();
-
-  MSE_DEBUG("SourceBuffer(%p)::Abort() Discarding decoder", this);
-  mTrackBuffer->DiscardDecoder();
-}
-
-void
-SourceBuffer::Remove(double aStart, double aEnd, ErrorResult& aRv)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MSE_API("SourceBuffer(%p)::Remove(aStart=%f, aEnd=%f)", this, aStart, aEnd);
-  if (!IsAttached()) {
-    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return;
-  }
-  if (IsNaN(mMediaSource->Duration()) ||
-      aStart < 0 || aStart > mMediaSource->Duration() ||
-      aEnd <= aStart || IsNaN(aEnd)) {
-    aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
-    return;
-  }
-  if (mUpdating || mMediaSource->ReadyState() != MediaSourceReadyState::Open) {
-    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return;
-  }
-  StartUpdating();
-  /// TODO: Run coded frame removal algorithm.
-
-  // Run the final step of the coded frame removal algorithm asynchronously
-  // to ensure the SourceBuffer's updating flag transition behaves as
-  // required by the spec.
-  nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &SourceBuffer::StopUpdating);
-  NS_DispatchToMainThread(event);
-}
-
-void
-SourceBuffer::Detach()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MSE_DEBUG("SourceBuffer(%p)::Detach", this);
-  if (mTrackBuffer) {
-    mTrackBuffer->Detach();
-  }
-  mTrackBuffer = nullptr;
-  mMediaSource = nullptr;
-}
-
-void
-SourceBuffer::Ended()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(IsAttached());
-  MSE_DEBUG("SourceBuffer(%p)::Ended", this);
-  mTrackBuffer->DiscardDecoder();
-}
-
-SourceBuffer::SourceBuffer(MediaSource* aMediaSource, const nsACString& aType)
-  : DOMEventTargetHelper(aMediaSource->GetParentObject())
-  , mMediaSource(aMediaSource)
-  , mType(aType)
-  , mAppendWindowStart(0)
-  , mAppendWindowEnd(PositiveInfinity<double>())
-  , mTimestampOffset(0)
-  , mAppendMode(SourceBufferAppendMode::Segments)
-  , mUpdating(false)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(aMediaSource);
-  mEvictionThreshold = Preferences::GetUint("media.mediasource.eviction_threshold",
-                                            75 * (1 << 20));
-  mParser = ContainerParser::CreateForMIMEType(aType);
-  mTrackBuffer = new TrackBuffer(aMediaSource->GetDecoder(), aType);
-  MSE_DEBUG("SourceBuffer(%p)::SourceBuffer: Create mParser=%p mTrackBuffer=%p",
-            this, mParser.get(), mTrackBuffer.get());
-}
-
-SourceBuffer::~SourceBuffer()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(!mMediaSource);
-  MSE_DEBUG("SourceBuffer(%p)::~SourceBuffer", this);
-}
-
-MediaSource*
-SourceBuffer::GetParentObject() const
-{
-  return mMediaSource;
-}
-
-JSObject*
-SourceBuffer::WrapObject(JSContext* aCx)
-{
-  return SourceBufferBinding::Wrap(aCx, this);
-}
-
-void
-SourceBuffer::DispatchSimpleEvent(const char* aName)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MSE_API("SourceBuffer(%p) Dispatch event '%s'", this, aName);
-  DispatchTrustedEvent(NS_ConvertUTF8toUTF16(aName));
-}
-
-void
-SourceBuffer::QueueAsyncSimpleEvent(const char* aName)
-{
-  MSE_DEBUG("SourceBuffer(%p) Queuing event '%s'", this, aName);
-  nsCOMPtr<nsIRunnable> event = new AsyncEventRunner<SourceBuffer>(this, aName);
-  NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
-}
-
-void
-SourceBuffer::StartUpdating()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(!mUpdating);
-  mUpdating = true;
-  QueueAsyncSimpleEvent("updatestart");
-}
-
-void
-SourceBuffer::StopUpdating()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(mUpdating);
-  mUpdating = false;
-  QueueAsyncSimpleEvent("update");
-  QueueAsyncSimpleEvent("updateend");
-}
-
-void
-SourceBuffer::AbortUpdating()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(mUpdating);
-  mUpdating = false;
-  QueueAsyncSimpleEvent("abort");
-  QueueAsyncSimpleEvent("updateend");
-}
-
-void
-SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv)
-{
-  MSE_DEBUG("SourceBuffer(%p)::AppendData(aLength=%u)", this, aLength);
-  if (!PrepareAppend(aRv)) {
-    return;
-  }
-  StartUpdating();
-  // TODO: Run more of the buffer append algorithm asynchronously.
-  if (mParser->IsInitSegmentPresent(aData, aLength)) {
-    MSE_DEBUG("SourceBuffer(%p)::AppendData: New initialization segment.", this);
-    mMediaSource->QueueInitializationEvent();
-    mTrackBuffer->DiscardDecoder();
-    if (!mTrackBuffer->NewDecoder()) {
-      aRv.Throw(NS_ERROR_FAILURE); // XXX: Review error handling.
-      return;
-    }
-    MSE_DEBUG("SourceBuffer(%p)::AppendData: Decoder marked as initialized.", this);
-  } else if (!mTrackBuffer->HasInitSegment()) {
-    MSE_DEBUG("SourceBuffer(%p)::AppendData: Non-init segment appended during initialization.", this);
-    Optional<MediaSourceEndOfStreamError> decodeError(MediaSourceEndOfStreamError::Decode);
-    ErrorResult dummy;
-    mMediaSource->EndOfStream(decodeError, dummy);
-    aRv.Throw(NS_ERROR_FAILURE);
-    return;
-  }
-  int64_t start, end;
-  if (mParser->ParseStartAndEndTimestamps(aData, aLength, start, end)) {
-    int64_t lastStart, lastEnd;
-    mTrackBuffer->LastTimestamp(lastStart, lastEnd);
-    if (mParser->IsMediaSegmentPresent(aData, aLength) &&
-        !mParser->TimestampsFuzzyEqual(start, lastEnd)) {
-      MSE_DEBUG("SourceBuffer(%p)::AppendData: Data last=[%lld, %lld] overlaps [%lld, %lld]",
-                this, lastStart, lastEnd, start, end);
-
-      // This data is earlier in the timeline than data we have already
-      // processed, so we must create a new decoder to handle the decoding.
-      mTrackBuffer->DiscardDecoder();
-
-      // If we've got a decoder here, it's not initialized, so we can use it
-      // rather than creating a new one.
-      if (!mTrackBuffer->NewDecoder()) {
-        aRv.Throw(NS_ERROR_FAILURE); // XXX: Review error handling.
-        return;
-      }
-      MSE_DEBUG("SourceBuffer(%p)::AppendData: Decoder marked as initialized.", this);
-      const nsTArray<uint8_t>& initData = mParser->InitData();
-      mTrackBuffer->AppendData(initData.Elements(), initData.Length());
-      mTrackBuffer->SetLastStartTimestamp(start);
-    }
-    mTrackBuffer->SetLastEndTimestamp(end);
-    MSE_DEBUG("SourceBuffer(%p)::AppendData: Segment last=[%lld, %lld] [%lld, %lld]",
-              this, lastStart, lastEnd, start, end);
-  }
-  if (!mTrackBuffer->AppendData(aData, aLength)) {
-    Optional<MediaSourceEndOfStreamError> decodeError(MediaSourceEndOfStreamError::Decode);
-    ErrorResult dummy;
-    mMediaSource->EndOfStream(decodeError, dummy);
-    aRv.Throw(NS_ERROR_FAILURE);
-    return;
-  }
-
-  // Schedule the state machine thread to ensure playback starts
-  // if required when data is appended.
-  mMediaSource->GetDecoder()->ScheduleStateMachineThread();
-
-  // Run the final step of the buffer append algorithm asynchronously to
-  // ensure the SourceBuffer's updating flag transition behaves as required
-  // by the spec.
-  nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &SourceBuffer::StopUpdating);
-  NS_DispatchToMainThread(event);
-}
-
-bool
-SourceBuffer::PrepareAppend(ErrorResult& aRv)
-{
-  if (!IsAttached() || mUpdating) {
-    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return false;
-  }
-  if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) {
-    mMediaSource->SetReadyState(MediaSourceReadyState::Open);
-  }
-
-  // Eviction uses a byte threshold. If the buffer is greater than the
-  // number of bytes then data is evicted. The time range for this
-  // eviction is reported back to the media source. It will then
-  // evict data before that range across all SourceBuffers it knows
-  // about.
-  // TODO: Make the eviction threshold smaller for audio-only streams.
-  // TODO: Drive evictions off memory pressure notifications.
-  // TODO: Consider a global eviction threshold  rather than per TrackBuffer.
-  bool evicted = mTrackBuffer->EvictData(mEvictionThreshold);
-  if (evicted) {
-    MSE_DEBUG("SourceBuffer(%p)::AppendData Evict; current buffered start=%f",
-              this, GetBufferedStart());
-
-    // We notify that we've evicted from the time range 0 through to
-    // the current start point.
-    mMediaSource->NotifyEvicted(0.0, GetBufferedStart());
-  }
-
-  // TODO: Test buffer full flag.
-  return true;
-}
-
-double
-SourceBuffer::GetBufferedStart()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  ErrorResult dummy;
-  nsRefPtr<TimeRanges> ranges = GetBuffered(dummy);
-  return ranges->Length() > 0 ? ranges->GetStartTime() : 0;
-}
-
-double
-SourceBuffer::GetBufferedEnd()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  ErrorResult dummy;
-  nsRefPtr<TimeRanges> ranges = GetBuffered(dummy);
-  return ranges->Length() > 0 ? ranges->GetEndTime() : 0;
-}
-
-void
-SourceBuffer::Evict(double aStart, double aEnd)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MSE_DEBUG("SourceBuffer(%p)::Evict(aStart=%f, aEnd=%f)", this, aStart, aEnd);
-  double currentTime = mMediaSource->GetDecoder()->GetCurrentTime();
-  double evictTime = aEnd;
-  const double safety_threshold = 5;
-  if (currentTime + safety_threshold >= evictTime) {
-    evictTime -= safety_threshold;
-  }
-  mTrackBuffer->EvictBefore(evictTime);
-}
-
-#if defined(DEBUG)
-void
-SourceBuffer::Dump(const char* aPath)
-{
-  if (mTrackBuffer) {
-    mTrackBuffer->Dump(aPath);
-  }
-}
-#endif
-
-NS_IMPL_CYCLE_COLLECTION_INHERITED(SourceBuffer, DOMEventTargetHelper,
-                                   mMediaSource)
-
-NS_IMPL_ADDREF_INHERITED(SourceBuffer, DOMEventTargetHelper)
-NS_IMPL_RELEASE_INHERITED(SourceBuffer, DOMEventTargetHelper)
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(SourceBuffer)
-NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
-
-} // namespace dom
-
 } // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/media/mediasource/ContainerParser.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_CONTAINERPARSER_H_
+#define MOZILLA_CONTAINERPARSER_H_
+
+#include "nsTArray.h"
+
+namespace mozilla {
+
+class ContainerParser {
+public:
+  ContainerParser() : mHasInitData(false) {}
+  virtual ~ContainerParser() {}
+
+  // Return true if aData starts with an initialization segment.
+  // The base implementation exists only for debug logging and is expected
+  // to be called first from the overriding implementation.
+  virtual bool IsInitSegmentPresent(const uint8_t* aData, uint32_t aLength);
+
+  // Return true if aData starts with a media segment.
+  // The base implementation exists only for debug logging and is expected
+  // to be called first from the overriding implementation.
+  virtual bool IsMediaSegmentPresent(const uint8_t* aData, uint32_t aLength);
+
+  // Parse aData to extract the start and end frame times from the media
+  // segment.  aData may not start on a parser sync boundary.  Return true
+  // if aStart and aEnd have been updated.
+  virtual bool ParseStartAndEndTimestamps(const uint8_t* aData, uint32_t aLength,
+                                          int64_t& aStart, int64_t& aEnd);
+
+  // Compare aLhs and rHs, considering any error that may exist in the
+  // timestamps from the format's base representation.  Return true if aLhs
+  // == aRhs within the error epsilon.
+  virtual bool TimestampsFuzzyEqual(int64_t aLhs, int64_t aRhs);
+
+  const nsTArray<uint8_t>& InitData();
+
+  bool HasInitData()
+  {
+    return mHasInitData;
+  }
+
+  static ContainerParser* CreateForMIMEType(const nsACString& aType);
+
+protected:
+  nsTArray<uint8_t> mInitData;
+  bool mHasInitData;
+};
+
+} // namespace mozilla
+#endif /* MOZILLA_CONTAINERPARSER_H_ */
--- a/content/media/mediasource/SourceBuffer.cpp
+++ b/content/media/mediasource/SourceBuffer.cpp
@@ -4,25 +4,21 @@
  * 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 "SourceBuffer.h"
 
 #include "AsyncEventRunner.h"
 #include "MediaSourceUtils.h"
 #include "TrackBuffer.h"
-#include "WebMBufferedParser.h"
-#include "mozilla/Endian.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/MediaSourceBinding.h"
 #include "mozilla/dom/TimeRanges.h"
-#include "mp4_demuxer/BufferStream.h"
-#include "mp4_demuxer/MoofParser.h"
 #include "nsError.h"
 #include "nsIEventTarget.h"
 #include "nsIRunnable.h"
 #include "nsThreadUtils.h"
 #include "prlog.h"
 
 struct JSContext;
 class JSObject;
@@ -37,283 +33,16 @@ extern PRLogModuleInfo* GetMediaSourceAP
 #else
 #define MSE_DEBUG(...)
 #define MSE_DEBUGV(...)
 #define MSE_API(...)
 #endif
 
 namespace mozilla {
 
-class ContainerParser {
-public:
-  virtual ~ContainerParser() {}
-
-  // Return true if aData starts with an initialization segment.
-  // The base implementation exists only for debug logging and is expected
-  // to be called first from the overriding implementation.
-  virtual bool IsInitSegmentPresent(const uint8_t* aData, uint32_t aLength)
-  {
-    MSE_DEBUG("ContainerParser(%p)::IsInitSegmentPresent aLength=%u [%x%x%x%x]",
-              this, aLength,
-              aLength > 0 ? aData[0] : 0,
-              aLength > 1 ? aData[1] : 0,
-              aLength > 2 ? aData[2] : 0,
-              aLength > 3 ? aData[3] : 0);
-    return false;
-  }
-
-  // Return true if aData starts with a media segment.
-  // The base implementation exists only for debug logging and is expected
-  // to be called first from the overriding implementation.
-  virtual bool IsMediaSegmentPresent(const uint8_t* aData, uint32_t aLength)
-  {
-    MSE_DEBUG("ContainerParser(%p)::IsMediaSegmentPresent aLength=%u [%x%x%x%x]",
-              this, aLength,
-              aLength > 0 ? aData[0] : 0,
-              aLength > 1 ? aData[1] : 0,
-              aLength > 2 ? aData[2] : 0,
-              aLength > 3 ? aData[3] : 0);
-    return false;
-  }
-
-  // Parse aData to extract the start and end frame times from the media
-  // segment.  aData may not start on a parser sync boundary.  Return true
-  // if aStart and aEnd have been updated.
-  virtual bool ParseStartAndEndTimestamps(const uint8_t* aData, uint32_t aLength,
-                                          int64_t& aStart, int64_t& aEnd)
-  {
-    return false;
-  }
-
-  // Compare aLhs and rHs, considering any error that may exist in the
-  // timestamps from the format's base representation.  Return true if aLhs
-  // == aRhs within the error epsilon.
-  virtual bool TimestampsFuzzyEqual(int64_t aLhs, int64_t aRhs)
-  {
-    NS_WARNING("Using default ContainerParser::TimestampFuzzyEquals implementation");
-    return aLhs == aRhs;
-  }
-
-  virtual const nsTArray<uint8_t>& InitData()
-  {
-    MOZ_ASSERT(mInitData.Length() > 0);
-    return mInitData;
-  }
-
-  static ContainerParser* CreateForMIMEType(const nsACString& aType);
-
-protected:
-  nsTArray<uint8_t> mInitData;
-};
-
-class WebMContainerParser : public ContainerParser {
-public:
-  WebMContainerParser()
-    : mParser(0), mOffset(0)
-  {}
-
-  static const unsigned NS_PER_USEC = 1000;
-
-  bool IsInitSegmentPresent(const uint8_t* aData, uint32_t aLength)
-  {
-    ContainerParser::IsInitSegmentPresent(aData, aLength);
-    // XXX: This is overly primitive, needs to collect data as it's appended
-    // to the SB and handle, rather than assuming everything is present in a
-    // single aData segment.
-    // 0x1a45dfa3 // EBML
-    // ...
-    // DocType == "webm"
-    // ...
-    // 0x18538067 // Segment (must be "unknown" size)
-    // 0x1549a966 // -> Segment Info
-    // 0x1654ae6b // -> One or more Tracks
-    if (aLength >= 4 &&
-        aData[0] == 0x1a && aData[1] == 0x45 && aData[2] == 0xdf && aData[3] == 0xa3) {
-      return true;
-    }
-    return false;
-  }
-
-  bool IsMediaSegmentPresent(const uint8_t* aData, uint32_t aLength)
-  {
-    ContainerParser::IsMediaSegmentPresent(aData, aLength);
-    // XXX: This is overly primitive, needs to collect data as it's appended
-    // to the SB and handle, rather than assuming everything is present in a
-    // single aData segment.
-    // 0x1a45dfa3 // EBML
-    // ...
-    // DocType == "webm"
-    // ...
-    // 0x18538067 // Segment (must be "unknown" size)
-    // 0x1549a966 // -> Segment Info
-    // 0x1654ae6b // -> One or more Tracks
-    if (aLength >= 4 &&
-        aData[0] == 0x1f && aData[1] == 0x43 && aData[2] == 0xb6 && aData[3] == 0x75) {
-      return true;
-    }
-    return false;
-  }
-
-  bool ParseStartAndEndTimestamps(const uint8_t* aData, uint32_t aLength,
-                                  int64_t& aStart, int64_t& aEnd)
-  {
-    bool initSegment = IsInitSegmentPresent(aData, aLength);
-    if (initSegment) {
-      mOffset = 0;
-      mParser = WebMBufferedParser(0);
-      mOverlappedMapping.Clear();
-    }
-
-    // XXX if it only adds new mappings, overlapped but not available
-    // (e.g. overlap < 0) frames are "lost" from the reported mappings here.
-    nsTArray<WebMTimeDataOffset> mapping;
-    mapping.AppendElements(mOverlappedMapping);
-    mOverlappedMapping.Clear();
-    ReentrantMonitor dummy("dummy");
-    mParser.Append(aData, aLength, mapping, dummy);
-
-    // XXX This is a bit of a hack.  Assume if there are no timecodes
-    // present and it's an init segment that it's _just_ an init segment.
-    // We should be more precise.
-    if (initSegment) {
-      uint32_t length = aLength;
-      if (!mapping.IsEmpty()) {
-        length = mapping[0].mSyncOffset;
-        MOZ_ASSERT(length <= aLength);
-      }
-      MSE_DEBUG("WebMContainerParser(%p)::ParseStartAndEndTimestamps: Stashed init of %u bytes.",
-                this, length);
-
-      mInitData.ReplaceElementsAt(0, mInitData.Length(), aData, length);
-    }
-    mOffset += aLength;
-
-    if (mapping.IsEmpty()) {
-      return false;
-    }
-
-    // Exclude frames that we don't enough data to cover the end of.
-    uint32_t endIdx = mapping.Length() - 1;
-    while (mOffset < mapping[endIdx].mEndOffset && endIdx > 0) {
-      endIdx -= 1;
-    }
-
-    if (endIdx == 0) {
-      return false;
-    }
-
-    uint64_t frameDuration = mapping[endIdx].mTimecode - mapping[endIdx - 1].mTimecode;
-    aStart = mapping[0].mTimecode / NS_PER_USEC;
-    aEnd = (mapping[endIdx].mTimecode + frameDuration) / NS_PER_USEC;
-
-    MSE_DEBUG("WebMContainerParser(%p)::ParseStartAndEndTimestamps: [%lld, %lld] [fso=%lld, leo=%lld, l=%u endIdx=%u]",
-              this, aStart, aEnd, mapping[0].mSyncOffset, mapping[endIdx].mEndOffset, mapping.Length(), endIdx);
-
-    mapping.RemoveElementsAt(0, endIdx + 1);
-    mOverlappedMapping.AppendElements(mapping);
-
-    return true;
-  }
-
-  bool TimestampsFuzzyEqual(int64_t aLhs, int64_t aRhs)
-  {
-    int64_t error = mParser.GetTimecodeScale() / NS_PER_USEC;
-    return llabs(aLhs - aRhs) <= error * 2;
-  }
-
-private:
-  WebMBufferedParser mParser;
-  nsTArray<WebMTimeDataOffset> mOverlappedMapping;
-  int64_t mOffset;
-};
-
-class MP4ContainerParser : public ContainerParser {
-public:
-  MP4ContainerParser() {}
-
-  bool IsInitSegmentPresent(const uint8_t* aData, uint32_t aLength)
-  {
-    ContainerParser::IsInitSegmentPresent(aData, aLength);
-    // Each MP4 atom has a chunk size and chunk type. The root chunk in an MP4
-    // file is the 'ftyp' atom followed by a file type. We just check for a
-    // vaguely valid 'ftyp' atom.
-
-    if (aLength < 8) {
-      return false;
-    }
-
-    uint32_t chunk_size = BigEndian::readUint32(aData);
-    if (chunk_size < 8) {
-      return false;
-    }
-
-    return aData[4] == 'f' && aData[5] == 't' && aData[6] == 'y' &&
-           aData[7] == 'p';
-  }
-
-  bool ParseStartAndEndTimestamps(const uint8_t* aData, uint32_t aLength,
-                                  int64_t& aStart, int64_t& aEnd)
-  {
-    bool initSegment = IsInitSegmentPresent(aData, aLength);
-    if (initSegment) {
-      mStream = new mp4_demuxer::BufferStream();
-      mParser = new mp4_demuxer::MoofParser(mStream, 0);
-    } else if (!mStream || !mParser) {
-      return false;
-    }
-
-    mStream->AppendBytes(aData, aLength);
-    nsTArray<MediaByteRange> byteRanges;
-    byteRanges.AppendElement(mStream->GetByteRange());
-    mParser->RebuildFragmentedIndex(byteRanges);
-
-    if (initSegment) {
-      const MediaByteRange& range = mParser->mInitRange;
-      MSE_DEBUG("MP4ContainerParser(%p)::ParseStartAndEndTimestamps: Stashed init of %u bytes.",
-                this, range.mEnd - range.mStart);
-
-      mInitData.ReplaceElementsAt(0, mInitData.Length(),
-                                  aData + range.mStart,
-                                  range.mEnd - range.mStart);
-    }
-
-    mp4_demuxer::Interval<mp4_demuxer::Microseconds> compositionRange =
-      mParser->GetCompositionRange(byteRanges);
-
-    mStream->DiscardBefore(mParser->mOffset);
-
-    if (compositionRange.IsNull()) {
-      return false;
-    }
-    aStart = compositionRange.start;
-    aEnd = compositionRange.end;
-    MSE_DEBUG("MP4ContainerParser(%p)::ParseStartAndEndTimestamps: [%lld, %lld]",
-              this, aStart, aEnd);
-    return true;
-  }
-
-private:
-  nsRefPtr<mp4_demuxer::BufferStream> mStream;
-  nsAutoPtr<mp4_demuxer::MoofParser> mParser;
-};
-
-
-/*static*/ ContainerParser*
-ContainerParser::CreateForMIMEType(const nsACString& aType)
-{
-  if (aType.LowerCaseEqualsLiteral("video/webm") || aType.LowerCaseEqualsLiteral("audio/webm")) {
-    return new WebMContainerParser();
-  }
-
-  if (aType.LowerCaseEqualsLiteral("video/mp4") || aType.LowerCaseEqualsLiteral("audio/mp4")) {
-    return new MP4ContainerParser();
-  }
-  return new ContainerParser();
-}
-
 namespace dom {
 
 void
 SourceBuffer::SetMode(SourceBufferAppendMode aMode, ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MSE_API("SourceBuffer(%p)::SetMode(aMode=%d)", this, aMode);
   if (!IsAttached() || mUpdating) {
@@ -492,31 +221,29 @@ SourceBuffer::Ended()
   MOZ_ASSERT(IsAttached());
   MSE_DEBUG("SourceBuffer(%p)::Ended", this);
   mTrackBuffer->DiscardDecoder();
 }
 
 SourceBuffer::SourceBuffer(MediaSource* aMediaSource, const nsACString& aType)
   : DOMEventTargetHelper(aMediaSource->GetParentObject())
   , mMediaSource(aMediaSource)
-  , mType(aType)
   , mAppendWindowStart(0)
   , mAppendWindowEnd(PositiveInfinity<double>())
   , mTimestampOffset(0)
   , mAppendMode(SourceBufferAppendMode::Segments)
   , mUpdating(false)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aMediaSource);
   mEvictionThreshold = Preferences::GetUint("media.mediasource.eviction_threshold",
                                             75 * (1 << 20));
-  mParser = ContainerParser::CreateForMIMEType(aType);
   mTrackBuffer = new TrackBuffer(aMediaSource->GetDecoder(), aType);
-  MSE_DEBUG("SourceBuffer(%p)::SourceBuffer: Create mParser=%p mTrackBuffer=%p",
-            this, mParser.get(), mTrackBuffer.get());
+  MSE_DEBUG("SourceBuffer(%p)::SourceBuffer: Create mTrackBuffer=%p",
+            this, mTrackBuffer.get());
 }
 
 SourceBuffer::~SourceBuffer()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!mMediaSource);
   MSE_DEBUG("SourceBuffer(%p)::~SourceBuffer", this);
 }
@@ -581,73 +308,28 @@ SourceBuffer::AbortUpdating()
 void
 SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv)
 {
   MSE_DEBUG("SourceBuffer(%p)::AppendData(aLength=%u)", this, aLength);
   if (!PrepareAppend(aRv)) {
     return;
   }
   StartUpdating();
-  // TODO: Run more of the buffer append algorithm asynchronously.
-  if (mParser->IsInitSegmentPresent(aData, aLength)) {
-    MSE_DEBUG("SourceBuffer(%p)::AppendData: New initialization segment.", this);
-    mMediaSource->QueueInitializationEvent();
-    mTrackBuffer->DiscardDecoder();
-    if (!mTrackBuffer->NewDecoder()) {
-      aRv.Throw(NS_ERROR_FAILURE); // XXX: Review error handling.
-      return;
-    }
-    MSE_DEBUG("SourceBuffer(%p)::AppendData: Decoder marked as initialized.", this);
-  } else if (!mTrackBuffer->HasInitSegment()) {
-    MSE_DEBUG("SourceBuffer(%p)::AppendData: Non-init segment appended during initialization.", this);
-    Optional<MediaSourceEndOfStreamError> decodeError(MediaSourceEndOfStreamError::Decode);
-    ErrorResult dummy;
-    mMediaSource->EndOfStream(decodeError, dummy);
-    aRv.Throw(NS_ERROR_FAILURE);
-    return;
-  }
-  int64_t start, end;
-  if (mParser->ParseStartAndEndTimestamps(aData, aLength, start, end)) {
-    int64_t lastStart, lastEnd;
-    mTrackBuffer->LastTimestamp(lastStart, lastEnd);
-    if (mParser->IsMediaSegmentPresent(aData, aLength) &&
-        !mParser->TimestampsFuzzyEqual(start, lastEnd)) {
-      MSE_DEBUG("SourceBuffer(%p)::AppendData: Data last=[%lld, %lld] overlaps [%lld, %lld]",
-                this, lastStart, lastEnd, start, end);
 
-      // This data is earlier in the timeline than data we have already
-      // processed, so we must create a new decoder to handle the decoding.
-      mTrackBuffer->DiscardDecoder();
-
-      // If we've got a decoder here, it's not initialized, so we can use it
-      // rather than creating a new one.
-      if (!mTrackBuffer->NewDecoder()) {
-        aRv.Throw(NS_ERROR_FAILURE); // XXX: Review error handling.
-        return;
-      }
-      MSE_DEBUG("SourceBuffer(%p)::AppendData: Decoder marked as initialized.", this);
-      const nsTArray<uint8_t>& initData = mParser->InitData();
-      mTrackBuffer->AppendData(initData.Elements(), initData.Length());
-      mTrackBuffer->SetLastStartTimestamp(start);
-    }
-    mTrackBuffer->SetLastEndTimestamp(end);
-    MSE_DEBUG("SourceBuffer(%p)::AppendData: Segment last=[%lld, %lld] [%lld, %lld]",
-              this, lastStart, lastEnd, start, end);
-  }
   if (!mTrackBuffer->AppendData(aData, aLength)) {
     Optional<MediaSourceEndOfStreamError> decodeError(MediaSourceEndOfStreamError::Decode);
     ErrorResult dummy;
     mMediaSource->EndOfStream(decodeError, dummy);
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
 
-  // Schedule the state machine thread to ensure playback starts
-  // if required when data is appended.
-  mMediaSource->GetDecoder()->ScheduleStateMachineThread();
+  if (mTrackBuffer->HasInitSegment()) {
+    mMediaSource->QueueInitializationEvent();
+  }
 
   // Run the final step of the buffer append algorithm asynchronously to
   // ensure the SourceBuffer's updating flag transition behaves as required
   // by the spec.
   nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &SourceBuffer::StopUpdating);
   NS_DispatchToMainThread(event);
 }
 
--- a/content/media/mediasource/SourceBuffer.h
+++ b/content/media/mediasource/SourceBuffer.h
@@ -23,17 +23,16 @@
 #include "nsString.h"
 #include "nscore.h"
 
 class JSObject;
 struct JSContext;
 
 namespace mozilla {
 
-class ContainerParser;
 class ErrorResult;
 class TrackBuffer;
 template <typename T> class AsyncEventRunner;
 
 namespace dom {
 
 class TimeRanges;
 
@@ -115,43 +114,32 @@ public:
 
 private:
   ~SourceBuffer();
 
   friend class AsyncEventRunner<SourceBuffer>;
   void DispatchSimpleEvent(const char* aName);
   void QueueAsyncSimpleEvent(const char* aName);
 
-  // Create a new decoder for mType, and store the result in mDecoder.
-  // Returns true if mDecoder was set.
-  bool InitNewDecoder();
-
-  // Set mDecoder to null and reset mDecoderInitialized.
-  void DiscardDecoder();
-
   // Update mUpdating and fire the appropriate events.
   void StartUpdating();
   void StopUpdating();
   void AbortUpdating();
 
   // Shared implementation of AppendBuffer overloads.
   void AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv);
 
   // Implements the "Prepare Append Algorithm".  Returns true if the append
   // may continue, or false (with aRv set) on error.
   bool PrepareAppend(ErrorResult& aRv);
 
   nsRefPtr<MediaSource> mMediaSource;
 
-  const nsCString mType;
-
   uint32_t mEvictionThreshold;
 
-  nsAutoPtr<ContainerParser> mParser;
-
   nsRefPtr<TrackBuffer> mTrackBuffer;
 
   double mAppendWindowStart;
   double mAppendWindowEnd;
 
   double mTimestampOffset;
 
   SourceBufferAppendMode mAppendMode;
--- a/content/media/mediasource/TrackBuffer.cpp
+++ b/content/media/mediasource/TrackBuffer.cpp
@@ -1,37 +1,29 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "TrackBuffer.h"
 
+#include "ContainerParser.h"
 #include "MediaSourceDecoder.h"
 #include "SharedThreadPool.h"
 #include "MediaTaskQueue.h"
 #include "SourceBufferDecoder.h"
 #include "SourceBufferResource.h"
 #include "VideoUtils.h"
-#include "mozilla/FloatingPoint.h"
 #include "mozilla/dom/TimeRanges.h"
 #include "nsError.h"
 #include "nsIRunnable.h"
 #include "nsThreadUtils.h"
 #include "prlog.h"
 
-#if defined(DEBUG)
-#include <sys/stat.h>
-#include <sys/types.h>
-#endif
-
-struct JSContext;
-class JSObject;
-
 #ifdef PR_LOGGING
 extern PRLogModuleInfo* GetMediaSourceLog();
 extern PRLogModuleInfo* GetMediaSourceAPILog();
 
 #define MSE_DEBUG(...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG, (__VA_ARGS__))
 #define MSE_DEBUGV(...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG+1, (__VA_ARGS__))
 #define MSE_API(...) PR_LOG(GetMediaSourceAPILog(), PR_LOG_DEBUG, (__VA_ARGS__))
 #else
@@ -42,19 +34,19 @@ extern PRLogModuleInfo* GetMediaSourceAP
 
 namespace mozilla {
 
 TrackBuffer::TrackBuffer(MediaSourceDecoder* aParentDecoder, const nsACString& aType)
   : mParentDecoder(aParentDecoder)
   , mType(aType)
   , mLastStartTimestamp(0)
   , mLastEndTimestamp(0)
-  , mHasInit(false)
 {
   MOZ_COUNT_CTOR(TrackBuffer);
+  mParser = ContainerParser::CreateForMIMEType(aType);
   mTaskQueue = new MediaTaskQueue(GetMediaDecodeThreadPool());
   aParentDecoder->AddTrackBuffer(this);
 }
 
 TrackBuffer::~TrackBuffer()
 {
   MOZ_COUNT_DTOR(TrackBuffer);
 }
@@ -99,16 +91,63 @@ TrackBuffer::Shutdown()
   MOZ_ASSERT(mDecoders.IsEmpty());
   mParentDecoder = nullptr;
 }
 
 bool
 TrackBuffer::AppendData(const uint8_t* aData, uint32_t aLength)
 {
   MOZ_ASSERT(NS_IsMainThread());
+  // TODO: Run more of the buffer append algorithm asynchronously.
+  if (mParser->IsInitSegmentPresent(aData, aLength)) {
+    MSE_DEBUG("TrackBuffer(%p)::AppendData: New initialization segment.", this);
+    if (!NewDecoder()) {
+      return false;
+    }
+  } else if (!mParser->HasInitData()) {
+    MSE_DEBUG("TrackBuffer(%p)::AppendData: Non-init segment appended during initialization.", this);
+    return false;
+  }
+
+  int64_t start, end;
+  if (mParser->ParseStartAndEndTimestamps(aData, aLength, start, end)) {
+    if (mParser->IsMediaSegmentPresent(aData, aLength) &&
+        !mParser->TimestampsFuzzyEqual(start, mLastEndTimestamp)) {
+      MSE_DEBUG("TrackBuffer(%p)::AppendData: Data last=[%lld, %lld] overlaps [%lld, %lld]",
+                this, mLastStartTimestamp, mLastEndTimestamp, start, end);
+
+      // This data is earlier in the timeline than data we have already
+      // processed, so we must create a new decoder to handle the decoding.
+      if (!NewDecoder()) {
+        return false;
+      }
+      MSE_DEBUG("TrackBuffer(%p)::AppendData: Decoder marked as initialized.", this);
+      const nsTArray<uint8_t>& initData = mParser->InitData();
+      AppendDataToCurrentResource(initData.Elements(), initData.Length());
+      mLastStartTimestamp = start;
+    }
+    mLastEndTimestamp = end;
+    MSE_DEBUG("TrackBuffer(%p)::AppendData: Segment last=[%lld, %lld] [%lld, %lld]",
+              this, mLastStartTimestamp, mLastEndTimestamp, start, end);
+  }
+
+  if (!AppendDataToCurrentResource(aData, aLength)) {
+    return false;
+  }
+
+  // Schedule the state machine thread to ensure playback starts if required
+  // when data is appended.
+  mParentDecoder->ScheduleStateMachineThread();
+  return true;
+}
+
+bool
+TrackBuffer::AppendDataToCurrentResource(const uint8_t* aData, uint32_t aLength)
+{
+  MOZ_ASSERT(NS_IsMainThread());
   if (!mCurrentDecoder) {
     return false;
   }
 
   SourceBufferResource* resource = mCurrentDecoder->GetResource();
   int64_t appendOffset = resource->GetLength();
   resource->AppendData(aData, aLength);
   // XXX: For future reference: NDA call must run on the main thread.
@@ -174,29 +213,30 @@ TrackBuffer::Buffered(dom::TimeRanges* a
 
   return highestEndTime;
 }
 
 bool
 TrackBuffer::NewDecoder()
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(!mCurrentDecoder && mParentDecoder);
+  MOZ_ASSERT(mParentDecoder);
+
+  DiscardDecoder();
 
   nsRefPtr<SourceBufferDecoder> decoder = mParentDecoder->CreateSubDecoder(mType);
   if (!decoder) {
     return false;
   }
   ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
   mCurrentDecoder = decoder;
   mDecoders.AppendElement(decoder);
 
   mLastStartTimestamp = 0;
   mLastEndTimestamp = 0;
-  mHasInit = true;
 
   return QueueInitializeDecoder(decoder);
 }
 
 bool
 TrackBuffer::QueueInitializeDecoder(nsRefPtr<SourceBufferDecoder> aDecoder)
 {
   RefPtr<nsIRunnable> task =
@@ -311,47 +351,25 @@ TrackBuffer::Detach()
     DiscardDecoder();
   }
 }
 
 bool
 TrackBuffer::HasInitSegment()
 {
   ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
-  return mHasInit;
+  return mParser->HasInitData();
 }
 
 bool
 TrackBuffer::IsReady()
 {
   ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
   MOZ_ASSERT((mInfo.HasAudio() || mInfo.HasVideo()) || mInitializedDecoders.IsEmpty());
-  return HasInitSegment() && (mInfo.HasAudio() || mInfo.HasVideo());
-}
-
-void
-TrackBuffer::LastTimestamp(int64_t& aStart, int64_t& aEnd)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  aStart = mLastStartTimestamp;
-  aEnd = mLastEndTimestamp;
-}
-
-void
-TrackBuffer::SetLastStartTimestamp(int64_t aStart)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  mLastStartTimestamp = aStart;
-}
-
-void
-TrackBuffer::SetLastEndTimestamp(int64_t aEnd)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  mLastEndTimestamp = aEnd;
+  return mParser->HasInitData() && (mInfo.HasAudio() || mInfo.HasVideo());
 }
 
 bool
 TrackBuffer::ContainsTime(int64_t aTime)
 {
   ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
   for (uint32_t i = 0; i < mInitializedDecoders.Length(); ++i) {
     nsRefPtr<dom::TimeRanges> r = new dom::TimeRanges();
--- a/content/media/mediasource/TrackBuffer.h
+++ b/content/media/mediasource/TrackBuffer.h
@@ -12,16 +12,17 @@
 #include "mozilla/Attributes.h"
 #include "mozilla/mozalloc.h"
 #include "nsCOMPtr.h"
 #include "nsString.h"
 #include "nscore.h"
 
 namespace mozilla {
 
+class ContainerParser;
 class MediaSourceDecoder;
 
 namespace dom {
 
 class TimeRanges;
 
 } // namespace dom
 
@@ -40,39 +41,29 @@ public:
   bool EvictData(uint32_t aThreshold);
   void EvictBefore(double aTime);
 
   // Returns the highest end time of all of the buffered ranges in the
   // decoders managed by this TrackBuffer, and returns the union of the
   // decoders buffered ranges in aRanges.
   double Buffered(dom::TimeRanges* aRanges);
 
-  // Create a new decoder, set mCurrentDecoder to the new decoder, and queue
-  // the decoder for initialization.  The decoder is not considered
-  // initialized until it is added to mDecoders.
-  bool NewDecoder();
-
   // Mark the current decoder's resource as ended, clear mCurrentDecoder and
   // reset mLast{Start,End}Timestamp.
   void DiscardDecoder();
 
   void Detach();
 
   // Returns true if an init segment has been appended.
   bool HasInitSegment();
 
-  // Returns true iff HasInitSegment() and the decoder using that init
+  // Returns true iff mParser->HasInitData() and the decoder using that init
   // segment has successfully initialized by setting mHas{Audio,Video}..
   bool IsReady();
 
-  // Query and update mLast{Start,End}Timestamp.
-  void LastTimestamp(int64_t& aStart, int64_t& aEnd);
-  void SetLastStartTimestamp(int64_t aStart);
-  void SetLastEndTimestamp(int64_t aEnd);
-
   // Returns true if any of the decoders managed by this track buffer
   // contain aTime in their buffered ranges.
   bool ContainsTime(int64_t aTime);
 
   void BreakCycles();
 
   // Call ResetDecode() on each decoder in mDecoders.
   void ResetDecode();
@@ -84,16 +75,25 @@ public:
 
 #if defined(DEBUG)
   void Dump(const char* aPath);
 #endif
 
 private:
   ~TrackBuffer();
 
+  // Create a new decoder, set mCurrentDecoder to the new decoder, and queue
+  // the decoder for initialization.  The decoder is not considered
+  // initialized until it is added to mDecoders.
+  bool NewDecoder();
+
+  // Helper for AppendData, ensures NotifyDataArrived is called whenever
+  // data is appended to the current decoder's SourceBufferResource.
+  bool AppendDataToCurrentResource(const uint8_t* aData, uint32_t aLength);
+
   // Queue execution of InitializeDecoder on mTaskQueue.
   bool QueueInitializeDecoder(nsRefPtr<SourceBufferDecoder> aDecoder);
 
   // Runs decoder initialization including calling ReadMetadata.  Runs as an
   // event on the decode thread pool.
   void InitializeDecoder(nsRefPtr<SourceBufferDecoder> aDecoder);
 
   // Adds a successfully initialized decoder to mDecoders and (if it's the
@@ -107,16 +107,18 @@ private:
   bool ValidateTrackFormats(const MediaInfo& aInfo);
 
   // Remove aDecoder from mDecoders and dispatch an event to the main thread
   // to clean up the decoder.  If aDecoder was added to
   // mInitializedDecoders, it must have been removed before calling this
   // function.
   void RemoveDecoder(nsRefPtr<SourceBufferDecoder> aDecoder);
 
+  nsAutoPtr<ContainerParser> mParser;
+
   // A task queue using the shared media thread pool.  Used exclusively to
   // initialize (i.e. call ReadMetadata on) decoders as they are created via
   // NewDecoder.
   RefPtr<MediaTaskQueue> mTaskQueue;
 
   // All of the decoders managed by this TrackBuffer.  Access protected by
   // mParentDecoder's monitor.
   nsTArray<nsRefPtr<SourceBufferDecoder>> mDecoders;
@@ -131,19 +133,15 @@ private:
   nsRefPtr<MediaSourceDecoder> mParentDecoder;
   const nsCString mType;
 
   // The last start and end timestamps added to the TrackBuffer via
   // AppendData.  Accessed on the main thread only.
   int64_t mLastStartTimestamp;
   int64_t mLastEndTimestamp;
 
-  // Set when the initialization segment is first seen and cached (implied
-  // by new decoder creation).  Protected by mParentDecoder's monitor.
-  bool mHasInit;
-
   // Set when the first decoder used by this TrackBuffer is initialized.
   // Protected by mParentDecoder's monitor.
   MediaInfo mInfo;
 };
 
 } // namespace mozilla
 #endif /* MOZILLA_TRACKBUFFER_H_ */
--- a/content/media/mediasource/moz.build
+++ b/content/media/mediasource/moz.build
@@ -12,16 +12,17 @@ EXPORTS += [
 
 EXPORTS.mozilla.dom += [
     'MediaSource.h',
     'SourceBuffer.h',
     'SourceBufferList.h',
 ]
 
 UNIFIED_SOURCES += [
+    'ContainerParser.cpp',
     'MediaSource.cpp',
     'MediaSourceDecoder.cpp',
     'MediaSourceReader.cpp',
     'MediaSourceUtils.cpp',
     'SourceBuffer.cpp',
     'SourceBufferDecoder.cpp',
     'SourceBufferList.cpp',
     'SourceBufferResource.cpp',
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -664,20 +664,22 @@ public:
                    bool strict,
                    JS::MutableHandle<JS::Value> vp) const MOZ_OVERRIDE;
   virtual bool keys(JSContext *cx, JS::Handle<JSObject*> proxy,
                     JS::AutoIdVector &props) const MOZ_OVERRIDE;
   virtual bool iterate(JSContext *cx, JS::Handle<JSObject*> proxy,
                        unsigned flags,
                        JS::MutableHandle<JS::Value> vp) const MOZ_OVERRIDE;
 
+  static void ObjectMoved(JSObject *obj, const JSObject *old);
+
   static const nsOuterWindowProxy singleton;
 
 protected:
-  nsGlobalWindow* GetWindow(JSObject *proxy) const
+  static nsGlobalWindow* GetWindow(JSObject *proxy)
   {
     return nsGlobalWindow::FromSupports(
       static_cast<nsISupports*>(js::GetProxyExtra(proxy, 0).toPrivate()));
   }
 
   // False return value means we threw an exception.  True return value
   // but false "found" means we didn't have a subframe at that index.
   bool GetSubframeWindow(JSContext *cx, JS::Handle<JSObject*> proxy,
@@ -699,17 +701,18 @@ const js::Class OuterWindowProxyClass =
     PROXY_CLASS_WITH_EXT(
         "Proxy",
         0, /* additional slots */
         0, /* additional class flags */
         PROXY_MAKE_EXT(
             nullptr, /* outerObject */
             js::proxy_innerObject,
             nullptr, /* iteratorObject */
-            false   /* isWrappedNative */
+            false,   /* isWrappedNative */
+            nsOuterWindowProxy::ObjectMoved
         ));
 
 bool
 nsOuterWindowProxy::isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy,
                                  bool *extensible) const
 {
   // If [[Extensible]] could be false, then navigating a window could navigate
   // to a window that's [[Extensible]] after being at one that wasn't: an
@@ -1019,16 +1022,25 @@ nsOuterWindowProxy::watch(JSContext *cx,
 
 bool
 nsOuterWindowProxy::unwatch(JSContext *cx, JS::Handle<JSObject*> proxy,
                             JS::Handle<jsid> id) const
 {
   return js::UnwatchGuts(cx, proxy, id);
 }
 
+void
+nsOuterWindowProxy::ObjectMoved(JSObject *obj, const JSObject *old)
+{
+  nsGlobalWindow* global = GetWindow(obj);
+  if (global) {
+    global->UpdateWrapper(obj, old);
+  }
+}
+
 const nsOuterWindowProxy
 nsOuterWindowProxy::singleton;
 
 class nsChromeOuterWindowProxy : public nsOuterWindowProxy
 {
 public:
   MOZ_CONSTEXPR nsChromeOuterWindowProxy() : nsOuterWindowProxy() { }
 
--- a/dom/base/nsWrapperCache.h
+++ b/dom/base/nsWrapperCache.h
@@ -3,16 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsWrapperCache_h___
 #define nsWrapperCache_h___
 
 #include "nsCycleCollectionParticipant.h"
 #include "mozilla/Assertions.h"
+#include "js/Class.h"
 #include "js/Id.h"          // must come before js/RootingAPI.h
 #include "js/Value.h"       // must come before js/RootingAPI.h
 #include "js/RootingAPI.h"
 #include "js/TracingAPI.h"
 
 class XPCWrappedNativeScope;
 
 #define NS_WRAPPERCACHE_IID \
@@ -39,16 +40,24 @@ class XPCWrappedNativeScope;
  *  If WRAPPER_IS_DOM_BINDING is not set (IsDOMBinding() returns false):
  *    - a slim wrapper or the JSObject of an XPCWrappedNative wrapper
  *
  *  If WRAPPER_IS_DOM_BINDING is set (IsDOMBinding() returns true):
  *    - a DOM binding object (regular JS object or proxy)
  *
  * The finalizer for the wrapper clears the cache.
  *
+ * A compacting GC can move the wrapper object. Pointers to moved objects are
+ * usually found and updated by tracing the heap, however non-preserved wrappers
+ * are weak references and are not traced, so another approach is
+ * necessary. Instead a class hook (objectMovedOp) is provided that is called
+ * when an object is moved and is responsible for ensuring pointers are
+ * updated. It does this by calling UpdateWrapper() on the wrapper
+ * cache. SetWrapper() asserts that the hook is implemented for any wrapper set.
+ *
  * A number of the methods are implemented in nsWrapperCacheInlines.h because we
  * have to include some JS headers that don't play nicely with the rest of the
  * codebase. Include nsWrapperCacheInlines.h if you need to call those methods.
  */
 class nsWrapperCache
 {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_WRAPPERCACHE_IID)
@@ -84,31 +93,45 @@ public:
   {
     return GetWrapperJSObject();
   }
 
   void SetWrapper(JSObject* aWrapper)
   {
     MOZ_ASSERT(!PreservingWrapper(), "Clearing a preserved wrapper!");
     MOZ_ASSERT(aWrapper, "Use ClearWrapper!");
+    MOZ_ASSERT(js::HasObjectMovedOp(aWrapper),
+               "Object has not provided the hook to update the wrapper if it is moved");
 
     SetWrapperJSObject(aWrapper);
   }
 
   /**
    * Clear the wrapper. This should be called from the finalizer for the
    * wrapper.
    */
   void ClearWrapper()
   {
     MOZ_ASSERT(!PreservingWrapper(), "Clearing a preserved wrapper!");
 
     SetWrapperJSObject(nullptr);
   }
 
+  /**
+   * Update the wrapper if the object it contains is moved.
+   *
+   * This method must be called from the objectMovedOp class extension hook for
+   * any wrapper cached object.
+   */
+  void UpdateWrapper(JSObject* aNewObject, const JSObject* aOldObject)
+  {
+    MOZ_ASSERT(mWrapper == aOldObject);
+    mWrapper = aNewObject;
+  }
+
   bool PreservingWrapper()
   {
     return HasWrapperFlag(WRAPPER_BIT_PRESERVED);
   }
 
   void SetIsDOMBinding()
   {
     MOZ_ASSERT(!mWrapper && !(GetWrapperFlags() & ~WRAPPER_IS_DOM_BINDING),
@@ -157,17 +180,17 @@ public:
 
   void TraceWrapper(const TraceCallbacks& aCallbacks, void* aClosure)
   {
     if (PreservingWrapper() && mWrapper) {
       aCallbacks.Trace(&mWrapper, "Preserved wrapper", aClosure);
     }
   }
 
-  /* 
+  /*
    * The following methods for getting and manipulating flags allow the unused
    * bits of mFlags to be used by derived classes.
    */
 
   typedef uint32_t FlagsType;
 
   FlagsType GetFlags() const
   {
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -1208,16 +1208,34 @@ template<class T>
 inline void
 ClearWrapper(T* p, void*)
 {
   nsWrapperCache* cache;
   CallQueryInterface(p, &cache);
   ClearWrapper(p, cache);
 }
 
+template<class T>
+inline void
+UpdateWrapper(T* p, nsWrapperCache* cache, JSObject* obj, const JSObject* old)
+{
+  JS::AutoAssertGCCallback inCallback(obj);
+  cache->UpdateWrapper(obj, old);
+}
+
+template<class T>
+inline void
+UpdateWrapper(T* p, void*, JSObject* obj, const JSObject* old)
+{
+  JS::AutoAssertGCCallback inCallback(obj);
+  nsWrapperCache* cache;
+  CallQueryInterface(p, &cache);
+  UpdateWrapper(p, cache, obj, old);
+}
+
 // Attempt to preserve the wrapper, if any, for a Paris DOM bindings object.
 // Return true if we successfully preserved the wrapper, or there is no wrapper
 // to preserve. In the latter case we don't need to preserve the wrapper, because
 // the object can only be obtained by JS once, or they cannot be meaningfully
 // owned from the native side.
 //
 // This operation will return false only for non-nsISupports cycle-collected
 // objects, because we cannot determine if they are wrappercached or not.
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -12,16 +12,17 @@ import textwrap
 
 from WebIDL import BuiltinTypes, IDLBuiltinType, IDLNullValue, IDLSequenceType, IDLType, IDLAttribute, IDLUndefinedValue, IDLEmptySequenceValue
 from Configuration import NoSuchDescriptorError, getTypesFromDescriptor, getTypesFromDictionary, getTypesFromCallback, Descriptor
 
 AUTOGENERATED_WARNING_COMMENT = \
     "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n\n"
 ADDPROPERTY_HOOK_NAME = '_addProperty'
 FINALIZE_HOOK_NAME = '_finalize'
+OBJECT_MOVED_HOOK_NAME = '_objectMoved'
 CONSTRUCT_HOOK_NAME = '_constructor'
 LEGACYCALLER_HOOK_NAME = '_legacycaller'
 HASINSTANCE_HOOK_NAME = '_hasInstance'
 NEWRESOLVE_HOOK_NAME = '_newResolve'
 ENUMERATE_HOOK_NAME = '_enumerate'
 ENUM_ENTRY_VARIABLE_NAME = 'strings'
 INSTANCE_RESERVED_SLOTS = 1
 
@@ -359,58 +360,71 @@ class CGDOMJSClass(CGThing):
         self.descriptor = descriptor
 
     def declare(self):
         return ""
 
     def define(self):
         traceHook = 'nullptr'
         callHook = LEGACYCALLER_HOOK_NAME if self.descriptor.operations["LegacyCaller"] else 'nullptr'
+        objectMovedHook = OBJECT_MOVED_HOOK_NAME if self.descriptor.wrapperCache else 'nullptr'
         slotCount = INSTANCE_RESERVED_SLOTS + self.descriptor.interface.totalMembersInSlots
         classFlags = "JSCLASS_IS_DOMJSCLASS | "
-        classExtensionAndObjectOps = """\
-JS_NULL_CLASS_EXT,
-JS_NULL_OBJECT_OPS
-"""
+        classExtensionAndObjectOps = fill(
+            """
+            {
+              nullptr, /* outerObject */
+              nullptr, /* innerObject */
+              nullptr, /* iteratorObject */
+              false,   /* isWrappedNative */
+              nullptr, /* weakmapKeyDelegateOp */
+              ${objectMoved} /* objectMovedOp */
+            },
+            JS_NULL_OBJECT_OPS
+            """,
+            objectMoved=objectMovedHook)
         if self.descriptor.isGlobal():
             classFlags += "JSCLASS_DOM_GLOBAL | JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(DOM_GLOBAL_SLOTS) | JSCLASS_IMPLEMENTS_BARRIERS"
             traceHook = "JS_GlobalObjectTraceHook"
             reservedSlots = "JSCLASS_GLOBAL_APPLICATION_SLOTS"
             if not self.descriptor.workers:
-                classExtensionAndObjectOps = """\
-{
-  nsGlobalWindow::OuterObject, /* outerObject */
-  nullptr, /* innerObject */
-  nullptr, /* iteratorObject */
-  false, /* isWrappedNative */
-  nullptr /* weakmapKeyDelegateOp */
-},
-{
-  nullptr, /* lookupGeneric */
-  nullptr, /* lookupProperty */
-  nullptr, /* lookupElement */
-  nullptr, /* defineGeneric */
-  nullptr, /* defineProperty */
-  nullptr, /* defineElement */
-  nullptr, /* getGeneric  */
-  nullptr, /* getProperty */
-  nullptr, /* getElement */
-  nullptr, /* setGeneric */
-  nullptr, /* setProperty */
-  nullptr, /* setElement */
-  nullptr, /* getGenericAttributes */
-  nullptr, /* setGenericAttributes */
-  nullptr, /* deleteGeneric */
-  nullptr, /* watch */
-  nullptr, /* unwatch */
-  nullptr, /* slice */
-  nullptr, /* enumerate */
-  JS_ObjectToOuterObject /* thisObject */
-}
-"""
+                classExtensionAndObjectOps = fill(
+                    """
+                    {
+                      nsGlobalWindow::OuterObject, /* outerObject */
+                      nullptr, /* innerObject */
+                      nullptr, /* iteratorObject */
+                      false,   /* isWrappedNative */
+                      nullptr, /* weakmapKeyDelegateOp */
+                      ${objectMoved} /* objectMovedOp */
+                    },
+                    {
+                      nullptr, /* lookupGeneric */
+                      nullptr, /* lookupProperty */
+                      nullptr, /* lookupElement */
+                      nullptr, /* defineGeneric */
+                      nullptr, /* defineProperty */
+                      nullptr, /* defineElement */
+                      nullptr, /* getGeneric  */
+                      nullptr, /* getProperty */
+                      nullptr, /* getElement */
+                      nullptr, /* setGeneric */
+                      nullptr, /* setProperty */
+                      nullptr, /* setElement */
+                      nullptr, /* getGenericAttributes */
+                      nullptr, /* setGenericAttributes */
+                      nullptr, /* deleteGeneric */
+                      nullptr, /* watch */
+                      nullptr, /* unwatch */
+                      nullptr, /* slice */
+                      nullptr, /* enumerate */
+                      JS_ObjectToOuterObject /* thisObject */
+                    }
+                    """,
+                    objectMoved=objectMovedHook)
         else:
             classFlags += "JSCLASS_HAS_RESERVED_SLOTS(%d)" % slotCount
             reservedSlots = slotCount
         if self.descriptor.interface.getExtendedAttribute("NeedNewResolve"):
             newResolveHook = "(JSResolveOp)" + NEWRESOLVE_HOOK_NAME
             classFlags += " | JSCLASS_NEW_RESOLVE"
             enumerateHook = ENUMERATE_HOOK_NAME
         elif self.descriptor.isGlobal():
@@ -476,27 +490,34 @@ class CGDOMProxyJSClass(CGThing):
 
     def define(self):
         flags = ["JSCLASS_IS_DOMJSCLASS"]
         # We don't use an IDL annotation for JSCLASS_EMULATES_UNDEFINED because
         # we don't want people ever adding that to any interface other than
         # HTMLAllCollection.  So just hardcode it here.
         if self.descriptor.interface.identifier.name == "HTMLAllCollection":
             flags.append("JSCLASS_EMULATES_UNDEFINED")
+        objectMovedHook = OBJECT_MOVED_HOOK_NAME if self.descriptor.wrapperCache else 'nullptr'
         return fill(
             """
             static const DOMJSClass Class = {
-              PROXY_CLASS_DEF("${name}",
-                              0, /* extra slots */
-                              ${flags}),
+              PROXY_CLASS_WITH_EXT("${name}",
+                                   0, /* extra slots */
+                                   ${flags},
+                                   PROXY_MAKE_EXT(nullptr, /* outerObject */
+                                                  nullptr, /* innerObject */
+                                                  nullptr, /* iteratorObject */
+                                                  false,   /* isWrappedNative */
+                                                  ${objectMoved})),
               $*{descriptor}
             };
             """,
             name=self.descriptor.interface.identifier.name,
             flags=" | ".join(flags),
+            objectMoved=objectMovedHook,
             descriptor=DOMClass(self.descriptor))
 
 
 def PrototypeIDAndDepth(descriptor):
     prototypeID = "prototypes::id::"
     if descriptor.interface.hasInterfacePrototypeObject():
         prototypeID += descriptor.interface.identifier.name
         if descriptor.workers:
@@ -1544,21 +1565,38 @@ class CGClassFinalizeHook(CGAbstractClas
         args = [Argument('js::FreeOp*', 'fop'), Argument('JSObject*', 'obj')]
         CGAbstractClassHook.__init__(self, descriptor, FINALIZE_HOOK_NAME,
                                      'void', args)
 
     def generate_code(self):
         return finalizeHook(self.descriptor, self.name, self.args[0].name).define()
 
 
+class CGClassObjectMovedHook(CGAbstractClassHook):
+    """
+    A hook for objectMovedOp, used to update the wrapper cache when an object it
+    is holding moves.
+    """
+    def __init__(self, descriptor):
+        args = [Argument('JSObject*', 'obj'), Argument('const JSObject*', 'old')]
+        CGAbstractClassHook.__init__(self, descriptor, OBJECT_MOVED_HOOK_NAME,
+                                     'void', args)
+
+    def generate_code(self):
+        assert self.descriptor.wrapperCache
+        return CGIfWrapper(CGGeneric("UpdateWrapper(self, self, obj, old);\n"),
+                           "self").define()
+
+
 def JSNativeArguments():
     return [Argument('JSContext*', 'cx'),
             Argument('unsigned', 'argc'),
             Argument('JS::Value*', 'vp')]
 
+
 class CGClassConstructor(CGAbstractStaticMethod):
     """
     JS-visible constructor for our objects
     """
     def __init__(self, descriptor, ctor, name=CONSTRUCT_HOOK_NAME):
         CGAbstractStaticMethod.__init__(self, descriptor, name, 'bool',
                                         JSNativeArguments())
         self._ctor = ctor
@@ -10914,16 +10952,19 @@ class CGDescriptor(CGThing):
         if descriptor.concrete and not descriptor.proxy:
             if wantsAddProperty(descriptor):
                 cgThings.append(CGAddPropertyHook(descriptor))
 
             # Always have a finalize hook, regardless of whether the class
             # wants a custom hook.
             cgThings.append(CGClassFinalizeHook(descriptor))
 
+        if descriptor.concrete and descriptor.wrapperCache:
+            cgThings.append(CGClassObjectMovedHook(descriptor))
+
         if len(descriptor.permissions):
             for (k, v) in sorted(descriptor.permissions.items()):
                 perms = CGList((CGGeneric('"%s",' % p) for p in k), joiner="\n")
                 perms.append(CGGeneric("nullptr"))
                 cgThings.append(CGWrapper(CGIndenter(perms),
                                           pre="static const char* const permissions_%i[] = {\n" % v,
                                           post="\n};\n",
                                           defineOnly=True))
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -336,16 +336,17 @@ struct nsIConsoleService::COMTypeInfo<ns
   static const nsIID kIID;
 };
 const nsIID nsIConsoleService::COMTypeInfo<nsConsoleService, void>::kIID = NS_ICONSOLESERVICE_IID;
 
 namespace mozilla {
 namespace dom {
 
 #ifdef MOZ_NUWA_PROCESS
+int32_t ContentParent::sNuwaPid = 0;
 bool ContentParent::sNuwaReady = false;
 #endif
 
 #define NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC "ipc:network:set-offline"
 
 class MemoryReportRequestParent : public PMemoryReportRequestParent
 {
 public:
@@ -581,16 +582,17 @@ ContentParent::RunNuwaProcess()
         new ContentParent(/* aApp = */ nullptr,
                           /* aOpener = */ nullptr,
                           /* aIsForBrowser = */ false,
                           /* aIsForPreallocated = */ true,
                           PROCESS_PRIORITY_BACKGROUND,
                           /* aIsNuwaProcess = */ true);
     nuwaProcess->Init();
 #ifdef MOZ_NUWA_PROCESS
+    sNuwaPid = nuwaProcess->Pid();
     sNuwaReady = false;
 #endif
     return nuwaProcess.forget();
 }
 
 // PreallocateAppProcess is called by the PreallocatedProcessManager.
 // ContentParent then takes this process back within
 // GetNewOrPreallocatedAppProcess.
@@ -1982,16 +1984,17 @@ ContentParent::~ContentParent()
         // that sAppContentParents->Get(mAppManifestURL) != this.
         MOZ_ASSERT(!sAppContentParents ||
                    sAppContentParents->Get(mAppManifestURL) != this);
     }
 
 #ifdef MOZ_NUWA_PROCESS
     if (IsNuwaProcess()) {
         sNuwaReady = false;
+        sNuwaPid = 0;
     }
 #endif
 }
 
 void
 ContentParent::InitInternal(ProcessPriority aInitialPriority,
                             bool aSetupOffMainThreadCompositing,
                             bool aSendRegisteredChrome)
@@ -3673,16 +3676,22 @@ ContentParent::DoSendAsyncMessage(JSCont
     ClonedMessageData data;
     if (!BuildClonedMessageDataForParent(this, aData, data)) {
         return false;
     }
     InfallibleTArray<CpowEntry> cpows;
     if (aCpows && !GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) {
         return false;
     }
+#ifdef MOZ_NUWA_PROCESS
+    if (IsNuwaProcess() && IsNuwaReady()) {
+        // Nuwa won't receive frame messages after it is frozen.
+        return true;
+    }
+#endif
     return SendAsyncMessage(nsString(aMessage), data, cpows, Principal(aPrincipal));
 }
 
 bool
 ContentParent::CheckPermission(const nsAString& aPermission)
 {
     return AssertAppProcessPermission(this, NS_ConvertUTF16toUTF8(aPermission).get());
 }
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -74,16 +74,20 @@ class ContentParent MOZ_FINAL : public P
     typedef mozilla::ipc::GeckoChildProcessHost GeckoChildProcessHost;
     typedef mozilla::ipc::OptionalURIParams OptionalURIParams;
     typedef mozilla::ipc::TestShellParent TestShellParent;
     typedef mozilla::ipc::URIParams URIParams;
     typedef mozilla::dom::ClonedMessageData ClonedMessageData;
 
 public:
 #ifdef MOZ_NUWA_PROCESS
+    static int32_t NuwaPid() {
+        return sNuwaPid;
+    }
+
     static bool IsNuwaReady() {
         return sNuwaReady;
     }
 #endif
     virtual bool IsContentParent() MOZ_OVERRIDE { return true; }
     /**
      * Start up the content-process machinery.  This might include
      * scheduling pre-launch tasks.
@@ -707,16 +711,17 @@ private:
 
 #ifdef MOZ_X11
     // Dup of child's X socket, used to scope its resources to this
     // object instead of the child process's lifetime.
     ScopedClose mChildXSocketFdDup;
 #endif
 
 #ifdef MOZ_NUWA_PROCESS
+    static int32_t sNuwaPid;
     static bool sNuwaReady;
 #endif
 };
 
 } // namespace dom
 } // namespace mozilla
 
 class ParentIdleListener : public nsIObserver {
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -1893,18 +1893,19 @@ TabChild::RecvHandleDoubleTap(const CSSP
     DispatchMessageManagerMessage(NS_LITERAL_STRING("Gesture:DoubleTap"), data);
 
     return true;
 }
 
 bool
 TabChild::RecvHandleSingleTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid)
 {
-  TABC_LOG("Handling single tap at %s with %p %p %d\n",
-    Stringify(aPoint).c_str(), mGlobal.get(), mTabChildGlobal.get(), mTouchEndCancelled);
+  TABC_LOG("Handling single tap at %s on %s with %p %p %d\n",
+    Stringify(aPoint).c_str(), Stringify(aGuid).c_str(), mGlobal.get(),
+    mTabChildGlobal.get(), mTouchEndCancelled);
 
   if (!mGlobal || !mTabChildGlobal) {
     return true;
   }
 
   if (mTouchEndCancelled) {
     return true;
   }
--- a/dom/media/tests/mochitest/test_peerConnection_offerRequiresReceiveVideo.html
+++ b/dom/media/tests/mochitest/test_peerConnection_offerRequiresReceiveVideo.html
@@ -15,15 +15,15 @@
     bug: "850275",
     title: "Simple offer media constraint test with video"
   });
 
   runNetworkTest(function() {
     var test = new PeerConnectionTest();
     // TODO: Stop using old constraint-like RTCOptions soon (Bug 1064223).
     // Watch out for case-difference when fixing: { offerToReceiveVideo: true }
-    test.setOfferOptions({ optional: [{ OfferToReceiveAudio: true }] });
+    test.setOfferOptions({ optional: [{ OfferToReceiveVideo: true }] });
     test.run();
   });
 </script>
 </pre>
 </body>
 </html>
--- a/dom/storage/DOMStorageIPC.cpp
+++ b/dom/storage/DOMStorageIPC.cpp
@@ -584,18 +584,25 @@ DOMStorageDBParent::RecvAsyncFlush()
 
 // DOMStorageObserverSink
 
 nsresult
 DOMStorageDBParent::Observe(const char* aTopic,
                             const nsACString& aScopePrefix)
 {
   if (mIPCOpen) {
-    mozilla::unused << SendObserve(nsDependentCString(aTopic),
-                                   nsCString(aScopePrefix));
+#ifdef MOZ_NUWA_PROCESS
+    if (!(static_cast<ContentParent*>(Manager())->IsNuwaProcess() &&
+          ContentParent::IsNuwaReady())) {
+#endif
+      mozilla::unused << SendObserve(nsDependentCString(aTopic),
+                                     nsCString(aScopePrefix));
+#ifdef MOZ_NUWA_PROCESS
+    }
+#endif
   }
 
   return NS_OK;
 }
 
 namespace { // anon
 
 // Results must be sent back on the main thread
--- a/gfx/layers/LayersLogging.cpp
+++ b/gfx/layers/LayersLogging.cpp
@@ -148,16 +148,25 @@ AppendToString(std::stringstream& aStrea
     AppendToString(aStream, m.GetScrollParentId(), " p=");
     aStream << nsPrintfCString(" i=(%ld %lld) }",
             m.GetPresShellId(), m.GetScrollId()).get();
   }
   aStream << sfx;
 }
 
 void
+AppendToString(std::stringstream& aStream, const ScrollableLayerGuid& s,
+               const char* pfx, const char* sfx)
+{
+  aStream << pfx
+          << nsPrintfCString("{ l=%llu, p=%u, v=%llu }", s.mLayersId, s.mPresShellId, s.mScrollId).get()
+          << sfx;
+}
+
+void
 AppendToString(std::stringstream& aStream, const Matrix4x4& m,
                const char* pfx, const char* sfx)
 {
   aStream << pfx;
   if (m.Is2D()) {
     Matrix matrix = m.As2D();
     if (matrix.IsIdentity()) {
       aStream << "[ I ]";
--- a/gfx/layers/LayersLogging.h
+++ b/gfx/layers/LayersLogging.h
@@ -92,16 +92,20 @@ AppendToString(std::stringstream& aStrea
 void
 AppendToString(std::stringstream& aStream, const nsIntSize& sz,
                const char* pfx="", const char* sfx="");
 
 void
 AppendToString(std::stringstream& aStream, const FrameMetrics& m,
                const char* pfx="", const char* sfx="", bool detailed = false);
 
+void
+AppendToString(std::stringstream& aStream, const ScrollableLayerGuid& s,
+               const char* pfx="", const char* sfx="");
+
 template<class T>
 void
 AppendToString(std::stringstream& aStream, const mozilla::gfx::MarginTyped<T>& m,
                const char* pfx="", const char* sfx="")
 {
   aStream << pfx;
   aStream << nsPrintfCString(
     "(l=%f, t=%f, r=%f, b=%f)",
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -2745,17 +2745,17 @@ void AsyncPanZoomController::NotifyLayer
 
   if (scrollOffsetUpdated) {
     // Once layout issues a scroll offset update, it becomes impervious to
     // scroll offset updates from APZ until we acknowledge the update it sent.
     // This prevents APZ updates from clobbering scroll updates from other
     // more "legitimate" sources like content scripts.
     nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
     if (controller) {
-      APZC_LOG("%p sending scroll update acknowledgement with gen %lu\n", this, aLayerMetrics.GetScrollGeneration());
+      APZC_LOG("%p sending scroll update acknowledgement with gen %u\n", this, aLayerMetrics.GetScrollGeneration());
       controller->AcknowledgeScrollUpdate(aLayerMetrics.GetScrollId(),
                                           aLayerMetrics.GetScrollGeneration());
     }
   }
 
   if (needContentRepaint) {
     RequestContentRepaint();
   }
--- a/gfx/layers/apz/util/APZCCallbackHelper.cpp
+++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp
@@ -287,16 +287,25 @@ public:
     NS_IMETHOD Run() {
         MOZ_ASSERT(NS_IsMainThread());
 
         nsIScrollableFrame* sf = nsLayoutUtils::FindScrollableFrameFor(mScrollId);
         if (sf) {
             sf->ResetScrollInfoIfGeneration(mScrollGeneration);
         }
 
+        // Since the APZ and content are in sync, we need to clear any callback transform
+        // that might have been set on the last repaint request (which might have failed
+        // due to the inflight scroll update that this message is acknowledging).
+        nsCOMPtr<nsIContent> content = nsLayoutUtils::FindContentFor(mScrollId);
+        if (content) {
+            content->SetProperty(nsGkAtoms::apzCallbackTransform, new CSSPoint(),
+                                 nsINode::DeleteProperty<CSSPoint>);
+        }
+
         return NS_OK;
     }
 
 protected:
     ViewID mScrollId;
     uint32_t mScrollGeneration;
 };
 
--- a/gfx/skia/trunk/include/config/SkUserConfig.h
+++ b/gfx/skia/trunk/include/config/SkUserConfig.h
@@ -200,14 +200,13 @@
 #define SK_A32_SHIFT 24
 #define SK_R32_SHIFT 16
 #define SK_G32_SHIFT 8
 #define SK_B32_SHIFT 0
 
 #define SK_ALLOW_STATIC_GLOBAL_INITIALIZERS 0
 
 #define SK_SUPPORT_LEGACY_GETDEVICE
-#define SK_SUPPORT_LEGACY_GETTOPDEVICE
 #define SK_IGNORE_ETC1_SUPPORT
 
 #define SK_RASTERIZE_EVEN_ROUNDING
 
 #endif
--- a/image/src/imgLoader.cpp
+++ b/image/src/imgLoader.cpp
@@ -1101,20 +1101,19 @@ void imgLoader::GlobalInit()
   if (NS_SUCCEEDED(rv))
     sCacheTimeWeight = timeweight / 1000.0;
   else
     sCacheTimeWeight = 0.5;
 
   int32_t cachesize;
   rv = Preferences::GetInt("image.cache.size", &cachesize);
   if (NS_SUCCEEDED(rv))
-    sCacheMaxSize = cachesize;
+    sCacheMaxSize = cachesize > 0 ? cachesize : 0;
   else
     sCacheMaxSize = 5 * 1024 * 1024;
-  sCacheMaxSize = sCacheMaxSize > 0 ? sCacheMaxSize : 0;
 
   sMemReporter = new imgMemoryReporter();
   RegisterStrongMemoryReporter(sMemReporter);
   RegisterImagesContentUsedUncompressedDistinguishedAmount(imgMemoryReporter::ImagesContentUsedUncompressedDistinguishedAmount);
 }
 
 nsresult imgLoader::InitCache()
 {
--- a/ipc/glue/FileDescriptor.cpp
+++ b/ipc/glue/FileDescriptor.cpp
@@ -37,17 +37,17 @@ FileDescriptor::FileDescriptor(PlatformH
   mHandleCreatedByOtherProcessWasUsed(false)
 {
   DuplicateInCurrentProcess(aHandle);
 }
 
 void
 FileDescriptor::DuplicateInCurrentProcess(PlatformHandleType aHandle)
 {
-  MOZ_ASSERT_IF(mHandleCreatedByOtherProcess,
+  MOZ_ASSERT_IF(mHandleCreatedByOtherProcess && IsValid(),
                 mHandleCreatedByOtherProcessWasUsed);
 
   if (IsValid(aHandle)) {
     PlatformHandleType newHandle;
 #ifdef XP_WIN
     if (DuplicateHandle(GetCurrentProcess(), aHandle, GetCurrentProcess(),
                         &newHandle, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
 #else // XP_WIN
@@ -60,17 +60,17 @@ FileDescriptor::DuplicateInCurrentProces
   }
 
   mHandle = INVALID_HANDLE;
 }
 
 void
 FileDescriptor::CloseCurrentProcessHandle()
 {
-  MOZ_ASSERT_IF(mHandleCreatedByOtherProcess,
+  MOZ_ASSERT_IF(mHandleCreatedByOtherProcess && IsValid(),
                 mHandleCreatedByOtherProcessWasUsed);
 
   // Don't actually close handles created by another process.
   if (mHandleCreatedByOtherProcess) {
     return;
   }
 
   if (IsValid()) {
--- a/ipc/glue/MessageLink.cpp
+++ b/ipc/glue/MessageLink.cpp
@@ -8,16 +8,17 @@
 #include "mozilla/ipc/MessageLink.h"
 #include "mozilla/ipc/MessageChannel.h"
 #include "mozilla/ipc/BrowserProcessSubThread.h"
 #include "mozilla/ipc/ProtocolUtils.h"
 
 #ifdef MOZ_NUWA_PROCESS
 #include "ipc/Nuwa.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/dom/ContentParent.h"
 #endif
 
 #include "mozilla/Assertions.h"
 #include "mozilla/DebugOnly.h"
 #include "nsDebug.h"
 #include "nsISupportsImpl.h"
 #include "nsXULAppAPI.h"
 
@@ -65,16 +66,19 @@ MessageLink::~MessageLink()
 #endif
 }
 
 ProcessLink::ProcessLink(MessageChannel *aChan)
   : MessageLink(aChan)
   , mTransport(nullptr)
   , mIOLoop(nullptr)
   , mExistingListener(nullptr)
+#ifdef MOZ_NUWA_PROCESS
+  , mIsToNuwaProcess(false)
+#endif
 {
 }
 
 ProcessLink::~ProcessLink()
 {
 #ifdef DEBUG
     mTransport = nullptr;
     mIOLoop = nullptr;
@@ -163,16 +167,30 @@ ProcessLink::EchoMessage(Message *msg)
 }
 
 void
 ProcessLink::SendMessage(Message *msg)
 {
     mChan->AssertWorkerThread();
     mChan->mMonitor->AssertCurrentThreadOwns();
 
+#ifdef MOZ_NUWA_PROCESS
+    if (mIsToNuwaProcess && mozilla::dom::ContentParent::IsNuwaReady()) {
+        switch (msg->type()) {
+        case mozilla::dom::PContent::Msg_NuwaFork__ID:
+        case mozilla::dom::PContent::Reply_AddNewProcess__ID:
+        case mozilla::dom::PContent::Msg_NotifyPhoneStateChange__ID:
+        case GOODBYE_MESSAGE_TYPE:
+            break;
+        default:
+            MOZ_CRASH();
+        }
+    }
+#endif
+
     mIOLoop->PostTask(
         FROM_HERE,
         NewRunnableMethod(mTransport, &Transport::Send, msg));
 }
 
 void
 ProcessLink::SendClose()
 {
@@ -355,16 +373,20 @@ ProcessLink::OnChannelConnected(int32_t 
           mChan->mMonitor->Notify();
           notifyChannel = true;
         }
     }
 
     if (mExistingListener)
         mExistingListener->OnChannelConnected(peer_pid);
 
+#ifdef MOZ_NUWA_PROCESS
+    mIsToNuwaProcess = (peer_pid == mozilla::dom::ContentParent::NuwaPid());
+#endif
+
     if (notifyChannel) {
       mChan->OnChannelConnected(peer_pid);
     }
 }
 
 void
 ProcessLink::OnChannelError()
 {
--- a/ipc/glue/MessageLink.h
+++ b/ipc/glue/MessageLink.h
@@ -165,16 +165,19 @@ class ProcessLink
 
     virtual bool Unsound_IsClosed() const MOZ_OVERRIDE;
     virtual uint32_t Unsound_NumQueuedMessages() const MOZ_OVERRIDE;
 
   protected:
     Transport* mTransport;
     MessageLoop* mIOLoop;       // thread where IO happens
     Transport::Listener* mExistingListener; // channel's previous listener
+#ifdef MOZ_NUWA_PROCESS
+    bool mIsToNuwaProcess;
+#endif
 };
 
 class ThreadLink : public MessageLink
 {
   public:
     ThreadLink(MessageChannel *aChan, MessageChannel *aTargetChan);
     virtual ~ThreadLink();
 
--- a/js/public/GCAPI.h
+++ b/js/public/GCAPI.h
@@ -388,34 +388,70 @@ class JS_PUBLIC_API(AutoAssertOnGC)
     explicit AutoAssertOnGC(JSRuntime *rt) {}
     ~AutoAssertOnGC() {}
 
     static void VerifyIsSafeToGC(JSRuntime *rt) {}
 #endif
 };
 
 /*
- * Disable the static rooting hazard analysis in the live region, but assert if
- * any GC occurs while this guard object is live. This is most useful to help
- * the exact rooting hazard analysis in complex regions, since it cannot
- * understand dataflow.
+ * Assert if an allocation of a GC thing occurs while this class is live. This
+ * class does not disable the static rooting hazard analysis.
+ */
+class JS_PUBLIC_API(AutoAssertNoAlloc)
+{
+#ifdef JS_DEBUG
+    js::gc::GCRuntime *gc;
+
+  public:
+    AutoAssertNoAlloc() : gc(nullptr) {}
+    explicit AutoAssertNoAlloc(JSRuntime *rt);
+    void disallowAlloc(JSRuntime *rt);
+    ~AutoAssertNoAlloc();
+#else
+  public:
+    AutoAssertNoAlloc() {}
+    explicit AutoAssertNoAlloc(JSRuntime *rt) {}
+    void disallowAlloc(JSRuntime *rt) {}
+#endif
+};
+
+/*
+ * Disable the static rooting hazard analysis in the live region and assert if
+ * any allocation that could potentially trigger a GC occurs while this guard
+ * object is live. This is most useful to help the exact rooting hazard analysis
+ * in complex regions, since it cannot understand dataflow.
  *
  * Note: GC behavior is unpredictable even when deterministice and is generally
  *       non-deterministic in practice. The fact that this guard has not
  *       asserted is not a guarantee that a GC cannot happen in the guarded
  *       region. As a rule, anyone performing a GC unsafe action should
  *       understand the GC properties of all code in that region and ensure
  *       that the hazard analysis is correct for that code, rather than relying
  *       on this class.
  */
-class JS_PUBLIC_API(AutoSuppressGCAnalysis) : public AutoAssertOnGC
+class JS_PUBLIC_API(AutoSuppressGCAnalysis) : public AutoAssertNoAlloc
 {
   public:
-    AutoSuppressGCAnalysis() : AutoAssertOnGC() {}
-    explicit AutoSuppressGCAnalysis(JSRuntime *rt) : AutoAssertOnGC(rt) {}
+    AutoSuppressGCAnalysis() : AutoAssertNoAlloc() {}
+    explicit AutoSuppressGCAnalysis(JSRuntime *rt) : AutoAssertNoAlloc(rt) {}
+};
+
+/*
+ * Assert that code is only ever called from a GC callback, disable the static
+ * rooting hazard analysis and assert if any allocation that could potentially
+ * trigger a GC occurs while this guard object is live.
+ *
+ * This is useful to make the static analysis ignore code that runs in GC
+ * callbacks.
+ */
+class JS_PUBLIC_API(AutoAssertGCCallback) : public AutoSuppressGCAnalysis
+{
+  public:
+    explicit AutoAssertGCCallback(JSObject *obj);
 };
 
 /*
  * Place AutoCheckCannotGC in scopes that you believe can never GC. These
  * annotations will be verified both dynamically via AutoAssertOnGC, and
  * statically with the rooting hazard analysis (implemented by making the
  * analysis consider AutoCheckCannotGC to be a GC pointer, and therefore
  * complain if it is live across a GC call.) It is useful when dealing with
--- a/js/src/asmjs/AsmJSLink.cpp
+++ b/js/src/asmjs/AsmJSLink.cpp
@@ -355,16 +355,20 @@ ValidateSimdOperation(JSContext *cx, Asm
           case AsmJSSimdOperation_lessThan: native = simd_int32x4_lessThan; break;
           case AsmJSSimdOperation_greaterThan: native = simd_int32x4_greaterThan; break;
           case AsmJSSimdOperation_equal: native = simd_int32x4_equal; break;
           case AsmJSSimdOperation_and: native = simd_int32x4_and; break;
           case AsmJSSimdOperation_or: native = simd_int32x4_or; break;
           case AsmJSSimdOperation_xor: native = simd_int32x4_xor; break;
           case AsmJSSimdOperation_select: native = simd_int32x4_select; break;
           case AsmJSSimdOperation_splat: native = simd_int32x4_splat; break;
+          case AsmJSSimdOperation_withX: native = simd_int32x4_withX; break;
+          case AsmJSSimdOperation_withY: native = simd_int32x4_withY; break;
+          case AsmJSSimdOperation_withZ: native = simd_int32x4_withZ; break;
+          case AsmJSSimdOperation_withW: native = simd_int32x4_withW; break;
           case AsmJSSimdOperation_lessThanOrEqual:
           case AsmJSSimdOperation_greaterThanOrEqual:
           case AsmJSSimdOperation_notEqual:
           case AsmJSSimdOperation_mul:
           case AsmJSSimdOperation_div:
           case AsmJSSimdOperation_max:
           case AsmJSSimdOperation_min:
             MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("shouldn't have been validated in the first "
@@ -385,16 +389,20 @@ ValidateSimdOperation(JSContext *cx, Asm
           case AsmJSSimdOperation_notEqual: native = simd_float32x4_notEqual ; break;
           case AsmJSSimdOperation_greaterThan: native = simd_float32x4_greaterThan; break;
           case AsmJSSimdOperation_greaterThanOrEqual: native = simd_float32x4_greaterThanOrEqual ; break;
           case AsmJSSimdOperation_and: native = simd_float32x4_and; break;
           case AsmJSSimdOperation_or: native = simd_float32x4_or; break;
           case AsmJSSimdOperation_xor: native = simd_float32x4_xor; break;
           case AsmJSSimdOperation_select: native = simd_float32x4_select; break;
           case AsmJSSimdOperation_splat: native = simd_float32x4_splat; break;
+          case AsmJSSimdOperation_withX: native = simd_float32x4_withX; break;
+          case AsmJSSimdOperation_withY: native = simd_float32x4_withY; break;
+          case AsmJSSimdOperation_withZ: native = simd_float32x4_withZ; break;
+          case AsmJSSimdOperation_withW: native = simd_float32x4_withW; break;
         }
         break;
     }
     if (!native || !IsNativeFunction(v, native))
         return LinkFail(cx, "bad SIMD.type.* operation");
     return true;
 }
 
--- a/js/src/asmjs/AsmJSModule.h
+++ b/js/src/asmjs/AsmJSModule.h
@@ -87,17 +87,21 @@ enum AsmJSSimdOperation
     AsmJSSimdOperation_equal,
     AsmJSSimdOperation_notEqual,
     AsmJSSimdOperation_greaterThan,
     AsmJSSimdOperation_greaterThanOrEqual,
     AsmJSSimdOperation_and,
     AsmJSSimdOperation_or,
     AsmJSSimdOperation_xor,
     AsmJSSimdOperation_select,
-    AsmJSSimdOperation_splat
+    AsmJSSimdOperation_splat,
+    AsmJSSimdOperation_withX,
+    AsmJSSimdOperation_withY,
+    AsmJSSimdOperation_withZ,
+    AsmJSSimdOperation_withW
 };
 
 // These labels describe positions in the prologue/epilogue of functions while
 // compiling an AsmJSModule.
 struct AsmJSFunctionLabels
 {
     AsmJSFunctionLabels(jit::Label &entry, jit::Label &overflowExit)
       : entry(entry), overflowExit(overflowExit) {}
--- a/js/src/asmjs/AsmJSValidate.cpp
+++ b/js/src/asmjs/AsmJSValidate.cpp
@@ -1418,17 +1418,21 @@ class MOZ_STACK_CLASS ModuleCompiler
             !addStandardLibrarySimdOpName("greaterThan", AsmJSSimdOperation_greaterThan) ||
             !addStandardLibrarySimdOpName("greaterThanOrEqual", AsmJSSimdOperation_greaterThanOrEqual) ||
             !addStandardLibrarySimdOpName("and", AsmJSSimdOperation_and) ||
             !addStandardLibrarySimdOpName("or", AsmJSSimdOperation_or) ||
             !addStandardLibrarySimdOpName("xor", AsmJSSimdOperation_xor) ||
             !addStandardLibrarySimdOpName("select", AsmJSSimdOperation_select) ||
             !addStandardLibrarySimdOpName("splat", AsmJSSimdOperation_splat) ||
             !addStandardLibrarySimdOpName("max", AsmJSSimdOperation_max) ||
-            !addStandardLibrarySimdOpName("min", AsmJSSimdOperation_min))
+            !addStandardLibrarySimdOpName("min", AsmJSSimdOperation_min) ||
+            !addStandardLibrarySimdOpName("withX", AsmJSSimdOperation_withX) ||
+            !addStandardLibrarySimdOpName("withY", AsmJSSimdOperation_withY) ||
+            !addStandardLibrarySimdOpName("withZ", AsmJSSimdOperation_withZ) ||
+            !addStandardLibrarySimdOpName("withW", AsmJSSimdOperation_withW))
         {
             return false;
         }
 
         uint32_t srcStart = parser_.pc->maybeFunction->pn_body->pn_pos.begin;
         uint32_t srcBodyStart = tokenStream().currentToken().pos.end;
 
         // "use strict" should be added to the source if we are in an implicit
@@ -2473,16 +2477,28 @@ class FunctionCompiler
             return nullptr;
 
         JS_ASSERT(IsSimdType(lhs->type()) && rhs->type() == lhs->type());
         MSimdBinaryComp *ins = MSimdBinaryComp::NewAsmJS(alloc(), lhs, rhs, op);
         curBlock_->add(ins);
         return ins;
     }
 
+    MDefinition *insertElementSimd(MDefinition *vec, MDefinition *val, SimdLane lane, MIRType type)
+    {
+        if (inDeadCode())
+            return nullptr;
+
+        MOZ_ASSERT(IsSimdType(vec->type()) && vec->type() == type);
+        MOZ_ASSERT(!IsSimdType(val->type()));
+        MSimdInsertElement *ins = MSimdInsertElement::NewAsmJS(alloc(), vec, val, type, lane);
+        curBlock_->add(ins);
+        return ins;
+    }
+
     MDefinition *ternarySimd(MDefinition *mask, MDefinition *lhs, MDefinition *rhs,
                              MSimdTernaryBitwise::Operation op, MIRType type)
     {
         if (inDeadCode())
             return nullptr;
 
         MOZ_ASSERT(IsSimdType(mask->type()));
         MOZ_ASSERT(mask->type() == MIRType_Int32x4);
@@ -3562,16 +3578,20 @@ IsSimdValidOperationType(AsmJSSimdType t
       case AsmJSSimdOperation_lessThan:
       case AsmJSSimdOperation_equal:
       case AsmJSSimdOperation_greaterThan:
       case AsmJSSimdOperation_and:
       case AsmJSSimdOperation_or:
       case AsmJSSimdOperation_xor:
       case AsmJSSimdOperation_select:
       case AsmJSSimdOperation_splat:
+      case AsmJSSimdOperation_withX:
+      case AsmJSSimdOperation_withY:
+      case AsmJSSimdOperation_withZ:
+      case AsmJSSimdOperation_withW:
         return true;
       case AsmJSSimdOperation_mul:
       case AsmJSSimdOperation_div:
       case AsmJSSimdOperation_max:
       case AsmJSSimdOperation_min:
       case AsmJSSimdOperation_lessThanOrEqual:
       case AsmJSSimdOperation_notEqual:
       case AsmJSSimdOperation_greaterThanOrEqual:
@@ -4348,40 +4368,45 @@ CheckMathMinMax(FunctionCompiler &f, Par
         return f.fail(callNode, "Math.min/max must be passed at least 2 arguments");
 
     ParseNode *firstArg = CallArgList(callNode);
     MDefinition *firstDef;
     Type firstType;
     if (!CheckExpr(f, firstArg, &firstDef, &firstType))
         return false;
 
-    bool opIsDouble = firstType.isMaybeDouble();
-    bool opIsInteger = firstType.isInt();
+    if (firstType.isMaybeDouble()) {
+        *type = MathRetType::Double;
+        firstType = Type::MaybeDouble;
+    } else if (firstType.isMaybeFloat()) {
+        *type = MathRetType::Float;
+        firstType = Type::MaybeFloat;
+    } else if (firstType.isInt()) {
+        *type = MathRetType::Signed;
+        firstType = Type::Int;
+    } else {
+        return f.failf(firstArg, "%s is not a subtype of double?, float? or int",
+                       firstType.toChars());
+    }
+
     MIRType opType = firstType.toMIRType();
-
-    if (!opIsDouble && !opIsInteger)
-        return f.failf(firstArg, "%s is not a subtype of double? or int", firstType.toChars());
-
     MDefinition *lastDef = firstDef;
     ParseNode *nextArg = NextNode(firstArg);
     for (unsigned i = 1; i < CallArgListLength(callNode); i++, nextArg = NextNode(nextArg)) {
         MDefinition *nextDef;
         Type nextType;
         if (!CheckExpr(f, nextArg, &nextDef, &nextType))
             return false;
 
-        if (opIsDouble && !nextType.isMaybeDouble())
-            return f.failf(nextArg, "%s is not a subtype of double?", nextType.toChars());
-        if (opIsInteger && !nextType.isInt())
-            return f.failf(nextArg, "%s is not a subtype of int", nextType.toChars());
+        if (!(nextType <= firstType))
+            return f.failf(nextArg, "%s is not a subtype of %s", nextType.toChars(), firstType.toChars());
 
         lastDef = f.minMax(lastDef, nextDef, opType, isMax);
     }
 
-    *type = MathRetType(opIsDouble ? MathRetType::Double : MathRetType::Signed);
     *def = lastDef;
     return true;
 }
 
 typedef bool (*CheckArgType)(FunctionCompiler &f, ParseNode *argNode, Type type);
 
 static bool
 CheckCallArgs(FunctionCompiler &f, ParseNode *callNode, CheckArgType checkArg,
@@ -4813,16 +4838,45 @@ class CheckSimdSelectArgs
         if (!(actualType <= formalType_)) {
             return f.failf(arg, "%s is not a subtype of %s", actualType.toChars(),
                            formalType_.toChars());
         }
         return true;
     }
 };
 
+class CheckSimdVectorScalarArgs
+{
+    Type formalType_;
+
+  public:
+    explicit CheckSimdVectorScalarArgs(Type t) : formalType_(t) {}
+
+    bool operator()(FunctionCompiler &f, ParseNode *arg, unsigned argIndex, Type actualType) const
+    {
+        MOZ_ASSERT(argIndex < 2);
+        if (argIndex == 0) {
+            // First argument is the vector
+            if (!(actualType <= formalType_)) {
+                return f.failf(arg, "%s is not a subtype of %s", actualType.toChars(),
+                               formalType_.toChars());
+            }
+            return true;
+        }
+
+        // Second argument is the scalar
+        Type coercedFormalType = formalType_.simdToCoercedScalarType();
+        if (!(actualType <= coercedFormalType)) {
+            return f.failf(arg, "%s is not a subtype of %s", actualType.toChars(),
+                           coercedFormalType.toChars());
+        }
+        return true;
+    }
+};
+
 } // anonymous namespace
 
 template<class OpEnum>
 static inline bool
 CheckSimdBinary(FunctionCompiler &f, ParseNode *call, Type retType, OpEnum op, MDefinition **def,
                 Type *type)
 {
     DefinitionVector argDefs;
@@ -4843,16 +4897,28 @@ CheckSimdBinary<MSimdBinaryComp::Operati
     if (!CheckSimdCallArgs(f, call, 2, CheckArgIsSubtypeOf(retType), &argDefs))
         return false;
     *def = f.binarySimd(argDefs[0], argDefs[1], op);
     *type = Type::Int32x4;
     return true;
 }
 
 static bool
+CheckSimdWith(FunctionCompiler &f, ParseNode *call, Type retType, SimdLane lane, MDefinition **def,
+              Type *type)
+{
+    DefinitionVector defs;
+    if (!CheckSimdCallArgs(f, call, 2, CheckSimdVectorScalarArgs(retType), &defs))
+        return false;
+    *def = f.insertElementSimd(defs[0], defs[1], lane, retType.toMIRType());
+    *type = retType;
+    return true;
+}
+
+static bool
 CheckSimdOperationCall(FunctionCompiler &f, ParseNode *call, const ModuleCompiler::Global *global,
                        MDefinition **def, Type *type)
 {
     MOZ_ASSERT(global->isSimdOperation());
 
     Type retType = global->simdOperationType();
 
     switch (global->simdOperation()) {
@@ -4884,16 +4950,25 @@ CheckSimdOperationCall(FunctionCompiler 
 
       case AsmJSSimdOperation_and:
         return CheckSimdBinary(f, call, retType, MSimdBinaryBitwise::and_, def, type);
       case AsmJSSimdOperation_or:
         return CheckSimdBinary(f, call, retType, MSimdBinaryBitwise::or_, def, type);
       case AsmJSSimdOperation_xor:
         return CheckSimdBinary(f, call, retType, MSimdBinaryBitwise::xor_, def, type);
 
+      case AsmJSSimdOperation_withX:
+        return CheckSimdWith(f, call, retType, SimdLane::LaneX, def, type);
+      case AsmJSSimdOperation_withY:
+        return CheckSimdWith(f, call, retType, SimdLane::LaneY, def, type);
+      case AsmJSSimdOperation_withZ:
+        return CheckSimdWith(f, call, retType, SimdLane::LaneZ, def, type);
+      case AsmJSSimdOperation_withW:
+        return CheckSimdWith(f, call, retType, SimdLane::LaneW, def, type);
+
       case AsmJSSimdOperation_splat: {
         DefinitionVector defs;
         Type formalType = retType.simdToCoercedScalarType();
         if (!CheckSimdCallArgs(f, call, 1, CheckArgIsSubtypeOf(formalType), &defs))
             return false;
         *def = f.splatSimd(defs[0], retType.toMIRType());
         *type = retType;
         return true;
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -217,36 +217,49 @@ GC(JSContext *cx, unsigned argc, jsval *
 
     /*
      * If the first argument is 'compartment', we collect any compartments
      * previously scheduled for GC via schedulegc. If the first argument is an
      * object, we collect the object's compartment (and any other compartments
      * scheduled for GC). Otherwise, we collect all compartments.
      */
     bool compartment = false;
-    if (args.length() == 1) {
+    if (args.length() >= 1) {
         Value arg = args[0];
         if (arg.isString()) {
             if (!JS_StringEqualsAscii(cx, arg.toString(), "compartment", &compartment))
                 return false;
         } else if (arg.isObject()) {
             PrepareZoneForGC(UncheckedUnwrap(&arg.toObject())->zone());
             compartment = true;
         }
     }
 
+    bool shrinking = false;
+    if (args.length() >= 2) {
+        Value arg = args[1];
+        if (arg.isString()) {
+            if (!JS_StringEqualsAscii(cx, arg.toString(), "shrinking", &shrinking))
+                return false;
+        }
+    }
+
 #ifndef JS_MORE_DETERMINISTIC
     size_t preBytes = cx->runtime()->gc.usage.gcBytes();
 #endif
 
     if (compartment)
         PrepareForDebugGC(cx->runtime());
     else
         PrepareForFullGC(cx->runtime());
-    GCForReason(cx->runtime(), gcreason::API);
+
+    if (shrinking)
+        ShrinkingGC(cx->runtime(), gcreason::API);
+    else
+        GCForReason(cx->runtime(), gcreason::API);
 
     char buf[256] = { '\0' };
 #ifndef JS_MORE_DETERMINISTIC
     JS_snprintf(buf, sizeof(buf), "before %lu, after %lu\n",
                 (unsigned long)preBytes, (unsigned long)cx->runtime()->gc.usage.gcBytes());
 #endif
     JSString *str = JS_NewStringCopyZ(cx, buf);
     if (!str)
@@ -1996,20 +2009,22 @@ IsSimdAvailable(JSContext *cx, unsigned 
     bool available = cx->jitSupportsSimd();
 #endif
     args.rval().set(BooleanValue(available));
     return true;
 }
 
 static const JSFunctionSpecWithHelp TestingFunctions[] = {
     JS_FN_HELP("gc", ::GC, 0, 0,
-"gc([obj] | 'compartment')",
+"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."),
+"  GC via schedulegc.\n"
+"  If 'shrinking' is passes as the optional second argument, perform a\n"
+"  shrinking GC rather than a normal GC."),
 
     JS_FN_HELP("minorgc", ::MinorGC, 0, 0,
 "minorgc([aboutToOverflow])",
 "  Run a minor collector on the Nursery. When aboutToOverflow is true, marks\n"
 "  the store buffer as about-to-overflow before collecting."),
 
     JS_FN_HELP("gcparam", GCParameter, 2, 0,
 "gcparam(name [, value])",
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -2885,48 +2885,16 @@ js::ClampToUint8(ThreadSafeContext *, un
     args.rval().setNumber(ClampDoubleToUint8(args[0].toNumber()));
     return true;
 }
 
 JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::ClampToUint8JitInfo, ClampToUint8JitInfo,
                                       js::ClampToUint8);
 
 bool
-js::Memcpy(ThreadSafeContext *, unsigned argc, Value *vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    JS_ASSERT(args.length() == 5);
-    JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>());
-    JS_ASSERT(args[1].isInt32());
-    JS_ASSERT(args[2].isObject() && args[2].toObject().is<TypedObject>());
-    JS_ASSERT(args[3].isInt32());
-    JS_ASSERT(args[4].isInt32());
-
-    TypedObject &targetTypedObj = args[0].toObject().as<TypedObject>();
-    int32_t targetOffset = args[1].toInt32();
-    TypedObject &sourceTypedObj = args[2].toObject().as<TypedObject>();
-    int32_t sourceOffset = args[3].toInt32();
-    int32_t size = args[4].toInt32();
-
-    JS_ASSERT(targetOffset >= 0);
-    JS_ASSERT(sourceOffset >= 0);
-    JS_ASSERT(size >= 0);
-    JS_ASSERT(size + targetOffset <= targetTypedObj.size());
-    JS_ASSERT(size + sourceOffset <= sourceTypedObj.size());
-
-    uint8_t *target = targetTypedObj.typedMem(targetOffset);
-    uint8_t *source = sourceTypedObj.typedMem(sourceOffset);
-    memcpy(target, source, size);
-    args.rval().setUndefined();
-    return true;
-}
-
-JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::MemcpyJitInfo, MemcpyJitInfo, js::Memcpy);
-
-bool
 js::GetTypedObjectModule(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     Rooted<GlobalObject*> global(cx, cx->global());
     JS_ASSERT(global);
     args.rval().setObject(global->getTypedObjectModule());
     return true;
 }
--- a/js/src/builtin/TypedObject.h
+++ b/js/src/builtin/TypedObject.h
@@ -847,30 +847,16 @@ extern const JSJitInfo TypedObjectIsAtta
  * Usage: ClampToUint8(v)
  *
  * Same as the C function ClampDoubleToUint8. `v` must be a number.
  */
 bool ClampToUint8(ThreadSafeContext *cx, unsigned argc, Value *vp);
 extern const JSJitInfo ClampToUint8JitInfo;
 
 /*
- * Usage: Memcpy(targetDatum, targetOffset,
- *               sourceDatum, sourceOffset,
- *               size)
- *
- * Intrinsic function. Copies size bytes from the data for
- * `sourceDatum` at `sourceOffset` into the data for
- * `targetDatum` at `targetOffset`.
- *
- * Both `sourceDatum` and `targetDatum` must be attached.
- */
-bool Memcpy(ThreadSafeContext *cx, unsigned argc, Value *vp);
-extern const JSJitInfo MemcpyJitInfo;
-
-/*
  * Usage: GetTypedObjectModule()
  *
  * Returns the global "typed object" module, which provides access
  * to the various builtin type descriptors. These are currently
  * exported as immutable properties so it is safe for self-hosted code
  * to access them; eventually this should be linked into the module
  * system.
  */
--- a/js/src/builtin/TypedObject.js
+++ b/js/src/builtin/TypedObject.js
@@ -190,30 +190,16 @@ function TypedObjectGetSimd(descr, typed
 
 // Writes `fromValue` into the `typedObj` at offset `offset`, adapting
 // it to `descr` as needed. This is the most general entry point
 // and works for any type.
 function TypedObjectSet(descr, typedObj, offset, fromValue) {
   if (!TypedObjectIsAttached(typedObj))
     ThrowError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED);
 
-  // Fast path: `fromValue` is a typed object with same type
-  // representation as the destination. In that case, we can just do a
-  // memcpy.
-  if (IsObject(fromValue) && ObjectIsTypedObject(fromValue)) {
-    if (!descr.variable && DescrsEquiv(descr, TypedObjectTypeDescr(fromValue))) {
-      if (!TypedObjectIsAttached(fromValue))
-        ThrowError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED);
-
-      var size = DESCR_SIZE(descr);
-      Memcpy(typedObj, offset, fromValue, 0, size);
-      return;
-    }
-  }
-
   switch (DESCR_KIND(descr)) {
   case JS_TYPEREPR_SCALAR_KIND:
     TypedObjectSetScalar(descr, typedObj, offset, fromValue);
     return;
 
   case JS_TYPEREPR_REFERENCE_KIND:
     TypedObjectSetReference(descr, typedObj, offset, fromValue);
     return;
@@ -338,23 +324,43 @@ function TypedObjectSetReference(descr, 
   }
 
   assert(false, "Unhandled scalar type: " + type);
   return undefined;
 }
 
 // Sets `fromValue` to `this` assuming that `this` is a scalar type.
 function TypedObjectSetSimd(descr, typedObj, offset, fromValue) {
-  // It is only permitted to set a float32x4/int32x4 value from another
-  // float32x4/int32x4; in that case, the "fast path" that uses memcopy will
-  // have already matched. So if we get to this point, we're supposed
-  // to "adapt" fromValue, but there are no legal adaptions.
-  ThrowError(JSMSG_CANT_CONVERT_TO,
-             typeof(fromValue),
-             DESCR_STRING_REPR(descr));
+  if (!IsObject(fromValue) || !ObjectIsTypedObject(fromValue))
+    ThrowError(JSMSG_CANT_CONVERT_TO,
+               typeof(fromValue),
+               DESCR_STRING_REPR(descr));
+
+  if (!DescrsEquiv(descr, TypedObjectTypeDescr(fromValue)))
+    ThrowError(JSMSG_CANT_CONVERT_TO,
+               typeof(fromValue),
+               DESCR_STRING_REPR(descr));
+
+  var type = DESCR_TYPE(descr);
+  switch (type) {
+    case JS_SIMDTYPEREPR_FLOAT32:
+      Store_float32(typedObj, offset + 0, Load_float32(fromValue, 0));
+      Store_float32(typedObj, offset + 4, Load_float32(fromValue, 4));
+      Store_float32(typedObj, offset + 8, Load_float32(fromValue, 8));
+      Store_float32(typedObj, offset + 12, Load_float32(fromValue, 12));
+      break;
+    case JS_SIMDTYPEREPR_INT32:
+      Store_int32(typedObj, offset + 0, Load_int32(fromValue, 0));
+      Store_int32(typedObj, offset + 4, Load_int32(fromValue, 4));
+      Store_int32(typedObj, offset + 8, Load_int32(fromValue, 8));
+      Store_int32(typedObj, offset + 12, Load_int32(fromValue, 12));
+      break;
+    default:
+      assert(false, "Unhandled Simd type: " + type);
+  }
 }
 
 ///////////////////////////////////////////////////////////////////////////
 // C++ Wrappers
 //
 // These helpers are invoked by C++ code or used as method bodies.
 
 // Wrapper for use from C++ code.
--- a/js/src/devtools/rootAnalysis/annotations.js
+++ b/js/src/devtools/rootAnalysis/annotations.js
@@ -217,16 +217,17 @@ function isRootedPointerTypeName(name)
         return /\(js::AllowGC\)1u>::RootType/.test(name);
 
     return name.startsWith('Rooted') || name.startsWith('PersistentRooted');
 }
 
 function isSuppressConstructor(name)
 {
     return name.indexOf("::AutoSuppressGC") != -1
+        || name.indexOf("::AutoAssertGCCallback") != -1
         || name.indexOf("::AutoEnterAnalysis") != -1
         || name.indexOf("::AutoSuppressGCAnalysis") != -1
         || name.indexOf("::AutoIgnoreRootingHazards") != -1;
 }
 
 // nsISupports subclasses' methods may be scriptable (or overridden
 // via binary XPCOM), and so may GC. But some fields just aren't going
 // to get overridden with something that can GC.
--- a/js/src/gc/GCInternals.h
+++ b/js/src/gc/GCInternals.h
@@ -133,17 +133,17 @@ struct AutoStopVerifyingBarriers
 
 #ifdef JSGC_HASH_TABLE_CHECKS
 void
 CheckHashTablesAfterMovingGC(JSRuntime *rt);
 #endif
 
 #ifdef JSGC_COMPACTING
 struct MovingTracer : JSTracer {
-    MovingTracer(JSRuntime *rt) : JSTracer(rt, Visit, TraceWeakMapValues) {}
+    MovingTracer(JSRuntime *rt) : JSTracer(rt, Visit, TraceWeakMapKeysValues) {}
 
     static void Visit(JSTracer *jstrc, void **thingp, JSGCTraceKind kind);
     static void Sweep(JSTracer *jstrc);
     static bool IsMovingTracer(JSTracer *trc) {
         return trc->callback == Visit;
     }
 };
 #endif
--- a/js/src/gc/Heap.h
+++ b/js/src/gc/Heap.h
@@ -619,16 +619,18 @@ struct ArenaHeader : public JS::shadow::
 
     inline ArenaHeader *getNextDelayedMarking() const;
     inline void setNextDelayedMarking(ArenaHeader *aheader);
     inline void unsetDelayedMarking();
 
     inline ArenaHeader *getNextAllocDuringSweep() const;
     inline void setNextAllocDuringSweep(ArenaHeader *aheader);
     inline void unsetAllocDuringSweep();
+
+    void unmarkAll();
 };
 
 struct Arena
 {
     /*
      * Layout of an arena:
      * An arena is 4K in size and 4K-aligned. It starts with the ArenaHeader
      * descriptor followed by some pad bytes. The remainder of the arena is
--- a/js/src/jit-test/tests/asm.js/testMathLib.js
+++ b/js/src/jit-test/tests/asm.js/testMathLib.js
@@ -61,62 +61,68 @@ var f = asmLink(asmCompile('glob', USE_A
 for (n of [-Math.pow(2,31)-1, -Math.pow(2,31), -Math.pow(2,31)+1, -1, 0, 1, Math.pow(2,31)-2, Math.pow(2,31)-1, Math.pow(2,31)])
     assertEq(f(n), Math.abs(n|0)|0);
 
 var f = asmLink(asmCompile('glob', USE_ASM + 'var clz32=glob.Math.clz32; function f(i) { i=i|0; return clz32(i)|0 } return f'), this);
 for (n of [0, 1, 2, 15, 16, Math.pow(2,31)-1, Math.pow(2,31), Math.pow(2,31)+1, Math.pow(2,32)-1, Math.pow(2,32), Math.pow(2,32)+1])
     assertEq(f(n), Math.clz32(n|0));
 
 var doubleNumbers = [NaN, Infinity, -Infinity, -10000, -3.4, -0, 0, 3.4, 10000];
+var floatNumbers = [];
+for (var x of doubleNumbers) floatNumbers.push(Math.fround(x));
 var intNumbers = [-10000, -3, -1, 0, 3, 10000];
+
 function testBinary(f, g, numbers) {
     for (n of numbers)
         for (o of numbers)
             assertEq(f(n,o), g(n,o));
 }
 
 assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var po=glob.Math.pow; function f(d,e) { d=+d;e=+e; return +po(d,e) } return f'), {Math:{pow:Math.sin}});
 assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var po=glob.Math.pow; function f(d,e) { d=+d;e=+e; return +po(d,e) } return f'), {Math:{pow:null}});
 testBinary(asmLink(asmCompile('glob', USE_ASM + 'var po=glob.Math.pow; function f(d,e) { d=+d;e=+e; return +po(d,e) } return f'), {Math:{pow:Math.pow}}), Math.pow, doubleNumbers);
 
 assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var at=glob.Math.atan2; function f(d,e) { d=+d;e=+e; return +at(d,e) } return f'), {Math:{atan2:Math.sin}});
 assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var at=glob.Math.atan2; function f(d,e) { d=+d;e=+e; return +at(d,e) } return f'), {Math:{atan2:null}});
 testBinary(asmLink(asmCompile('glob', USE_ASM + 'var at=glob.Math.atan2; function f(d,e) { d=+d;e=+e; return +at(d,e) } return f'), {Math:{atan2:Math.atan2}}), Math.atan2, doubleNumbers);
 
 assertAsmTypeFail('glob', USE_ASM + 'var min=glob.Math.min; function f(d) { d=+d; return +min(d) } return f');
-assertAsmTypeFail('glob', USE_ASM + 'var f32=glob.Math.fround; var min=glob.Math.min; function f(d) { d=f32(d); return +min(d, f32(5)) } return f');
 assertAsmTypeFail('glob', 'ffi', 'heap', USE_ASM + 'var i32=new glob.Int32Array(heap); var min=glob.Math.min; function f() { return min(i32[0], 5)|0 } return f');
 assertAsmTypeFail('glob', USE_ASM + 'var min=glob.Math.min; function f(x) { x=x|0; return min(3 + x, 5)|0 } return f');
 assertAsmTypeFail('glob', USE_ASM + 'var min=glob.Math.min; function f(x) { x=x|0; return min(5, 3 + x)|0 } return f');
 
 assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e) { d=+d;e=+e; return +min(d,e) } return f'), {Math:{min:Math.sin}});
 assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e) { d=+d;e=+e; return +min(d,e) } return f'), {Math:{min:null}});
 testBinary(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e) { d=+d;e=+e; return +min(d,e) } return f'), {Math:{min:Math.min}}), Math.min, doubleNumbers);
+testBinary(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; var f32=glob.Math.fround; function f(d,e) { d=f32(d);e=f32(e); return f32(min(d,e)) } return f'), this), Math.min, floatNumbers);
 testBinary(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e) { d=d|0;e=e|0; return min(d,e)|0} return f'), {Math:{min:Math.min}}), Math.min, intNumbers);
 
 assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e) { d=+d;e=+e; return +max(d,e) } return f'), {Math:{max:Math.sin}});
 assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e) { d=+d;e=+e; return +max(d,e) } return f'), {Math:{max:null}});
 testBinary(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e) { d=+d;e=+e; return +max(d,e) } return f'), {Math:{max:Math.max}}), Math.max, doubleNumbers);
+testBinary(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; var f32=glob.Math.fround; function f(d,e) { d=f32(d);e=f32(e); return f32(max(d,e)) } return f'), this), Math.max, floatNumbers);
 testBinary(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e) { d=d|0;e=e|0; return max(d,e)|0} return f'), {Math:{max:Math.max}}), Math.max, intNumbers);
 
 function testTernary(f, g, numbers) {
     for (n of numbers)
         for (o of numbers)
             for (p of numbers)
                 assertEq(f(n,o,p), g(n,o,p));
 }
 
 assertAsmTypeFail('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e,g) { d=+d;e=+e;g=g|0; return +min(d,e,g) } return f');
 assertAsmTypeFail('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e,g) { d=d|0;e=e|0;g=+g; return max(d,e,g)|0 } return f');
 assertAsmTypeFail('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e,g) { d=+d;e=+e;g=+g; return min(d,e,g)|0 } return f');
 testTernary(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e,g) { d=d|0;e=e|0;g=g|0; return +max(d,e,g) } return f'), {Math:{max:Math.max}}), Math.max, intNumbers);
 testTernary(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e,g) { d=d|0;e=e|0;g=g|0; return max(d,e,g)|0 } return f'), {Math:{max:Math.max}}), Math.max, intNumbers);
 testTernary(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e,g) { d=+d;e=+e;g=+g; return +max(d,e,g) } return f'), {Math:{max:Math.max}}), Math.max, doubleNumbers);
+testTernary(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; var _=glob.Math.fround; function f(d,e,g) { d=_(d);e=_(e);g=_(g); return _(max(d,e,g)) } return f'), this), Math.max, floatNumbers);
 testTernary(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e,g) { d=d|0;e=e|0;g=g|0; return min(d,e,g)|0 } return f'), {Math:{min:Math.min}}), Math.min, intNumbers);
 testTernary(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e,g) { d=+d;e=+e;g=+g; return +min(d,e,g) } return f'), {Math:{min:Math.min}}), Math.min, doubleNumbers);
+testTernary(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; var _=glob.Math.fround; function f(d,e,g) { d=_(d);e=_(e);g=_(g); return _(min(d,e,g)) } return f'), this), Math.min, floatNumbers);
 
 // Implicit return coercions of math functions
 assertEq(asmLink(asmCompile('glob', USE_ASM + 'var im=glob.Math.imul; function f(i) { i=i|0; i = im(i,i); return i|0 } return f'), this)(3), 9);
 assertAsmTypeFail('glob', USE_ASM + 'var im=glob.Math.imul; function f(d) { d=+d; d = im(d, d) } return f');
 assertAsmTypeFail('glob', USE_ASM + FROUND + 'var im=glob.Math.imul; function f(d) { d=fround(d); d = im(d, d) } return f');
 
 assertEq(asmLink(asmCompile('glob', USE_ASM + 'var abs=glob.Math.abs; function f(d) { d=d|0; d = abs(d|0); return +(d>>>0) } return f'), this)(-1), 1);
 assertEq(asmLink(asmCompile('glob', USE_ASM + 'var abs=glob.Math.abs; function f(d) { d=d|0; var m = 0; m = (-1)>>>0; return (abs(d|0) < (m>>>0))|0 } return f'), this)(42), 1);
@@ -129,17 +135,17 @@ assertEq(asmLink(asmCompile('glob', USE_
 assertEq(asmLink(asmCompile('glob', USE_ASM + FROUND + 'var sqrt=glob.Math.sqrt; function f(d) { d=fround(d); d = fround(sqrt(d)); return +d } return f'), this)(13.37), Math.fround(Math.sqrt(Math.fround(13.37))));
 assertAsmTypeFail('glob', USE_ASM + FROUND + 'var sqrt=glob.Math.sqrt; function f(d) { d=fround(d); d = sqrt(d); return fround(d) } return f');
 assertAsmTypeFail('glob', USE_ASM + FROUND + 'var sqrt=glob.Math.sqrt; function f(d) { d=fround(d); d = sqrt(d); return d } return f');
 assertAsmTypeFail('glob', USE_ASM + 'var sqrt=glob.Math.sqrt; function f(n) { n=n|0; var d=0.; d = sqrt(n|0) } return f');
 assertAsmTypeFail('glob', USE_ASM + 'var sqrt=glob.Math.sqrt; function f(n) { n=n|0; var d=3.; n = sqrt(d)|0 } return f');
 
 assertEq(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d) { d=+d; d = min(d, 13.); return +d } return f'), this)(12), 12);
 assertEq(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d) { d=d|0; d = min(d, 11); return d|0 } return f'), this)(12), 11);
-assertAsmTypeFail('glob', USE_ASM + FROUND + 'var max=glob.Math.max; function f(d) { d=fround(d); d = max(d) } return f');
+assertEq(asmLink(asmCompile('glob', USE_ASM + FROUND + 'var min=glob.Math.min; function f(d) { d=fround(d); d = min(d, fround(13.37)); return fround(d) } return f'), this)(14), Math.fround(13.37));
 
 assertEq(asmLink(asmCompile('glob', USE_ASM + 'var sin=glob.Math.sin; function f(d) { d=+d; d = sin(d); return +d } return f'), this)(Math.PI), Math.sin(Math.PI));
 assertAsmTypeFail('glob', USE_ASM + FROUND + 'var sin=glob.Math.sin; function f(d) { d=fround(d); d = sin(d) } return f');
 assertAsmTypeFail('glob', USE_ASM + 'var sin=glob.Math.sin; function f(d) { d=d|0; d = sin(d) } return f');
 
 assertEq(asmLink(asmCompile('glob', USE_ASM + 'var pow=glob.Math.pow; function f(d) { d=+d; d = pow(d,d); return +d } return f'), this)(3), 27);
 assertAsmTypeFail('glob', USE_ASM + FROUND + 'var pow=glob.Math.pow; function f(d) { d=fround(d); d = pow(d, d) } return f');
 assertAsmTypeFail('glob', USE_ASM + 'var pow=glob.Math.pow; function f(d) { d=d|0; d = pow(d, d) } return f');
@@ -148,19 +154,23 @@ assertAsmTypeFail('glob', USE_ASM + 'var
 assertAsmTypeFail('glob', USE_ASM + 'var pow=glob.Math.pow; function f(d) { d=+d; var i=0; i = pow(d,d)|0; } return f');
 assertAsmTypeFail('glob', USE_ASM + 'var atan2=glob.Math.atan2; function f(d) { d=+d; var i=0; i = atan2(d,d)|0; } return f');
 assertAsmTypeFail('glob', USE_ASM + 'var sqrt=glob.Math.sqrt; function f(d) { d=+d; sqrt(d)|0; } return f');
 assertAsmTypeFail('glob', USE_ASM + 'var abs=glob.Math.abs; function f(d) { d=+d; abs(d)|0; } return f');
 
 assertEq(asmLink(asmCompile('glob', USE_ASM + 'var im=glob.Math.imul; function f(i) { i=i|0; var d=0.0; d = +im(i,i); return +d } return f'), this)(42), Math.imul(42, 42));
 assertEq(asmLink(asmCompile('glob', USE_ASM + 'var abs=glob.Math.abs; function f(i) { i=i|0; var d=0.0; d = +abs(i|0); return +d } return f'), this)(-42), 42);
 assertEq(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(i) { i=i|0; var d=0.0; d = +min(i, 0); return +d } return f'), this)(-42), -42);
+assertEq(asmLink(asmCompile('glob', USE_ASM + FROUND + 'var min=glob.Math.min; function f(i) { i=i|0; var d=fround(0); d = fround(min(i, 0)); return +d } return f'), this)(-42), -42);
 assertEq(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(i) { i=i|0; var d=0.0; d = +max(i, 0); return +d } return f'), this)(-42), 0);
+assertEq(asmLink(asmCompile('glob', USE_ASM + FROUND + 'var max=glob.Math.max; function f(i) { i=i|0; var d=fround(0); d = fround(max(i, 0)); return +d } return f'), this)(-42), 0);
 assertEq(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d) { d=+d; var i=0; i = ~~min(d, 0.)|0; return i|0 } return f'), this)(-42), -42);
+assertEq(asmLink(asmCompile('glob', USE_ASM + FROUND + 'var min=glob.Math.min; function f(d) { d=fround(d); var i=0; i = ~~min(d, fround(0))|0; return i|0 } return f'), this)(-42), -42);
 assertEq(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d) { d=+d; var i=0; i = ~~max(d, 0.)|0; return i|0 } return f'), this)(-42), 0);
+assertEq(asmLink(asmCompile('glob', USE_ASM + FROUND + 'var max=glob.Math.max; function f(d) { d=fround(d); var i=0; i = ~~max(d, fround(0))|0; return i|0 } return f'), this)(-42), 0);
 
 assertEq(asmLink(asmCompile('glob', USE_ASM + 'var abs=glob.Math.abs; function f(i) { i=i|0; var d=0.0; return +d; +abs(i|0); return 3.0;} return f'), this)(-42), 0);
 
 assertAsmTypeFail('glob', USE_ASM + 'var tau=glob.Math.TAU; function f() {} return f');
 assertAsmTypeFail('glob', USE_ASM + 'var pi=glob.Math.PI; function f() { return pi | 0 } return f');
 assertAsmTypeFail('glob', USE_ASM + 'var pi=glob.Math.PI; function f() { return +pi() } return f');
 assertAsmTypeFail('glob', USE_ASM + 'var pi=glob.Math.PI; function f() { pi = +3; } return f');
 assertAsmLinkAlwaysFail(asmCompile('glob', USE_ASM + 'var pi=glob.Math.PI; function f() {} return f'), {});
--- a/js/src/jit-test/tests/asm.js/testSIMD.js
+++ b/js/src/jit-test/tests/asm.js/testSIMD.js
@@ -472,16 +472,45 @@ CheckF4(F32D, 'var x=f4(1,2,3,4); x=f4d(
 CheckF4(F32D, 'var x=f4(1,2,3,4); var y=f4(4,3,5,2); x=f4d(x,y)', [1/4,2/3,3/5,2]);
 CheckF4(F32D, 'var x=f4(13.37,1,1,4); var y=f4(4,0,-0.,2); x=f4d(x,y)', [Math.fround(13.37) / 4,+Infinity,-Infinity,2]);
 
 // Test NaN
 var f32x4 = SIMD.float32x4(0, 0, -0, NaN);
 var another = SIMD.float32x4(0, -0, 0, 0);
 assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + F32D + "function f(x,y) {x=f4(x); y=f4(y); x=f4d(x,y); return f4(x);} return f"), this)(f32x4, another), [NaN, NaN, NaN, NaN]);
 
+// With
+const WXF = 'var w = f4.withX;';
+const WYF = 'var w = f4.withY;';
+const WZF = 'var w = f4.withZ;';
+const WWF = 'var w = f4.withW;';
+
+assertAsmTypeFail('glob', USE_ASM + F32 + WXF + "function f() {var x = f4(1,2,3,4); x = w(x, 1);} return f");
+assertAsmTypeFail('glob', USE_ASM + F32 + WXF + "function f() {var x = f4(1,2,3,4); x = w(x, 1.0);} return f");
+assertAsmTypeFail('glob', USE_ASM + F32 + WXF + "function f() {var x = f4(1,2,3,4); x = w(x, x);} return f");
+assertAsmTypeFail('glob', USE_ASM + F32 + WXF + FROUND + "function f() {var x = f4(1,2,3,4); x = w(1, f32(1));} return f");
+assertAsmTypeFail('glob', USE_ASM + F32 + WXF + FROUND + "function f() {var x = f4(1,2,3,4); x = w(1., f32(1));} return f");
+assertAsmTypeFail('glob', USE_ASM + F32 + WXF + FROUND + "function f() {var x = f4(1,2,3,4); x = w(f32(1), f32(1));} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + F32 + WXF + FROUND + "function f() {var x = f4(1,2,3,4); var y = i4(1,2,3,4); x = w(y, f32(1));} return f");
+
+CheckF4(WXF + FROUND, 'var x = f4(1,2,3,4); x = w(x, f32(13.37));', [Math.fround(13.37), 2, 3, 4]);
+CheckF4(WYF + FROUND, 'var x = f4(1,2,3,4); x = w(x, f32(13.37));', [1, Math.fround(13.37), 3, 4]);
+CheckF4(WZF + FROUND, 'var x = f4(1,2,3,4); x = w(x, f32(13.37));', [1, 2, Math.fround(13.37), 4]);
+CheckF4(WWF + FROUND, 'var x = f4(1,2,3,4); x = w(x, f32(13.37));', [1, 2, 3, Math.fround(13.37)]);
+CheckF4(WWF + FROUND, 'var x = f4(1,2,3,4); x = w(x, f32(13.37) + f32(6.63));', [1, 2, 3, Math.fround(Math.fround(13.37) + Math.fround(6.63))]);
+
+const WXI = 'var w = i4.withX;';
+const WYI = 'var w = i4.withY;';
+const WZI = 'var w = i4.withZ;';
+const WWI = 'var w = i4.withW;';
+CheckI4(WXI, 'var x = i4(1,2,3,4); x = w(x, 42);', [42, 2, 3, 4]);
+CheckI4(WYI, 'var x = i4(1,2,3,4); x = w(x, 42);', [1, 42, 3, 4]);
+CheckI4(WZI, 'var x = i4(1,2,3,4); x = w(x, 42);', [1, 2, 42, 4]);
+CheckI4(WWI, 'var x = i4(1,2,3,4); x = w(x, 42);', [1, 2, 3, 42]);
+
 // Comparisons
 // True yields all bits set to 1 (i.e as an int32, 0xFFFFFFFF === -1), false
 // yields all bits set to 0 (i.e 0).
 const T = -1;
 const F = 0;
 assertAsmTypeFail('glob', USE_ASM + I32 + "var lt=i4.lessThanOrEqual; function f() {} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + "var ge=i4.greaterThanOrEqual; function f() {} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + "var ne=i4.notEqual; function f() {} return f");
--- a/js/src/jit-test/tests/ion/dce-with-rinstructions.js
+++ b/js/src/jit-test/tests/ion/dce-with-rinstructions.js
@@ -456,16 +456,24 @@ function rpowhalf_object(i) {
 var uceFault_min_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_min_number'));
 function rmin_number(i) {
     var x = Math.min(i, i-1, i-2.1);
     if (uceFault_min_number(i) || uceFault_min_number(i))
         assertEq(x, i-2.1);
     return i;
 }
 
+var uceFault_min_float = eval(uneval(uceFault).replace('uceFault', 'uceFault_min_float'));
+function rmin_float(i) {
+    var x = Math.fround(Math.min(Math.fround(20), Math.fround(13.37)));
+    if (uceFault_min_number(i) || uceFault_min_number(i))
+        assertEq(x, Math.fround(13.37));
+    return i;
+}
+
 var uceFault_min_object = eval(uneval(uceFault).replace('uceFault', 'uceFault_min_object'));
 function rmin_object(i) {
     var t = i;
     var o = { valueOf: function () { return t; } };
     var x = Math.min(o, o-1, o-2.1)
     t = 1000;
     if (uceFault_min_object(i) || uceFault_min_object(i))
         assertEq(x, i-2.1);
@@ -475,16 +483,24 @@ function rmin_object(i) {
 var uceFault_max_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_max_number'));
 function rmax_number(i) {
     var x = Math.max(i, i-1, i-2.1);
     if (uceFault_max_number(i) || uceFault_max_number(i))
         assertEq(x, i);
     return i;
 }
 
+var uceFault_max_float = eval(uneval(uceFault).replace('uceFault', 'uceFault_max_float'));
+function rmax_float(i) {
+    var x = Math.fround(Math.max(Math.fround(2), Math.fround(13.37)));
+    if (uceFault_max_number(i) || uceFault_max_number(i))
+        assertEq(x, Math.fround(13.37));
+    return i;
+}
+
 var uceFault_max_object = eval(uneval(uceFault).replace('uceFault', 'uceFault_max_object'));
 function rmax_object(i) {
     var t = i;
     var o = { valueOf: function () { return t; } };
     var x = Math.max(o, o-1, o-2.1)
     t = 1000;
     if (uceFault_max_object(i) || uceFault_max_object(i))
         assertEq(x, i);
@@ -970,18 +986,20 @@ for (i = 0; i < 100; i++) {
     rcharCodeAt(i);
     rfrom_char_code(i);
     rfrom_char_code_non_ascii(i);
     rpow_number(i);
     rpow_object(i);
     rpowhalf_number(i);
     rpowhalf_object(i);
     rmin_number(i);
+    rmin_float(i);
     rmin_object(i);
     rmax_number(i);
+    rmax_float(i);
     rmax_object(i);
     rabs_number(i);
     rabs_object(i);
     rsqrt_number(i);
     rsqrt_float(i);
     rsqrt_object(i);
     ratan2_number(i);
     ratan2_object(i);
--- a/js/src/jit-test/tests/ion/testFloat32-correctness.js
+++ b/js/src/jit-test/tests/ion/testFloat32-correctness.js
@@ -84,16 +84,48 @@ function sqrt() {
     for(var i = 0; i < 7; ++i) {
         var sqrtf = Math.fround(Math.sqrt(f32[i]));
         var sqrtd = 1 + Math.sqrt(f32[i]) - 1; // force no float32 by chaining arith ops
         assertEq( sqrtf, Math.fround(sqrtd) );
     }
 }
 test(setupSqrt, sqrt);
 
+// MMinMax
+function setupMinMax() {
+    f32[0] = -0;
+    f32[1] = 0;
+    f32[2] = 1;
+    f32[3] = 4;
+    f32[4] = -1;
+    f32[5] = Infinity;
+    f32[6] = NaN;
+    f32[7] = 13.37;
+    f32[8] = -Infinity;
+    f32[9] = Math.pow(2,31) - 1;
+}
+function minMax() {
+    for(var i = 0; i < 9; ++i) {
+        for(var j = 0; j < 9; j++) {
+            var minf = Math.fround(Math.min(f32[i], f32[j]));
+            var mind = 1 / (1 / Math.min(f32[i], f32[j])); // force no float32 by chaining arith ops
+            assertFloat32(minf, true);
+            assertFloat32(mind, false);
+            assertEq( minf, Math.fround(mind) );
+
+            var maxf = Math.fround(Math.max(f32[i], f32[j]));
+            var maxd = 1 / (1 / Math.max(f32[i], f32[j])); // force no float32 by chaining arith ops
+            assertFloat32(maxf, true);
+            assertFloat32(maxd, false);
+            assertEq( maxf, Math.fround(maxd) );
+        }
+    }
+}
+test(setupMinMax, minMax);
+
 // MTruncateToInt32
 // The only way to get a MTruncateToInt32 with a Float32 input is to use Math.imul
 function setupTruncateToInt32() {
     f32[0] = -1;
     f32[1] = 4;
     f32[2] = 5.13;
 }
 function truncateToInt32() {
--- a/js/src/jit-test/tests/ion/testFloat32.js
+++ b/js/src/jit-test/tests/ion/testFloat32.js
@@ -56,18 +56,18 @@
 setJitCompilerOption("ion.warmup.trigger", 50);
 
 function test(f) {
     f32[0] = .5;
     for(var n = 110; n; n--)
         f();
 }
 
-var f32 = new Float32Array(2);
-var f64 = new Float64Array(2);
+var f32 = new Float32Array(4);
+var f64 = new Float64Array(4);
 
 function acceptAdd() {
     var use = f32[0] + 1;
     assertFloat32(use, true);
     f32[0] = use;
 }
 test(acceptAdd);
 
@@ -146,16 +146,89 @@ test(acceptSqrt);
 
 function refuseSqrt() {
     var res = Math.sqrt(f32[0]);
     assertFloat32(res, false);
     f32[0] = res + 1;
 }
 test(refuseSqrt);
 
+function acceptMin() {
+    var res = Math.min(f32[0], f32[1]);
+    assertFloat32(res, true);
+    f64[0] = res;
+}
+test(acceptMin);
+
+// In theory, we could do it, as Math.min/max actually behave as a Phi (it's a
+// float32 producer iff its inputs are producers, it's a consumer iff its uses
+// are consumers). In practice, this would involve some overhead for big chains
+// of min/max.
+function refuseMinAdd() {
+    var res = Math.min(f32[0], f32[1]) + f32[2];
+    assertFloat32(res, false);
+    f32[3] = res;
+}
+test(refuseMinAdd);
+
+function acceptSeveralMinMax() {
+    var x = Math.min(f32[0], f32[1]);
+    var y = Math.max(f32[2], f32[3]);
+    var res = Math.min(x, y);
+    assertFloat32(res, true);
+    f64[0] = res;
+}
+test(acceptSeveralMinMax);
+
+function acceptSeveralMinMax2() {
+    var res = Math.min(f32[0], f32[1], f32[2], f32[3]);
+    assertFloat32(res, true);
+    f64[0] = res;
+}
+test(acceptSeveralMinMax2);
+
+function partialMinMax() {
+    var x = Math.min(f32[0], f32[1]);
+    var y = Math.min(f64[0], f32[1]);
+    var res  = Math.min(x, y);
+    assertFloat32(x, true);
+    assertFloat32(y, false);
+    assertFloat32(res, false);
+    f64[0] = res;
+}
+test(partialMinMax);
+
+function refuseSeveralMinMax() {
+    var res = Math.min(f32[0], f32[1] + f32[2], f32[2], f32[3]);
+    assertFloat32(res, false);
+    f64[0] = res;
+}
+test(refuseSeveralMinMax);
+
+function refuseMin() {
+    var res = Math.min(f32[0], 42.13 + f32[1]);
+    assertFloat32(res, false);
+    f64[0] = res;
+}
+test(refuseMin);
+
+function acceptMax() {
+    var res = Math.max(f32[0], f32[1]);
+    assertFloat32(res, true);
+    f64[0] = res;
+}
+test(acceptMax);
+
+function refuseMax() {
+    var res = Math.max(f32[0], 42.13 + f32[1]);
+    assertFloat32(res, false);
+    f64[0] = res;
+}
+test(refuseMax);
+
 function acceptAbs() {
     var res = Math.abs(f32[0]);
     assertFloat32(res, true);
     f32[0] = res;
 }
 test(acceptAbs);
 
 function refuseAbs() {
--- a/js/src/jit/JitOptions.cpp
+++ b/js/src/jit/JitOptions.cpp
@@ -1,118 +1,148 @@
 /* -*- 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/JitOptions.h"
+#include "mozilla/TypeTraits.h"
 
+#include <cstdlib>
 #include "jsfun.h"
-
 using namespace js;
 using namespace js::jit;
 
 namespace js {
 namespace jit {
 
 JitOptions js_JitOptions;
 
+template<typename T> struct IsBool : mozilla::FalseType {};
+template<> struct IsBool<bool> : mozilla::TrueType {};
+
+template<typename T>
+T overrideDefault(const char *param, T dflt) {
+    char *str = getenv(param);
+    if (!str)
+        return dflt;
+    if (IsBool<T>::value) {
+        if (strcmp(str, "true") == 0 ||
+            strcmp(str, "yes")) {
+            return true;
+        }
+        if (strcmp(str, "false") == 0 ||
+            strcmp(str, "no")) {
+            return false;
+        }
+        fprintf(stderr, "Warning: I didn't understand %s=\"%s\"", param, str);
+    } else {
+        char *endp;
+        int retval = strtol(str, &endp, 0);
+        if (*endp == '\0')
+            return retval;
+
+        fprintf(stderr, "Warning: I didn't understand %s=\"%s\"", param, str);
+    }
+    return dflt;
+}
+#define SET_DEFAULT(var, dflt) var = overrideDefault("JIT_OPTION_" #var, dflt)
 JitOptions::JitOptions()
 {
     // Whether to perform expensive graph-consistency DEBUG-only assertions.
     // It can be useful to disable this to reduce DEBUG-compile time of large
     // asm.js programs.
-    checkGraphConsistency = true;
+    SET_DEFAULT(checkGraphConsistency, true);
 
 #ifdef CHECK_OSIPOINT_REGISTERS
     // Emit extra code to verify live regs at the start of a VM call
     // are not modified before its OsiPoint.
-    checkOsiPointRegisters = false;
+    SET_DEFAULT(checkOsiPointRegisters, false);
 #endif
 
     // Whether to enable extra code to perform dynamic validation of
     // RangeAnalysis results.
-    checkRangeAnalysis = false;
+    SET_DEFAULT(checkRangeAnalysis, false);
 
     // Whether Ion should compile try-catch statements.
-    compileTryCatch = true;
+    SET_DEFAULT(compileTryCatch, true);
 
     // Toggle whether eager scalar replacement is globally disabled.
-    disableScalarReplacement = true; // experimental
+    SET_DEFAULT(disableScalarReplacement, true); // experimental
 
     // Toggle whether global value numbering is globally disabled.
-    disableGvn = false;
+    SET_DEFAULT(disableGvn, false);
 
     // Toggles whether loop invariant code motion is globally disabled.
-    disableLicm = false;
+    SET_DEFAULT(disableLicm, false);
 
     // Toggles whether inlining is globally disabled.
-    disableInlining = false;
+    SET_DEFAULT(disableInlining, false);
 
     // Toggles whether Edge Case Analysis is gobally disabled.
-    disableEdgeCaseAnalysis = false;
+    SET_DEFAULT(disableEdgeCaseAnalysis, false);
 
     // Toggles whether Range Analysis is globally disabled.
-    disableRangeAnalysis = false;
+    SET_DEFAULT(disableRangeAnalysis, false);
 
     // Toggles whether Loop Unrolling is globally disabled.
-    disableLoopUnrolling = true;
+    SET_DEFAULT(disableLoopUnrolling, true);
 
     // Toggles whether Effective Address Analysis is globally disabled.
-    disableEaa = false;
+    SET_DEFAULT(disableEaa, false);
 
     // Whether functions are compiled immediately.
-    eagerCompilation = false;
+    SET_DEFAULT(eagerCompilation, false);
 
     // Force how many invocation or loop iterations are needed before compiling
     // a function with the highest ionmonkey optimization level.
     // (i.e. OptimizationLevel_Normal)
-    forceDefaultIonWarmUpThreshold = false;
-    forcedDefaultIonWarmUpThreshold = 1000;
+    SET_DEFAULT(forceDefaultIonWarmUpThreshold, false);
+    SET_DEFAULT(forcedDefaultIonWarmUpThreshold, 1000);
 
     // Force the used register allocator instead of letting the
     // optimization pass decide.
     forceRegisterAllocator = false;
     forcedRegisterAllocator = RegisterAllocator_LSRA;
 
     // Toggles whether large scripts are rejected.
-    limitScriptSize = true;
+    SET_DEFAULT(limitScriptSize, true);
 
     // Toggles whether functions may be entered at loop headers.
-    osr = true;
+    SET_DEFAULT(osr, true);
 
     // How many invocations or loop iterations are needed before functions
     // are compiled with the baseline compiler.
-    baselineWarmUpThreshold = 10;
+    SET_DEFAULT(baselineWarmUpThreshold, 10);
 
     // Number of exception bailouts (resuming into catch/finally block) before
     // we invalidate and forbid Ion compilation.
-    exceptionBailoutThreshold = 10;
+    SET_DEFAULT(exceptionBailoutThreshold, 10);
 
     // Number of bailouts without invalidation before we set
     // JSScript::hadFrequentBailouts and invalidate.
-    frequentBailoutThreshold = 10;
+    SET_DEFAULT(frequentBailoutThreshold, 10);
 
     // How many actual arguments are accepted on the C stack.
-    maxStackArgs = 4096;
+    SET_DEFAULT(maxStackArgs, 4096);
 
     // How many times we will try to enter a script via OSR before
     // invalidating the script.
-    osrPcMismatchesBeforeRecompile = 6000;
+    SET_DEFAULT(osrPcMismatchesBeforeRecompile, 6000);
 
     // The bytecode length limit for small function.
     //
     // The default for this was arrived at empirically via benchmarking.
     // We may want to tune it further after other optimizations have gone
     // in.
-    smallFunctionMaxBytecodeLength_ = 100;
+    SET_DEFAULT(smallFunctionMaxBytecodeLength_, 100);
 
     // How many uses of a parallel kernel before we attempt compilation.
-    compilerWarmUpThresholdPar = 1;
+    SET_DEFAULT(compilerWarmUpThresholdPar, 1);
 }
 
 bool
 JitOptions::isSmallFunction(JSScript *script) const
 {
     return script->length() <= smallFunctionMaxBytecodeLength_;
 }
 
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -138,52 +138,90 @@ class LSimdSplatX4 : public LInstruction
         setOperand(0, v);
     }
 
     MSimdSplatX4 *mir() const {
         return mir_->toSimdSplatX4();
     }
 };
 
-// Extracts an element from a given SIMD int32x4 lane.
-class LSimdExtractElementI : public LInstructionHelper<1, 1, 0>
-{
-    SimdLane lane_;
-
-  public:
-    LIR_HEADER(SimdExtractElementI);
-
-    LSimdExtractElementI(const LAllocation &base, SimdLane lane) : lane_(lane) {
+class LSimdExtractElementBase : public LInstructionHelper<1, 1, 0>
+{
+  protected:
+    LSimdExtractElementBase(const LAllocation &base) {
         setOperand(0, base);
     }
+
+  public:
     const LAllocation *getBase() {
         return getOperand(0);
     }
     SimdLane lane() const {
-        return lane_;
-    }
-};
-
+        return mir_->toSimdExtractElement()->lane();
+    }
+};
+
+// Extracts an element from a given SIMD int32x4 lane.
+class LSimdExtractElementI : public LSimdExtractElementBase
+{
+  public:
+    LIR_HEADER(SimdExtractElementI);
+    LSimdExtractElementI(const LAllocation &base)
+      : LSimdExtractElementBase(base)
+    {}
+};
 // Extracts an element from a given SIMD float32x4 lane.
-class LSimdExtractElementF : public LInstructionHelper<1, 1, 0>
-{
-    SimdLane lane_;
-
+class LSimdExtractElementF : public LSimdExtractElementBase
+{
   public:
     LIR_HEADER(SimdExtractElementF);
-
-    LSimdExtractElementF(const LAllocation &base, SimdLane lane) : lane_(lane) {
-        setOperand(0, base);
-    }
-    const LAllocation *getBase() {
+    LSimdExtractElementF(const LAllocation &base)
+      : LSimdExtractElementBase(base)
+    {}
+};
+
+class LSimdInsertElementBase : public LInstructionHelper<1, 2, 0>
+{
+  protected:
+    LSimdInsertElementBase(const LAllocation &vec, const LAllocation &val)
+    {
+        setOperand(0, vec);
+        setOperand(1, val);
+    }
+
+  public:
+    const LAllocation *vector() {
         return getOperand(0);
     }
-    SimdLane lane() const {
-        return lane_;
-    }
+    const LAllocation *value() {
+        return getOperand(1);
+    }
+    const SimdLane lane() const {
+        return mir_->toSimdInsertElement()->lane();
+    }
+};
+
+// Replace an element from a given SIMD int32x4 lane with a given value.
+class LSimdInsertElementI : public LSimdInsertElementBase
+{
+  public:
+    LIR_HEADER(SimdInsertElementI);
+    LSimdInsertElementI(const LAllocation &vec, const LAllocation &val)
+      : LSimdInsertElementBase(vec, val)
+    {}
+};
+
+// Replace an element from a given SIMD float32x4 lane with a given value.
+class LSimdInsertElementF : public LSimdInsertElementBase
+{
+  public:
+    LIR_HEADER(SimdInsertElementF);
+    LSimdInsertElementF(const LAllocation &vec, const LAllocation &val)
+      : LSimdInsertElementBase(vec, val)
+    {}
 };
 
 class LSimdSignMaskX4 : public LInstructionHelper<1, 1, 0>
 {
   public:
     LIR_HEADER(SimdSignMaskX4);
 
     explicit LSimdSignMaskX4(const LAllocation &input) {
@@ -2651,26 +2689,26 @@ class LReturn : public LInstructionHelpe
 class LThrow : public LCallInstructionHelper<0, BOX_PIECES, 0>
 {
   public:
     LIR_HEADER(Throw)
 
     static const size_t Value = 0;
 };
 
-class LMinMaxI : public LInstructionHelper<1, 2, 0>
-{
-  public:
-    LIR_HEADER(MinMaxI)
-    LMinMaxI(const LAllocation &first, const LAllocation &second)
+class LMinMaxBase : public LInstructionHelper<1, 2, 0>
+{
+  protected:
+    LMinMaxBase(const LAllocation &first, const LAllocation &second)
     {
         setOperand(0, first);
         setOperand(1, second);
     }
 
+  public:
     const LAllocation *first() {
         return this->getOperand(0);
     }
     const LAllocation *second() {
         return this->getOperand(1);
     }
     const LDefinition *output() {
         return this->getDef(0);
@@ -2678,41 +2716,38 @@ class LMinMaxI : public LInstructionHelp
     MMinMax *mir() const {
         return mir_->toMinMax();
     }
     const char *extraName() const {
         return mir()->isMax() ? "Max" : "Min";
     }
 };
 
-class LMinMaxD : public LInstructionHelper<1, 2, 0>
+class LMinMaxI : public LMinMaxBase
+{
+  public:
+    LIR_HEADER(MinMaxI)
+    LMinMaxI(const LAllocation &first, const LAllocation &second) : LMinMaxBase(first, second)
+    {}
+};
+
+class LMinMaxD : public LMinMaxBase
 {
   public:
     LIR_HEADER(MinMaxD)
-    LMinMaxD(const LAllocation &first, const LAllocation &second)
-    {
-        setOperand(0, first);
-        setOperand(1, second);
-    }
-
-    const LAllocation *first() {
-        return this->getOperand(0);
-    }
-    const LAllocation *second() {
-        return this->getOperand(1);
-    }
-    const LDefinition *output() {
-        return this->getDef(0);
-    }
-    MMinMax *mir() const {
-        return mir_->toMinMax();
-    }
-    const char *extraName() const {
-        return mir()->isMax() ? "Max" : "Min";
-    }
+    LMinMaxD(const LAllocation &first, const LAllocation &second) : LMinMaxBase(first, second)
+    {}
+};
+
+class LMinMaxF : public LMinMaxBase
+{
+  public:
+    LIR_HEADER(MinMaxF)
+    LMinMaxF(const LAllocation &first, const LAllocation &second) : LMinMaxBase(first, second)
+    {}
 };
 
 // Negative of an integer
 class LNegI : public LInstructionHelper<1, 1, 0>
 {
   public:
     LIR_HEADER(NegI);
     explicit LNegI(const LAllocation &num) {
--- a/js/src/jit/LOpcodes.h
+++ b/js/src/jit/LOpcodes.h
@@ -16,16 +16,18 @@
     _(Pointer)                      \
     _(Double)                       \
     _(Float32)                      \
     _(SimdSplatX4)                  \
     _(Int32x4)                      \
     _(Float32x4)                    \
     _(SimdExtractElementI)          \
     _(SimdExtractElementF)          \
+    _(SimdInsertElementI)           \
+    _(SimdInsertElementF)           \
     _(SimdSignMaskX4)               \
     _(SimdBinaryCompIx4)            \
     _(SimdBinaryCompFx4)            \
     _(SimdBinaryArithIx4)           \
     _(SimdBinaryArithFx4)           \
     _(SimdBinaryBitwiseX4)          \
     _(SimdSelect)                   \
     _(Value)                        \
@@ -110,16 +112,17 @@
     _(CompareVM)                    \
     _(BitAndAndBranch)              \
     _(IsNullOrLikeUndefined)        \
     _(IsNullOrLikeUndefinedAndBranch)\
     _(EmulatesUndefined)            \
     _(EmulatesUndefinedAndBranch)   \
     _(MinMaxI)                      \
     _(MinMaxD)                      \
+    _(MinMaxF)                      \
     _(NegI)                         \
     _(NegD)                         \
     _(NegF)                         \
     _(AbsI)                         \
     _(AbsD)                         \
     _(AbsF)                         \
     _(ClzI)                         \
     _(SqrtD)                        \
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -1283,16 +1283,22 @@ LIRGenerator::visitMinMax(MMinMax *ins)
 
     ReorderCommutative(&first, &second, ins);
 
     if (ins->specialization() == MIRType_Int32) {
         LMinMaxI *lir = new(alloc()) LMinMaxI(useRegisterAtStart(first), useRegisterOrConstant(second));
         return defineReuseInput(lir, ins, 0);
     }
 
+    if (ins->specialization() == MIRType_Float32) {
+        LMinMaxF *lir = new(alloc()) LMinMaxF(useRegisterAtStart(first), useRegister(second));
+        return defineReuseInput(lir, ins, 0);
+    }
+
+    MOZ_ASSERT(ins->specialization() == MIRType_Double);
     LMinMaxD *lir = new(alloc()) LMinMaxD(useRegisterAtStart(first), useRegister(second));
     return defineReuseInput(lir, ins, 0);
 }
 
 bool
 LIRGenerator::visitAbs(MAbs *ins)
 {
     MDefinition *num = ins->input();
@@ -3716,28 +3722,43 @@ LIRGenerator::visitSimdExtractElement(MS
 {
     JS_ASSERT(IsSimdType(ins->input()->type()));
     JS_ASSERT(!IsSimdType(ins->type()));
 
     if (ins->input()->type() == MIRType_Int32x4) {
         // Note: there could be int16x8 in the future, which doesn't use the
         // same instruction. We either need to pass the arity or create new LIns.
         LUse use = useRegisterAtStart(ins->input());
-        return define(new(alloc()) LSimdExtractElementI(use, ins->lane()), ins);
+        return define(new(alloc()) LSimdExtractElementI(use), ins);
     }
 
     if (ins->input()->type() == MIRType_Float32x4) {
         LUse use = useRegisterAtStart(ins->input());
-        return define(new(alloc()) LSimdExtractElementF(use, ins->lane()), ins);
+        return define(new(alloc()) LSimdExtractElementF(use), ins);
     }
 
     MOZ_CRASH("Unknown SIMD kind when extracting element");
 }
 
 bool
+LIRGenerator::visitSimdInsertElement(MSimdInsertElement *ins)
+{
+    JS_ASSERT(IsSimdType(ins->type()));
+
+    LUse vec = useRegisterAtStart(ins->vector());
+    LUse val = useRegister(ins->value());
+    if (ins->type() == MIRType_Int32x4)
+        return defineReuseInput(new(alloc()) LSimdInsertElementI(vec, val), ins, 0);
+    if (ins->type() == MIRType_Float32x4)
+        return defineReuseInput(new(alloc()) LSimdInsertElementF(vec, val), ins, 0);
+
+    MOZ_CRASH("Unknown SIMD kind when generating constant");
+}
+
+bool
 LIRGenerator::visitSimdSignMask(MSimdSignMask *ins)
 {
     MDefinition *input = ins->input();
     MOZ_ASSERT(IsSimdType(input->type()));
     MOZ_ASSERT(ins->type() == MIRType_Int32);
 
     LUse use = useRegisterAtStart(input);
 
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -264,16 +264,17 @@ class LIRGenerator : public LIRGenerator
     bool visitAsmJSVoidReturn(MAsmJSVoidReturn *ins);
     bool visitAsmJSPassStackArg(MAsmJSPassStackArg *ins);
     bool visitAsmJSCall(MAsmJSCall *ins);
     bool visitSetDOMProperty(MSetDOMProperty *ins);
     bool visitGetDOMProperty(MGetDOMProperty *ins);
     bool visitGetDOMMember(MGetDOMMember *ins);
     bool visitRecompileCheck(MRecompileCheck *ins);
     bool visitSimdExtractElement(MSimdExtractElement *ins);
+    bool visitSimdInsertElement(MSimdInsertElement *ins);
     bool visitSimdSignMask(MSimdSignMask *ins);
     bool visitSimdBinaryComp(MSimdBinaryComp *ins);
     bool visitSimdBinaryArith(MSimdBinaryArith *ins);
     bool visitSimdBinaryBitwise(MSimdBinaryBitwise *ins);
     bool visitSimdConstant(MSimdConstant *ins);
     bool visitPhi(MPhi *ins);
     bool visitBeta(MBeta *ins);
     bool visitObjectState(MObjectState *ins);
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -1693,18 +1693,41 @@ MBinaryArithInstruction::trySpecializeFl
 {
     // Do not use Float32 if we can use int32.
     if (specialization_ == MIRType_Int32)
         return;
 
     MDefinition *left = lhs();
     MDefinition *right = rhs();
 
-    if (!left->canProduceFloat32() || !right->canProduceFloat32()
-        || !CheckUsesAreFloat32Consumers(this))
+    if (!left->canProduceFloat32() || !right->canProduceFloat32() ||
+        !CheckUsesAreFloat32Consumers(this))
+    {
+        if (left->type() == MIRType_Float32)
+            ConvertDefinitionToDouble<0>(alloc, left, this);
+        if (right->type() == MIRType_Float32)
+            ConvertDefinitionToDouble<1>(alloc, right, this);
+        return;
+    }
+
+    specialization_ = MIRType_Float32;
+    setResultType(MIRType_Float32);
+}
+
+void
+MMinMax::trySpecializeFloat32(TempAllocator &alloc)
+{
+    if (specialization_ == MIRType_Int32)
+        return;
+
+    MDefinition *left = lhs();
+    MDefinition *right = rhs();
+
+    if (!(left->canProduceFloat32() || (left->isMinMax() && left->type() == MIRType_Float32)) ||
+        !(right->canProduceFloat32() || (right->isMinMax() && right->type() == MIRType_Float32)))
     {
         if (left->type() == MIRType_Float32)
             ConvertDefinitionToDouble<0>(alloc, left, this);
         if (right->type() == MIRType_Float32)
             ConvertDefinitionToDouble<1>(alloc, right, this);
         return;
     }
 
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -1397,16 +1397,60 @@ class MSimdExtractElement : public MUnar
             return false;
         const MSimdExtractElement *other = ins->toSimdExtractElement();
         if (other->lane_ != lane_)
             return false;
         return congruentIfOperandsEqual(other);
     }
 };
 
+// Replaces the datum in the given lane by a scalar value of the same type.
+class MSimdInsertElement : public MBinaryInstruction
+{
+  private:
+    SimdLane lane_;
+
+    MSimdInsertElement(MDefinition *vec, MDefinition *val, MIRType type, SimdLane lane)
+      : MBinaryInstruction(vec, val), lane_(lane)
+    {
+        MOZ_ASSERT(IsSimdType(type) && vec->type() == type);
+        MOZ_ASSERT(SimdTypeToScalarType(type) == val->type());
+
+        setMovable();
+        setResultType(type);
+    }
+
+  public:
+    INSTRUCTION_HEADER(SimdInsertElement)
+
+    static MSimdInsertElement *NewAsmJS(TempAllocator &alloc, MDefinition *vec, MDefinition *val,
+                                         MIRType type, SimdLane lane)
+    {
+        return new(alloc) MSimdInsertElement(vec, val, type, lane);
+    }
+
+    MDefinition *vector() {
+        return getOperand(0);
+    }
+    MDefinition *value() {
+        return getOperand(1);
+    }
+    SimdLane lane() const {
+        return lane_;
+    }
+
+    AliasSet getAliasSet() const {
+        return AliasSet::None();
+    }
+
+    bool congruentTo(const MDefinition *ins) const {
+        return binaryCongruentTo(ins) && lane_ == ins->toSimdInsertElement()->lane();
+    }
+};
+
 // Extracts the sign bits from a given vector, returning an MIRType_Int32.
 class MSimdSignMask : public MUnaryInstruction
 {
   protected:
     explicit MSimdSignMask(MDefinition *obj)
       : MUnaryInstruction(obj)
     {
         MOZ_ASSERT(IsSimdType(obj->type()));
@@ -4534,17 +4578,17 @@ class MMinMax
     public ArithPolicy::Data
 {
     bool isMax_;
 
     MMinMax(MDefinition *left, MDefinition *right, MIRType type, bool isMax)
       : MBinaryInstruction(left, right),
         isMax_(isMax)
     {
-        JS_ASSERT(type == MIRType_Double || type == MIRType_Int32);
+        JS_ASSERT(IsNumberType(type));
         setResultType(type);
         setMovable();
         specialization_ = type;
     }
 
   public:
     INSTRUCTION_HEADER(MinMax)
     static MMinMax *New(TempAllocator &alloc, MDefinition *left, MDefinition *right, MIRType type,
@@ -4569,16 +4613,19 @@ class MMinMax
         return AliasSet::None();
     }
     void computeRange(TempAllocator &alloc);
     bool writeRecoverData(CompactBufferWriter &writer) const;
     bool canRecoverOnBailout() const {
         return true;
     }
 
+    bool isFloat32Commutative() const { return true; }
+    void trySpecializeFloat32(TempAllocator &alloc);
+
     ALLOW_CLONE(MMinMax)
 };
 
 class MAbs
   : public MUnaryInstruction,
     public ArithPolicy::Data
 {
     bool implicitTruncate_;
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -11,16 +11,17 @@ namespace js {
 namespace jit {
 
 #define MIR_OPCODE_LIST(_)                                                  \
     _(Constant)                                                             \
     _(SimdValueX4)                                                          \
     _(SimdSplatX4)                                                          \
     _(SimdConstant)                                                         \
     _(SimdExtractElement)                                                   \
+    _(SimdInsertElement)                                                    \
     _(SimdSignMask)                                                         \
     _(SimdBinaryComp)                                                       \
     _(SimdBinaryArith)                                                      \
     _(SimdBinaryBitwise)                                                    \
     _(SimdTernaryBitwise)                                                   \
     _(CloneLiteral)                                                         \
     _(Parameter)                                                            \
     _(Callee)                                                               \
--- a/js/src/jit/ParallelSafetyAnalysis.cpp
+++ b/js/src/jit/ParallelSafetyAnalysis.cpp
@@ -110,16 +110,17 @@ class ParallelSafetyVisitor : public MDe
     // I am taking the policy of blacklisting everything that's not
     // obviously safe for now.  We can loosen as we need.
 
     SAFE_OP(Constant)
     SAFE_OP(SimdValueX4)
     SAFE_OP(SimdSplatX4)
     SAFE_OP(SimdConstant)
     SAFE_OP(SimdExtractElement)
+    SAFE_OP(SimdInsertElement)
     SAFE_OP(SimdSignMask)
     SAFE_OP(SimdBinaryComp)
     SAFE_OP(SimdBinaryArith)
     SAFE_OP(SimdBinaryBitwise)
     SAFE_OP(SimdTernaryBitwise)
     UNSAFE_OP(CloneLiteral)
     SAFE_OP(Parameter)
     SAFE_OP(Callee)
--- a/js/src/jit/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -286,16 +286,65 @@ CodeGeneratorARM::visitMinMaxD(LMinMaxD 
     masm.bind(&returnSecond);
     masm.ma_vmov(second, output);
 
     masm.bind(&done);
     return true;
 }
 
 bool
+CodeGeneratorARM::visitMinMaxF(LMinMaxF *ins)
+{
+    FloatRegister first = ToFloatRegister(ins->first());
+    FloatRegister second = ToFloatRegister(ins->second());
+    FloatRegister output = ToFloatRegister(ins->output());
+
+    JS_ASSERT(first == output);
+
+    Assembler::Condition cond = ins->mir()->isMax()
+        ? Assembler::VFP_LessThanOrEqual
+        : Assembler::VFP_GreaterThanOrEqual;
+    Label nan, equal, returnSecond, done;
+
+    masm.compareFloat(first, second);
+    // First or second is NaN, result is NaN.
+    masm.ma_b(&nan, Assembler::VFP_Unordered);
+    // Make sure we handle -0 and 0 right.
+    masm.ma_b(&equal, Assembler::VFP_Equal);
+    masm.ma_b(&returnSecond, cond);
+    masm.ma_b(&done);
+
+    // Check for zero.
+    masm.bind(&equal);
+    masm.compareFloat(first, NoVFPRegister);
+    // First wasn't 0 or -0, so just return it.
+    masm.ma_b(&done, Assembler::VFP_NotEqualOrUnordered);
+    // So now both operands are either -0 or 0.
+    if (ins->mir()->isMax()) {
+        // -0 + -0 = -0 and -0 + 0 = 0.
+        masm.ma_vadd_f32(second, first, first);
+    } else {
+        masm.ma_vneg_f32(first, first);
+        masm.ma_vsub_f32(first, second, first);
+        masm.ma_vneg_f32(first, first);
+    }
+    masm.ma_b(&done);
+
+    masm.bind(&nan);
+    masm.loadConstantFloat32(GenericNaN(), output);
+    masm.ma_b(&done);
+
+    masm.bind(&returnSecond);
+    masm.ma_vmov_f32(second, output);
+
+    masm.bind(&done);
+    return true;
+}
+
+bool
 CodeGeneratorARM::visitAbsD(LAbsD *ins)
 {
     FloatRegister input = ToFloatRegister(ins->input());
     JS_ASSERT(input == ToFloatRegister(ins->output()));
     masm.ma_vabs(input, input);
     return true;
 }
 
--- a/js/src/jit/arm/CodeGenerator-arm.h
+++ b/js/src/jit/arm/CodeGenerator-arm.h
@@ -97,16 +97,17 @@ class CodeGeneratorARM : public CodeGene
         emitBranch(cond, ifTrue, ifFalse);
     }
 
     bool emitTableSwitchDispatch(MTableSwitch *mir, Register index, Register base);
 
   public:
     // Instruction visitors.
     virtual bool visitMinMaxD(LMinMaxD *ins);
+    virtual bool visitMinMaxF(LMinMaxF *ins);
     virtual bool visitAbsD(LAbsD *ins);
     virtual bool visitAbsF(LAbsF *ins);
     virtual bool visitSqrtD(LSqrtD *ins);
     virtual bool visitSqrtF(LSqrtF *ins);
     virtual bool visitAddI(LAddI *ins);
     virtual bool visitSubI(LSubI *ins);
     virtual bool visitBitNotI(LBitNotI *ins);
     virtual bool visitBitOpI(LBitOpI *ins);
--- a/js/src/jit/shared/Assembler-x86-shared.h
+++ b/js/src/jit/shared/Assembler-x86-shared.h
@@ -520,43 +520,50 @@ class AssemblerX86Shared : public Assemb
           case Operand::MEM_SCALE:
             masm.movups_rm(src.code(), dest.disp(), dest.base(), dest.index(), dest.scale());
             break;
           default:
             MOZ_CRASH("unexpected operand kind");
         }
     }
 
-    // movsd and movss are only provided in load/store form since the
+    // movsd is only provided in load/store form since the
     // register-to-register form has different semantics (it doesn't clobber
     // the whole output register) and isn't needed currently.
     void movsd(const Address &src, FloatRegister dest) {
         masm.movsd_mr(src.offset, src.base.code(), dest.code());
     }
     void movsd(const BaseIndex &src, FloatRegister dest) {
         masm.movsd_mr(src.offset, src.base.code(), src.index.code(), src.scale, dest.code());
     }
     void movsd(FloatRegister src, const Address &dest) {
         masm.movsd_rm(src.code(), dest.offset, dest.base.code());
     }
     void movsd(FloatRegister src, const BaseIndex &dest) {
         masm.movsd_rm(src.code(), dest.offset, dest.base.code(), dest.index.code(), dest.scale);
     }
+    // Although movss is not only provided in load/store form (for the same
+    // reasons as movsd above), the register to register form should be only
+    // used in contexts where we care about not clearing the higher lanes of
+    // the FloatRegister.
     void movss(const Address &src, FloatRegister dest) {
         masm.movss_mr(src.offset, src.base.code(), dest.code());
     }
     void movss(const BaseIndex &src, FloatRegister dest) {
         masm.movss_mr(src.offset, src.base.code(), src.index.code(), src.scale, dest.code());
     }
     void movss(FloatRegister src, const Address &dest) {
         masm.movss_rm(src.code(), dest.offset, dest.base.code());
     }
     void movss(FloatRegister src, const BaseIndex &dest) {
         masm.movss_rm(src.code(), dest.offset, dest.base.code(), dest.index.code(), dest.scale);
     }
+    void movss(FloatRegister src, const FloatRegister &dest) {
+        masm.movss_rr(src.code(), dest.code());
+    }
     void movdqu(const Operand &src, FloatRegister dest) {
         JS_ASSERT(HasSSE2());
         switch (src.kind()) {
           case Operand::MEM_REG_DISP:
             masm.movdqu_mr(src.disp(), src.base(), dest.code());
             break;
           case Operand::MEM_SCALE:
             masm.movdqu_mr(src.disp(), src.base(), src.index(), src.scale(), dest.code());
@@ -1407,21 +1414,21 @@ class AssemblerX86Shared : public Assemb
         masm.divl_r(divisor.code());
     }
 
     void unpcklps(FloatRegister src, FloatRegister dest) {
         JS_ASSERT(HasSSE2());
         masm.unpcklps_rr(src.code(), dest.code());
     }
     void pinsrd(unsigned lane, Register src, FloatRegister dest) {
-        JS_ASSERT(HasSSE2());
+        JS_ASSERT(HasSSE41());
         masm.pinsrd_irr(lane, src.code(), dest.code());
     }
     void pinsrd(unsigned lane, const Operand &src, FloatRegister dest) {
-        JS_ASSERT(HasSSE2());
+        JS_ASSERT(HasSSE41());
         switch (src.kind()) {
           case Operand::REG:
             masm.pinsrd_irr(lane, src.reg(), dest.code());
             break;
           case Operand::MEM_REG_DISP:
             masm.pinsrd_imr(lane, src.disp(), src.base(), dest.code());
             break;
           default:
@@ -1926,44 +1933,59 @@ class AssemblerX86Shared : public Assemb
     void xorps(FloatRegister src, FloatRegister dest) {
         JS_ASSERT(HasSSE2());
         masm.xorps_rr(src.code(), dest.code());
     }
     void orpd(FloatRegister src, FloatRegister dest) {
         JS_ASSERT(HasSSE2());
         masm.orpd_rr(src.code(), dest.code());
     }
+    void orps(FloatRegister src, FloatRegister dest) {
+        JS_ASSERT(HasSSE2());
+        masm.orps_rr(src.code(), dest.code());
+    }
     void andpd(FloatRegister src, FloatRegister dest) {
         JS_ASSERT(HasSSE2());
         masm.andpd_rr(src.code(), dest.code());
     }
     void andps(FloatRegister src, FloatRegister dest) {
         JS_ASSERT(HasSSE2());
         masm.andps_rr(src.code(), dest.code());
     }
     void sqrtsd(FloatRegister src, FloatRegister dest) {
         JS_ASSERT(HasSSE2());
         masm.sqrtsd_rr(src.code(), dest.code());
     }
     void sqrtss(FloatRegister src, FloatRegister dest) {
         JS_ASSERT(HasSSE2());
         masm.sqrtss_rr(src.code(), dest.code());
     }
-    void roundsd(FloatRegister src, FloatRegister dest,
-                 X86Assembler::RoundingMode mode)
-    {
+    void roundsd(FloatRegister src, FloatRegister dest, X86Assembler::RoundingMode mode) {
         JS_ASSERT(HasSSE41());
         masm.roundsd_rr(src.code(), dest.code(), mode);
     }
-    void roundss(FloatRegister src, FloatRegister dest,
-                 X86Assembler::RoundingMode mode)
-    {
+    void roundss(FloatRegister src, FloatRegister dest, X86Assembler::RoundingMode mode) {
         JS_ASSERT(HasSSE41());
         masm.roundss_rr(src.code(), dest.code(), mode);
     }
+    unsigned insertpsMask(SimdLane sourceLane, SimdLane destLane, unsigned zeroMask = 0)
+    {
+        // Note that the sourceLane bits are ignored in the case of a source
+        // memory operand, and the source is the given 32-bits memory location.
+        MOZ_ASSERT(zeroMask < 16);
+        unsigned ret = zeroMask ;
+        ret |= unsigned(destLane) << 4;
+        ret |= unsigned(sourceLane) << 6;
+        MOZ_ASSERT(ret < 256);
+        return ret;
+    }
+    void insertps(FloatRegister src, FloatRegister dest, unsigned mask) {
+        JS_ASSERT(HasSSE41());
+        masm.insertps_irr(mask, src.code(), dest.code());
+    }
     void minsd(FloatRegister src, FloatRegister dest) {
         JS_ASSERT(HasSSE2());
         masm.minsd_rr(src.code(), dest.code());
     }
     void minsd(const Operand &src, FloatRegister dest) {
         JS_ASSERT(HasSSE2());
         switch (src.kind()) {
           case Operand::FPREG:
@@ -1971,16 +1993,20 @@ class AssemblerX86Shared : public Assemb
             break;
           case Operand::MEM_REG_DISP:
             masm.minsd_mr(src.disp(), src.base(), dest.code());
             break;
           default:
             MOZ_CRASH("unexpected operand kind");
         }
     }
+    void minss(FloatRegister src, FloatRegister dest) {
+        JS_ASSERT(HasSSE2());
+        masm.minss_rr(src.code(), dest.code());
+    }
     void maxsd(FloatRegister src, FloatRegister dest) {
         JS_ASSERT(HasSSE2());
         masm.maxsd_rr(src.code(), dest.code());
     }
     void maxsd(const Operand &src, FloatRegister dest) {
         JS_ASSERT(HasSSE2());
         switch (src.kind()) {
           case Operand::FPREG:
@@ -1988,16 +2014,20 @@ class AssemblerX86Shared : public Assemb
             break;
           case Operand::MEM_REG_DISP:
             masm.maxsd_mr(src.disp(), src.base(), dest.code());
             break;
           default:
             MOZ_CRASH("unexpected operand kind");
         }
     }
+    void maxss(FloatRegister src, FloatRegister dest) {
+        JS_ASSERT(HasSSE2());
+        masm.maxss_rr(src.code(), dest.code());
+    }
     void fisttp(const Operand &dest) {
         JS_ASSERT(HasSSE3());
         switch (dest.kind()) {
           case Operand::MEM_REG_DISP:
             masm.fisttp_m(dest.disp(), dest.base());
             break;
           default:
             MOZ_CRASH("unexpected operand kind");
--- a/js/src/jit/shared/BaseAssembler-x86-shared.h
+++ b/js/src/jit/shared/BaseAssembler-x86-shared.h
@@ -303,20 +303,22 @@ private:
         OP2_ADDPS_VpsWps    = 0x58,
         OP2_MULSD_VsdWsd    = 0x59,
         OP2_MULPS_VpsWps    = 0x59,
         OP2_CVTSS2SD_VsdEd  = 0x5A,
         OP2_CVTSD2SS_VsdEd  = 0x5A,
         OP2_SUBSD_VsdWsd    = 0x5C,
         OP2_SUBPS_VpsWps    = 0x5C,
         OP2_MINSD_VsdWsd    = 0x5D,
+        OP2_MINSS_VssWss    = 0x5D,
         OP2_MINPS_VpsWps    = 0x5D,
         OP2_DIVSD_VsdWsd    = 0x5E,
         OP2_DIVPS_VpsWps    = 0x5E,
         OP2_MAXSD_VsdWsd    = 0x5F,
+        OP2_MAXSS_VssWss    = 0x5F,
         OP2_MAXPS_VpsWps    = 0x5F,
         OP2_SQRTSD_VsdWsd   = 0x51,
         OP2_SQRTSS_VssWss   = 0x51,
         OP2_ANDPD_VpdWpd    = 0x54,
         OP2_ORPD_VpdWpd     = 0x56,
         OP2_XORPD_VpdWpd    = 0x57,
         OP2_PCMPGTD_VdqWdq  = 0x66,
         OP2_MOVD_VdEd       = 0x6E,
@@ -345,23 +347,25 @@ private:
         OP2_PSUBD_VdqWdq    = 0xFA,
         OP2_PADDD_VdqWdq    = 0xFE
     } TwoByteOpcodeID;
 
     typedef enum {
         OP3_ROUNDSS_VsdWsd  = 0x0A,
         OP3_ROUNDSD_VsdWsd  = 0x0B,
         OP3_PTEST_VdVd      = 0x17,
+        OP3_INSERTPS_VpsUps = 0x21,
         OP3_PINSRD_VdqEdIb  = 0x22
     } ThreeByteOpcodeID;
 
     typedef enum {
         ESCAPE_PTEST        = 0x38,
         ESCAPE_PINSRD       = 0x3A,
-        ESCAPE_ROUNDSD      = 0x3A
+        ESCAPE_ROUNDSD      = 0x3A,
+        ESCAPE_INSERTPS     = 0x3A
     } ThreeByteEscape;
 
     TwoByteOpcodeID jccRel32(Condition cond)
     {
         return (TwoByteOpcodeID)(OP2_JCC_rel32 + cond);
     }
 
     TwoByteOpcodeID setccOpcode(Condition cond)
@@ -3597,16 +3601,26 @@ public:
     {
         spew("roundss    %s, %s, %d",
              nameFPReg(src), nameFPReg(dst), (int)mode);
         m_formatter.prefix(PRE_SSE_66);
         m_formatter.threeByteOp(OP3_ROUNDSS_VsdWsd, ESCAPE_ROUNDSD, (RegisterID)dst, (RegisterID)src);
         m_formatter.immediate8(mode); // modes are the same for roundsd and roundss
     }
 
+    void insertps_irr(unsigned mask, XMMRegisterID src, XMMRegisterID dst)
+    {
+        MOZ_ASSERT(mask < 256);
+        spew("insertps     $%u, %s, %s",
+             mask, nameFPReg(src), nameFPReg(dst));
+        m_formatter.prefix(PRE_SSE_66);
+        m_formatter.threeByteOp(OP3_INSERTPS_VpsUps, ESCAPE_INSERTPS, (RegisterID)dst, (RegisterID)src);
+        m_formatter.immediate8(uint8_t(mask));
+    }
+
     void pinsrd_irr(unsigned lane, RegisterID src, XMMRegisterID dst)
     {
         MOZ_ASSERT(lane < 4);
         spew("pinsrd     $%u, %s, %s",
              lane, nameIReg(src), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_66);
         m_formatter.threeByteOp(OP3_PINSRD_VdqEdIb, ESCAPE_PINSRD, (RegisterID)dst, (RegisterID)src);
         m_formatter.immediate8(uint8_t(lane));
@@ -3634,32 +3648,48 @@ public:
     void minsd_mr(int offset, RegisterID base, XMMRegisterID dst)
     {
         spew("minsd      %s0x%x(%s), %s",
              PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_MINSD_VsdWsd, (RegisterID)dst, base, offset);
     }
 
+    void minss_rr(XMMRegisterID src, XMMRegisterID dst)
+    {
+        spew("minss      %s, %s",
+             nameFPReg(src), nameFPReg(dst));
+        m_formatter.prefix(PRE_SSE_F3);
+        m_formatter.twoByteOp(OP2_MINSS_VssWss, (RegisterID)dst, (RegisterID)src);
+    }
+
     void maxsd_rr(XMMRegisterID src, XMMRegisterID dst)
     {
         spew("maxsd      %s, %s",
              nameFPReg(src), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_MAXSD_VsdWsd, (RegisterID)dst, (RegisterID)src);
     }
 
     void maxsd_mr(int offset, RegisterID base, XMMRegisterID dst)
     {
         spew("maxsd      %s0x%x(%s), %s",
              PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_MAXSD_VsdWsd, (RegisterID)dst, base, offset);
     }
 
+    void maxss_rr(XMMRegisterID src, XMMRegisterID dst)
+    {
+        spew("maxss      %s, %s",
+             nameFPReg(src), nameFPReg(dst));
+        m_formatter.prefix(PRE_SSE_F3);
+        m_formatter.twoByteOp(OP2_MAXSS_VssWss, (RegisterID)dst, (RegisterID)src);
+    }
+
     // Misc instructions:
 
     void int3()
     {
         spew("int3");
         m_formatter.oneByteOp(OP_INT3);
     }
 
--- a/js/src/jit/shared/CodeGenerator-x86-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-x86-shared.cpp
@@ -532,16 +532,68 @@ CodeGeneratorX86Shared::visitMinMaxD(LMi
     else
         masm.minsd(second, first);
 
     masm.bind(&done);
     return true;
 }
 
 bool
+CodeGeneratorX86Shared::visitMinMaxF(LMinMaxF *ins)
+{
+    FloatRegister first = ToFloatRegister(ins->first());
+    FloatRegister second = ToFloatRegister(ins->second());
+#ifdef DEBUG
+    FloatRegister output = ToFloatRegister(ins->output());
+    JS_ASSERT(first == output);
+#endif
+
+    Label done, nan, minMaxInst;
+
+    // Do a ucomiss to catch equality and NaNs, which both require special
+    // handling. If the operands are ordered and inequal, we branch straight to
+    // the min/max instruction. If we wanted, we could also branch for less-than
+    // or greater-than here instead of using min/max, however these conditions
+    // will sometimes be hard on the branch predictor.
+    masm.ucomiss(first, second);
+    masm.j(Assembler::NotEqual, &minMaxInst);
+    if (!ins->mir()->range() || ins->mir()->range()->canBeNaN())
+        masm.j(Assembler::Parity, &nan);
+
+    // Ordered and equal. The operands are bit-identical unless they are zero
+    // and negative zero. These instructions merge the sign bits in that
+    // case, and are no-ops otherwise.
+    if (ins->mir()->isMax())
+        masm.andps(second, first);
+    else
+        masm.orps(second, first);
+    masm.jump(&done);
+
+    // x86's min/max are not symmetric; if either operand is a NaN, they return
+    // the read-only operand. We need to return a NaN if either operand is a
+    // NaN, so we explicitly check for a NaN in the read-write operand.
+    if (!ins->mir()->range() || ins->mir()->range()->canBeNaN()) {
+        masm.bind(&nan);
+        masm.ucomiss(first, first);
+        masm.j(Assembler::Parity, &done);
+    }
+
+    // When the values are inequal, or second is NaN, x86's min and max will
+    // return the value we need.
+    masm.bind(&minMaxInst);
+    if (ins->mir()->isMax())
+        masm.maxss(second, first);
+    else
+        masm.minss(second, first);
+
+    masm.bind(&done);
+    return true;
+}
+
+bool
 CodeGeneratorX86Shared::visitAbsD(LAbsD *ins)
 {
     FloatRegister input = ToFloatRegister(ins->input());
     JS_ASSERT(input == ToFloatRegister(ins->output()));
     // Load a value which is all ones except for the sign bit.
     masm.loadConstantDouble(SpecificNaN<double>(0, FloatingPoint<double>::kSignificandBits),
                             ScratchDoubleReg);
     masm.andpd(ScratchDoubleReg, input);
@@ -2228,16 +2280,73 @@ CodeGeneratorX86Shared::visitSimdExtract
         uint32_t mask = MacroAssembler::ComputeShuffleMask(lane);
         masm.shuffleFloat32(mask, input, output);
     }
     masm.canonicalizeFloat(output);
     return true;
 }
 
 bool
+CodeGeneratorX86Shared::visitSimdInsertElementI(LSimdInsertElementI *ins)
+{
+    FloatRegister vector = ToFloatRegister(ins->vector());
+    Register value = ToRegister(ins->value());
+    FloatRegister output = ToFloatRegister(ins->output());
+    MOZ_ASSERT(vector == output); // defineReuseInput(0)
+
+    unsigned component = unsigned(ins->lane());
+
+    // Note that, contrarily to float32x4, we cannot use movd if the inserted
+    // value goes into the first component, as movd clears out the higher lanes
+    // of the output.
+    if (AssemblerX86Shared::HasSSE41()) {
+        masm.pinsrd(component, value, output);
+        return true;
+    }
+
+    masm.reserveStack(Simd128DataSize);
+    masm.storeAlignedInt32x4(vector, Address(StackPointer, 0));
+    masm.store32(value, Address(StackPointer, component * sizeof(int32_t)));
+    masm.loadAlignedInt32x4(Address(StackPointer, 0), output);
+    masm.freeStack(Simd128DataSize);
+    return true;
+}
+
+bool
+CodeGeneratorX86Shared::visitSimdInsertElementF(LSimdInsertElementF *ins)
+{
+    FloatRegister vector = ToFloatRegister(ins->vector());
+    FloatRegister value = ToFloatRegister(ins->value());
+    FloatRegister output = ToFloatRegister(ins->output());
+    MOZ_ASSERT(vector == output); // defineReuseInput(0)
+
+    if (ins->lane() == SimdLane::LaneX) {
+        // As both operands are registers, movss doesn't modify the upper bits
+        // of the destination operand.
+        if (value != output)
+            masm.movss(value, output);
+        return true;
+    }
+
+    if (AssemblerX86Shared::HasSSE41()) {
+        // The input value is in the low float32 of the 'value' FloatRegister.
+        masm.insertps(value, output, masm.insertpsMask(SimdLane::LaneX, ins->lane()));
+        return true;
+    }
+
+    unsigned component = unsigned(ins->lane());
+    masm.reserveStack(Simd128DataSize);
+    masm.storeAlignedFloat32x4(vector, Address(StackPointer, 0));
+    masm.storeFloat32(value, Address(StackPointer, component * sizeof(int32_t)));
+    masm.loadAlignedFloat32x4(Address(StackPointer, 0), output);
+    masm.freeStack(Simd128DataSize);
+    return true;
+}
+
+bool
 CodeGeneratorX86Shared::visitSimdSignMaskX4(LSimdSignMaskX4 *ins)
 {
     FloatRegister input = ToFloatRegister(ins->input());
     Register output = ToRegister(ins->output());
 
     // For Float32x4 and Int32x4.
     masm.movmskps(input, output);
     return true;
--- a/js/src/jit/shared/CodeGenerator-x86-shared.h
+++ b/js/src/jit/shared/CodeGenerator-x86-shared.h
@@ -146,16 +146,17 @@ class CodeGeneratorX86Shared : public Co
   public:
     CodeGeneratorX86Shared(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm);
 
   public:
     // Instruction visitors.
     virtual bool visitDouble(LDouble *ins);
     virtual bool visitFloat32(LFloat32 *ins);
     virtual bool visitMinMaxD(LMinMaxD *ins);
+    virtual bool visitMinMaxF(LMinMaxF *ins);
     virtual bool visitAbsD(LAbsD *ins);
     virtual bool visitAbsF(LAbsF *ins);
     virtual bool visitClzI(LClzI *ins);
     virtual bool visitSqrtD(LSqrtD *ins);
     virtual bool visitSqrtF(LSqrtF *ins);
     virtual bool visitPowHalfD(LPowHalfD *ins);
     virtual bool visitAddI(LAddI *ins);
     virtual bool visitSubI(LSubI *ins);
@@ -208,16 +209,18 @@ class CodeGeneratorX86Shared : public Co
     // SIMD operators
     bool visitSimdValueInt32x4(LSimdValueInt32x4 *lir);
     bool visitSimdValueFloat32x4(LSimdValueFloat32x4 *lir);
     bool visitSimdSplatX4(LSimdSplatX4 *lir);
     bool visitInt32x4(LInt32x4 *ins);
     bool visitFloat32x4(LFloat32x4 *ins);
     bool visitSimdExtractElementI(LSimdExtractElementI *lir);
     bool visitSimdExtractElementF(LSimdExtractElementF *lir);
+    bool visitSimdInsertElementI(LSimdInsertElementI *lir);
+    bool visitSimdInsertElementF(LSimdInsertElementF *lir);
     bool visitSimdSignMaskX4(LSimdSignMaskX4 *ins);
     bool visitSimdBinaryCompIx4(LSimdBinaryCompIx4 *lir);
     bool visitSimdBinaryCompFx4(LSimdBinaryCompFx4 *lir);
     bool visitSimdBinaryArithIx4(LSimdBinaryArithIx4 *lir);
     bool visitSimdBinaryArithFx4(LSimdBinaryArithFx4 *lir);
     bool visitSimdBinaryBitwiseX4(LSimdBinaryBitwiseX4 *lir);
     bool visitSimdSelect(LSimdSelect *ins);
 
--- a/js/src/jsapi-tests/testBug604087.cpp
+++ b/js/src/jsapi-tests/testBug604087.cpp
@@ -18,17 +18,18 @@ const js::Class OuterWrapperClass =
     PROXY_CLASS_WITH_EXT(
         "Proxy",
         0, /* additional slots */
         0, /* additional class flags */
         PROXY_MAKE_EXT(
             nullptr, /* outerObject */
             js::proxy_innerObject,
             nullptr, /* iteratorObject */
-            false   /* isWrappedNative */
+            false,   /* isWrappedNative */
+            nullptr  /* objectMoved */
         ));
 
 static JSObject *
 wrap(JSContext *cx, JS::HandleObject toWrap, JS::HandleObject target)
 {
     JSAutoCompartment ac(cx, target);
     JS::RootedObject wrapper(cx, toWrap);
     if (!JS_WrapObject(cx, &wrapper))
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -247,24 +247,24 @@ namespace js {
 
 /*
  * Helper Macros for creating JSClasses that function as proxies.
  *
  * NB: The macro invocation must be surrounded by braces, so as to
  *     allow for potention JSClass extensions.
  */
 #define PROXY_MAKE_EXT(outerObject, innerObject, iteratorObject,        \
-                       isWrappedNative)                                 \
+                       isWrappedNative, objectMoved)                    \
     {                                                                   \
         outerObject,                                                    \
         innerObject,                                                    \
         iteratorObject,                                                 \
         isWrappedNative,                                                \
         js::proxy_WeakmapKeyDelegate,                                   \
-        js::proxy_ObjectMoved                                           \
+        objectMoved                                                     \
     }
 
 #define PROXY_CLASS_WITH_EXT(name, extraSlots, flags, ext)                              \
     {                                                                                   \
         name,                                                                           \
         js::Class::NON_NATIVE |                                                         \
             JSCLASS_IS_PROXY |                                                          \
             JSCLASS_IMPLEMENTS_BARRIERS |                                               \
@@ -308,17 +308,18 @@ namespace js {
     }
 
 #define PROXY_CLASS_DEF(name, extraSlots, flags)                        \
   PROXY_CLASS_WITH_EXT(name, extraSlots, flags,                         \
                        PROXY_MAKE_EXT(                                  \
                          nullptr, /* outerObject */                     \
                          nullptr, /* innerObject */                     \
                          nullptr, /* iteratorObject */                  \
-                         false    /* isWrappedNative */                 \
+                         false,   /* isWrappedNative */                 \
+                         js::proxy_ObjectMoved                          \
                        ))
 
 /*
  * Proxy stubs, similar to JS_*Stub, for embedder proxy class definitions.
  *
  * NB: Should not be called directly.
  */
 
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -450,16 +450,23 @@ ArenaHeader::checkSynchronizedWithFreeLi
     /*
      * Here this arena has free things, FreeList::lists[thingKind] is not
      * empty and also points to this arena. Thus they must be the same.
      */
     JS_ASSERT(freeList->isSameNonEmptySpan(firstSpan));
 }
 #endif
 
+void
+ArenaHeader::unmarkAll()
+{
+    uintptr_t *word = chunk()->bitmap.arenaBits(this);
+    memset(word, 0, ArenaBitmapWords * sizeof(uintptr_t));
+}
+
 /* static */ void
 Arena::staticAsserts()
 {
     static_assert(JS_ARRAY_LENGTH(ThingSizes) == FINALIZE_LIMIT, "We have defined all thing sizes.");
     static_assert(JS_ARRAY_LENGTH(FirstThingOffsets) == FINALIZE_LIMIT, "We have defined all offsets.");
 }
 
 void
@@ -2487,16 +2494,19 @@ GCRuntime::releaseRelocatedArenas(ArenaH
     }
 #endif
 
     unsigned count = 0;
     while (relocatedList) {
         ArenaHeader *aheader = relocatedList;
         relocatedList = relocatedList->next;
 
+        // Clear the mark bits
+        aheader->unmarkAll();
+
         // Mark arena as empty
         AllocKind thingKind = aheader->getAllocKind();
         size_t thingSize = aheader->getThingSize();
         Arena *arena = aheader->getArena();
         FreeSpan fullSpan;
         fullSpan.initFinal(arena->thingsStart(thingKind), arena->thingsEnd() - thingSize, thingSize);
         aheader->setFirstFreeSpan(&fullSpan);
 
@@ -5637,22 +5647,22 @@ GCRuntime::scanZonesBeforeGC()
     return zoneStats;
 }
 
 void
 GCRuntime::collect(bool incremental, int64_t budget, JSGCInvocationKind gckind,
                    JS::gcreason::Reason reason)
 {
     /* GC shouldn't be running in parallel execution mode */
-    MOZ_ASSERT(!InParallelSection());
+    MOZ_ALWAYS_TRUE(!InParallelSection());
 
     JS_AbortIfWrongThread(rt);
 
     /* If we attempt to invoke the GC while we are running in the GC, assert. */
-    MOZ_ASSERT(!rt->isHeapBusy());
+    MOZ_ALWAYS_TRUE(!rt->isHeapBusy());
 
     /* The engine never locks across anything that could GC. */
     MOZ_ASSERT(!rt->currentThreadHasExclusiveAccess());
 
     if (rt->mainThread.suppressGC)
         return;
 
     TraceLogger *logger = TraceLoggerForMainThread(rt);
@@ -6354,18 +6364,43 @@ JS::AutoAssertOnGC::~AutoAssertOnGC()
 }
 
 /* static */ void
 JS::AutoAssertOnGC::VerifyIsSafeToGC(JSRuntime *rt)
 {
     if (rt->gc.isInsideUnsafeRegion())
         MOZ_CRASH("[AutoAssertOnGC] possible GC in GC-unsafe region");
 }
+
+JS::AutoAssertNoAlloc::AutoAssertNoAlloc(JSRuntime *rt)
+  : gc(nullptr)
+{
+    disallowAlloc(rt);
+}
+
+void JS::AutoAssertNoAlloc::disallowAlloc(JSRuntime *rt)
+{
+    JS_ASSERT(!gc);
+    gc = &rt->gc;
+    gc->disallowAlloc();
+}
+
+JS::AutoAssertNoAlloc::~AutoAssertNoAlloc()
+{
+    if (gc)
+        gc->allowAlloc();
+}
 #endif
 
+JS::AutoAssertGCCallback::AutoAssertGCCallback(JSObject *obj)
+  : AutoSuppressGCAnalysis()
+{
+    MOZ_ASSERT(obj->runtimeFromMainThread()->isHeapMajorCollecting());
+}
+
 #ifdef JSGC_HASH_TABLE_CHECKS
 void
 js::gc::CheckHashTablesAfterMovingGC(JSRuntime *rt)
 {
     /*
      * Check that internal hash tables no longer have any pointers to things
      * that have been moved.
      */
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -690,20 +690,18 @@ class ArenaLists
         return true;
     }
 
     void unmarkAll() {
         for (size_t i = 0; i != FINALIZE_LIMIT; ++i) {
             /* The background finalization must have stopped at this point. */
             JS_ASSERT(backgroundFinalizeState[i] == BFS_DONE ||
                       backgroundFinalizeState[i] == BFS_JUST_FINISHED);
-            for (ArenaHeader *aheader = arenaLists[i].head(); aheader; aheader = aheader->next) {
-                uintptr_t *word = aheader->chunk()->bitmap.arenaBits(aheader);
-                memset(word, 0, ArenaBitmapWords * sizeof(uintptr_t));
-            }
+            for (ArenaHeader *aheader = arenaLists[i].head(); aheader; aheader = aheader->next)
+                aheader->unmarkAll();
         }
     }
 
     bool doneBackgroundFinalize(AllocKind kind) const {
         return backgroundFinalizeState[kind] == BFS_DONE ||
                backgroundFinalizeState[kind] == BFS_JUST_FINISHED;
     }
 
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -320,47 +320,19 @@ class ZoneCellIterUnderGC : public ZoneC
 #ifdef JSGC_GENERATIONAL
         JS_ASSERT(zone->runtimeFromAnyThread()->gc.nursery.isEmpty());
 #endif
         JS_ASSERT(zone->runtimeFromAnyThread()->isHeapBusy());
         init(zone, kind);
     }
 };
 
-/* In debug builds, assert that no allocation occurs. */
-class AutoAssertNoAlloc
-{
-#ifdef JS_DEBUG
-    GCRuntime *gc;
-
-  public:
-    AutoAssertNoAlloc() : gc(nullptr) {}
-    explicit AutoAssertNoAlloc(JSRuntime *rt) : gc(nullptr) {
-        disallowAlloc(rt);
-    }
-    void disallowAlloc(JSRuntime *rt) {
-        JS_ASSERT(!gc);
-        gc = &rt->gc;
-        gc->disallowAlloc();
-    }
-    ~AutoAssertNoAlloc() {
-        if (gc)
-            gc->allowAlloc();
-    }
-#else
-  public:
-    AutoAssertNoAlloc() {}
-    explicit AutoAssertNoAlloc(JSRuntime *) {}
-    void disallowAlloc(JSRuntime *rt) {}
-#endif
-};
-
 class ZoneCellIter : public ZoneCellIterImpl
 {
-    AutoAssertNoAlloc noAlloc;
+    JS::AutoAssertNoAlloc noAlloc;
     ArenaLists *lists;
     AllocKind kind;
 
   public:
     ZoneCellIter(JS::Zone *zone, AllocKind kind)
       : lists(&zone->allocator.arenas),
         kind(kind)
     {
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -658,16 +658,22 @@ JSObject::finish(js::FreeOp *fop)
                 // so that other objects can access these elements while they
                 // are themselves finalized.
                 fop->freeLater(elements);
             }
         } else {
             fop->free_(elements);
         }
     }
+
+    // It's possible that unreachable shapes may be marked whose listp points
+    // into this object. In case this happens, null out the shape's pointer here
+    // so that a moving GC will not try to access the dead object.
+    if (shape_->listp == &shape_)
+        shape_->listp = nullptr;
 }
 
 /* static */ inline bool
 JSObject::hasProperty(JSContext *cx, js::HandleObject obj,
                       js::HandleId id, bool *foundp)
 {
     JS::RootedObject pobj(cx);
     js::RootedShape prop(cx);
--- a/js/src/jsweakmap.cpp
+++ b/js/src/jsweakmap.cpp
@@ -66,18 +66,22 @@ WeakMapBase::unmarkCompartment(JSCompart
 {
     for (WeakMapBase *m = c->gcWeakMapList; m; m = m->next)
         m->marked = false;
 }
 
 void
 WeakMapBase::markAll(JSCompartment *c, JSTracer *tracer)
 {
-    for (WeakMapBase *m = c->gcWeakMapList; m; m = m->next)
-        m->markIteratively(tracer);
+    JS_ASSERT(tracer->eagerlyTraceWeakMaps() != DoNotTraceWeakMaps);
+    for (WeakMapBase *m = c->gcWeakMapList; m; m = m->next) {
+        m->trace(tracer);
+        if (m->memberOf)
+            gc::MarkObject(tracer, &m->memberOf, "memberOf");
+    }
 }
 
 bool
 WeakMapBase::markCompartmentIteratively(JSCompartment *c, JSTracer *tracer)
 {
     bool markedAny = false;
     for (WeakMapBase *m = c->gcWeakMapList; m; m = m->next) {
         if (m->marked && m->markIteratively(tracer))
--- a/js/src/jsweakmap.h
+++ b/js/src/jsweakmap.h
@@ -86,17 +86,17 @@ class WeakMapBase {
     virtual void nonMarkingTraceValues(JSTracer *tracer) = 0;
     virtual bool markIteratively(JSTracer *tracer) = 0;
     virtual bool findZoneEdges() = 0;
     virtual void sweep() = 0;
     virtual void traceMappings(WeakMapTracer *tracer) = 0;
     virtual void finish() = 0;
 
     // Object that this weak map is part of, if any.
-    JSObject *memberOf;
+    HeapPtrObject memberOf;
 
     // Compartment that this weak map is part of.
     JSCompartment *compartment;
 
     // Link in a list of all WeakMaps in a compartment, headed by
     // JSCompartment::gcWeakMapList. The last element of the list has nullptr as
     // its next. Maps not in the list have WeakMapNotInList as their next.
     WeakMapBase *next;
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -767,20 +767,23 @@ RegExpCompartment::sweep(JSRuntime *rt)
         // stack is traced but is not in a zone being collected.
         //
         // Because of this we only treat the marked_ bit as a hint, and destroy
         // the RegExpShared if it was accidentally marked earlier but wasn't
         // marked by the current trace.
         bool keep = shared->marked() && !IsStringAboutToBeFinalized(shared->source.unsafeGet());
         for (size_t i = 0; i < ArrayLength(shared->compilationArray); i++) {
             RegExpShared::RegExpCompilation &compilation = shared->compilationArray[i];
-            if (keep && compilation.jitCode)
-                keep = !IsJitCodeAboutToBeFinalized(compilation.jitCode.unsafeGet());
+            if (compilation.jitCode &&
+                IsJitCodeAboutToBeFinalized(compilation.jitCode.unsafeGet()))
+            {
+                keep = false;
+            }
         }
-        if (keep) {
+        if (keep || rt->isHeapCompacting()) {
             shared->clearMarked();
         } else {
             js_delete(shared);
             e.removeFront();
         }
     }
 
     if (matchResultTemplateObject_ &&
--- a/js/src/vm/RegExpObject.h
+++ b/js/src/vm/RegExpObject.h
@@ -194,17 +194,17 @@ class RegExpShared
     bool isCompiled() const {
         return isCompiled(Normal, true) || isCompiled(Normal, false)
             || isCompiled(MatchOnly, true) || isCompiled(MatchOnly, false);
     }
 
     void trace(JSTracer *trc);
 
     bool marked() const { return marked_; }
-    void clearMarked() { JS_ASSERT(marked_); marked_ = false; }
+    void clearMarked() { marked_ = false; }
 
     size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
 };
 
 /*
  * Extend the lifetime of a given RegExpShared to at least the lifetime of
  * the guard object. See Regular Expression comment at the top.
  */
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -892,19 +892,16 @@ static const JSFunctionSpec intrinsic_fu
               intrinsic_TypeDescrIsSizedArrayType,
               &js::TypeDescrIsSizedArrayTypeJitInfo, 1, 0),
     JS_FNINFO("TypeDescrIsSimpleType",
               intrinsic_TypeDescrIsSimpleType,
               &js::TypeDescrIsSimpleTypeJitInfo, 1, 0),
     JS_FNINFO("ClampToUint8",
               JSNativeThreadSafeWrapper<js::ClampToUint8>,
               &js::ClampToUint8JitInfo, 1, 0),
-    JS_FNINFO("Memcpy",
-              JSNativeThreadSafeWrapper<js::Memcpy>,
-              &js::MemcpyJitInfo, 5, 0),
     JS_FN("GetTypedObjectModule", js::GetTypedObjectModule, 0, 0),
     JS_FN("GetFloat32x4TypeDescr", js::GetFloat32x4TypeDescr, 0, 0),
     JS_FN("GetInt32x4TypeDescr", js::GetInt32x4TypeDescr, 0, 0),
 
 #define LOAD_AND_STORE_SCALAR_FN_DECLS(_constant, _type, _name)               \
     JS_FNINFO("Store_" #_name,                                                \
               JSNativeThreadSafeWrapper<js::StoreScalar##_type::Func>,        \
               &js::StoreScalar##_type::JitInfo, 3, 0),                        \
--- a/js/xpconnect/public/SandboxPrivate.h
+++ b/js/xpconnect/public/SandboxPrivate.h
@@ -44,15 +44,20 @@ public:
         return GetWrapper();
     }
 
     void ForgetGlobalObject()
     {
         ClearWrapper();
     }
 
+    void ObjectMoved(JSObject *obj, const JSObject *old)
+    {
+        UpdateWrapper(obj, old);
+    }
+
 private:
     virtual ~SandboxPrivate() { }
 
     nsCOMPtr<nsIPrincipal> mPrincipal;
 };
 
 #endif // __SANDBOXPRIVATE_H__
--- a/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -305,30 +305,39 @@ sandbox_enumerate(JSContext *cx, HandleO
 static bool
 sandbox_resolve(JSContext *cx, HandleObject obj, HandleId id)
 {
     bool resolved;
     return JS_ResolveStandardClass(cx, obj, id, &resolved);
 }
 
 static void
-sandbox_finalize(JSFreeOp *fop, JSObject *obj)
+sandbox_finalize(js::FreeOp *fop, JSObject *obj)
 {
     nsIScriptObjectPrincipal *sop =
         static_cast<nsIScriptObjectPrincipal *>(xpc_GetJSPrivate(obj));
     if (!sop) {
         // sop can be null if CreateSandboxObject fails in the middle.
         return;
     }
 
     static_cast<SandboxPrivate *>(sop)->ForgetGlobalObject();
     NS_RELEASE(sop);
     DestroyProtoAndIfaceCache(obj);
 }
 
+static void
+sandbox_moved(JSObject *obj, const JSObject *old)
+{
+    nsIScriptObjectPrincipal *sop =
+        static_cast<nsIScriptObjectPrincipal *>(xpc_GetJSPrivate(obj));
+    MOZ_ASSERT(sop);
+    static_cast<SandboxPrivate *>(sop)->ObjectMoved(obj, old);
+}
+
 static bool
 sandbox_convert(JSContext *cx, HandleObject obj, JSType type, MutableHandleValue vp)
 {
     if (type == JSTYPE_OBJECT) {
         vp.set(OBJECT_TO_JSVAL(obj));
         return true;
     }
 
@@ -438,45 +447,65 @@ sandbox_addProperty(JSContext *cx, Handl
                                writeToProto_getProperty, writeToProto_setProperty))
         return false;
 
     return true;
 }
 
 #define XPCONNECT_SANDBOX_CLASS_METADATA_SLOT (XPCONNECT_GLOBAL_EXTRA_SLOT_OFFSET)
 
-static const JSClass SandboxClass = {
+static const js::Class SandboxClass = {
     "Sandbox",
     XPCONNECT_GLOBAL_FLAGS_WITH_EXTRA_SLOTS(1),
     JS_PropertyStub,   JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
     sandbox_enumerate, sandbox_resolve, sandbox_convert,  sandbox_finalize,
-    nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook
+    nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook,
+    JS_NULL_CLASS_SPEC,
+    {
+      nullptr,      /* outerObject */
+      nullptr,      /* innerObject */
+      nullptr,      /* iteratorObject */
+      false,        /* isWrappedNative */
+      nullptr,      /* weakmapKeyDelegateOp */
+      sandbox_moved /* objectMovedOp */
+    },
+    JS_NULL_OBJECT_OPS
 };
 
 // Note to whomever comes here to remove addProperty hooks: billm has promised
 // to do the work for this class.
-static const JSClass SandboxWriteToProtoClass = {
+static const js::Class SandboxWriteToProtoClass = {
     "Sandbox",
     XPCONNECT_GLOBAL_FLAGS_WITH_EXTRA_SLOTS(1),
     sandbox_addProperty,   JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
     sandbox_enumerate, sandbox_resolve, sandbox_convert,  sandbox_finalize,
-    nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook
+    nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook,
+    JS_NULL_CLASS_SPEC,
+    {
+      nullptr,      /* outerObject */
+      nullptr,      /* innerObject */
+      nullptr,      /* iteratorObject */
+      false,        /* isWrappedNative */
+      nullptr,      /* weakmapKeyDelegateOp */
+      sandbox_moved /* objectMovedOp */
+    },
+    JS_NULL_OBJECT_OPS
 };
 
 static const JSFunctionSpec SandboxFunctions[] = {
     JS_FS("dump",    SandboxDump,    1,0),
     JS_FS("debug",   SandboxDebug,   1,0),
     JS_FS("importFunction", SandboxImport, 1,0),
     JS_FS_END
 };
 
 bool
 xpc::IsSandbox(JSObject *obj)
 {
-    const JSClass *clasp = GetObjectJSClass(obj);
+    const Class *clasp = GetObjectClass(obj);
     return clasp == &SandboxClass || clasp == &SandboxWriteToProtoClass;
 }
 
 /***************************************************************************/
 nsXPCComponents_utils_Sandbox::nsXPCComponents_utils_Sandbox()
 {
 }
 
@@ -856,21 +885,21 @@ xpc::CreateSandboxObject(JSContext *cx, 
         NS_ENSURE_TRUE(addonId, NS_ERROR_FAILURE);
     } else if (JSObject *obj = JS::CurrentGlobalOrNull(cx)) {
         if (JSAddonId *id = JS::AddonIdOfObject(obj))
             addonId = id;
     }
 
     compartmentOptions.setAddonId(addonId);
 
-    const JSClass *clasp = options.writeToGlobalPrototype
-                         ? &SandboxWriteToProtoClass
-                         : &SandboxClass;
+    const Class *clasp = options.writeToGlobalPrototype
+                       ? &SandboxWriteToProtoClass
+                       : &SandboxClass;
 
-    RootedObject sandbox(cx, xpc::CreateGlobalObject(cx, clasp,
+    RootedObject sandbox(cx, xpc::CreateGlobalObject(cx, js::Jsvalify(clasp),
                                                      principal, compartmentOptions));
     if (!sandbox)
         return NS_ERROR_FAILURE;
 
     CompartmentPrivate::Get(sandbox)->writeToGlobalPrototype =
       options.writeToGlobalPrototype;
 
     // Set up the wantXrays flag, which indicates whether xrays are desired even
--- a/js/xpconnect/src/XPCWrappedNative.cpp
+++ b/js/xpconnect/src/XPCWrappedNative.cpp
@@ -987,16 +987,30 @@ XPCWrappedNative::FlatJSObjectFinalized(
 
     // Note that it's not safe to touch mNativeWrapper here since it's
     // likely that it has already been finalized.
 
     Release();
 }
 
 void
+XPCWrappedNative::FlatJSObjectMoved(JSObject *obj, const JSObject *old)
+{
+    JS::AutoAssertGCCallback inCallback(obj);
+    MOZ_ASSERT(mFlatJSObject == old);
+
+    nsWrapperCache *cache = nullptr;
+    CallQueryInterface(mIdentity, &cache);
+    if (cache)
+        cache->UpdateWrapper(obj, old);
+
+    mFlatJSObject = obj;
+}
+
+void
 XPCWrappedNative::SystemIsBeingShutDown()
 {
     if (!IsValid())
         return;
 
     // The long standing strategy is to leak some objects still held at shutdown.
     // The general problem is that propagating release out of xpconnect at
     // shutdown time causes a world of problems.
--- a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
@@ -568,16 +568,27 @@ WrappedNativeFinalize(js::FreeOp *fop, J
 
     XPCWrappedNative* wrapper = static_cast<XPCWrappedNative*>(p);
     if (helperType == WN_HELPER)
         wrapper->GetScriptableCallback()->Finalize(wrapper, js::CastToJSFreeOp(fop), obj);
     wrapper->FlatJSObjectFinalized();
 }
 
 static void
+WrappedNativeObjectMoved(JSObject *obj, const JSObject *old)
+{
+    nsISupports* p = static_cast<nsISupports*>(xpc_GetJSPrivate(obj));
+    if (!p)
+        return;
+
+    XPCWrappedNative* wrapper = static_cast<XPCWrappedNative*>(p);
+    wrapper->FlatJSObjectMoved(obj, old);
+}
+
+static void
 XPC_WN_NoHelper_Finalize(js::FreeOp *fop, JSObject *obj)
 {
     WrappedNativeFinalize(fop, obj, WN_NOHELPER);
 }
 
 /*
  * General comment about XPConnect tracing: Given a C++ object |wrapper| and its
  * corresponding JS object |obj|, calling |wrapper->TraceSelf| will ask the JS
@@ -653,17 +664,19 @@ const XPCWrappedNativeJSClass XPC_WN_NoH
     XPCWrappedNative::Trace,         // trace
     JS_NULL_CLASS_SPEC,
 
     // ClassExtension
     {
         nullptr, // outerObject
         nullptr, // innerObject
         nullptr, // iteratorObject
-        true,   // isWrappedNative
+        true,    // isWrappedNative
+        nullptr, // weakmapKeyDelegateOp
+        WrappedNativeObjectMoved
     },
 
     // ObjectOps
     {
         nullptr, // lookupGeneric
         nullptr, // lookupProperty
         nullptr, // lookupElement
         nullptr, // defineGeneric
@@ -1160,16 +1173,17 @@ XPCNativeScriptableShared::PopulateJSCla
         mJSClass.base.hasInstance = XPC_WN_Helper_HasInstance;
 
     if (mFlags.IsGlobalObject())
         mJSClass.base.trace = JS_GlobalObjectTraceHook;
     else
         mJSClass.base.trace = XPCWrappedNative::Trace;
 
     mJSClass.base.ext.isWrappedNative = true;
+    mJSClass.base.ext.objectMovedOp = WrappedNativeObjectMoved;
 }
 
 /***************************************************************************/
 /***************************************************************************/
 
 // Compatibility hack.
 //
 // XPConnect used to do all sorts of funny tricks to find the "correct"
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -2136,16 +2136,17 @@ public:
     ReparentWrapperIfFound(XPCWrappedNativeScope* aOldScope,
                            XPCWrappedNativeScope* aNewScope,
                            JS::HandleObject aNewParent,
                            nsISupports* aCOMObj);
 
     nsresult RescueOrphans();
 
     void FlatJSObjectFinalized();
+    void FlatJSObjectMoved(JSObject *obj, const JSObject *old);
 
     void SystemIsBeingShutDown();
 
     enum CallMode {CALL_METHOD, CALL_GETTER, CALL_SETTER};
 
     static bool CallMethod(XPCCallContext& ccx,
                            CallMode mode = CALL_METHOD);
 
--- a/layout/base/nsBidiPresUtils.cpp
+++ b/layout/base/nsBidiPresUtils.cpp
@@ -1397,17 +1397,17 @@ nsBidiPresUtils::RepositionFrame(nsIFram
   // bidiUtils->ReorderFrames, so this is guaranteed to be after the inlines
   // have been reflowed, which is required for GetUsedMargin/Border/Padding
   LogicalMargin margin(aLineWM, aFrame->GetUsedMargin());
   if (isFirst) {
     aStart += margin.IStart(aLineWM);
   }
 
   nscoord start = aStart;
-  nscoord frameWidth = aFrame->GetSize().width;
+  nscoord frameISize = aFrame->ISize(aLineWM);
 
   if (!IsBidiLeaf(aFrame))
   {
     nscoord iCoord = 0;
     LogicalMargin borderPadding(frameWM, aFrame->GetUsedBorderAndPadding());
     if (isFirst) {
       iCoord += borderPadding.IStart(frameWM);
     }
@@ -1431,29 +1431,29 @@ nsBidiPresUtils::RepositionFrame(nsIFram
     // Reposition the child frames
     int32_t index = 0;
     while (frame) {
       RepositionFrame(frame,
                       aIsEvenLevel,
                       iCoord,
                       aContinuationStates,
                       frameWM,
-                      frameWidth);
+                      frameISize);
       index++;
       frame = reverseOrder ?
                 childList[childList.Length() - index - 1] :
                 frame->GetNextSibling();
     }
 
     if (isLast) {
       iCoord += borderPadding.IEnd(frameWM);
     }
     aStart += iCoord;
   } else {
-    aStart += frameWidth;
+    aStart += frameISize;
   }
 
   LogicalRect logicalRect(aLineWM, aFrame->GetRect(), aLineWidth);
   logicalRect.IStart(aLineWM) = start;
   logicalRect.ISize(aLineWM) = aStart - start;
   aFrame->SetRect(aLineWM, logicalRect, aLineWidth);
 
   if (isLast) {
--- a/layout/generic/test/test_backspace_delete.xul
+++ b/layout/generic/test/test_backspace_delete.xul
@@ -28,33 +28,50 @@ function execTests() {
     var n = editor.firstChild;
     if (node) {
       n = node();
     }
     sel.collapse(n, firstChildOffsetForCaret);
   }
 
   var eatSpace;
+  var deleteImmediately;
 
-  function getPrefs() {
+  function getPrefs(branch) {
     const prefSvcContractID = "@mozilla.org/preferences-service;1";
     const prefSvcIID = Components.interfaces.nsIPrefService;
     return Components.classes[prefSvcContractID].getService(prefSvcIID)
-                                                 .getBranch("layout.word_select.");
+                                                 .getBranch(branch);
+  }
+
+  function setPref(branch, pref, newValue) {
+    getPrefs(branch).setBoolPref(pref, newValue);
+    return newValue;
+  }
+
+  function restorePref(branch, pref, newValue) {
+    try {
+      getPrefs(branch).clearUserPref(pref);
+    } catch(ex) {}
   }
 
   function setEatSpace(newValue) {
-    getPrefs().setBoolPref("eat_space_to_next_word", newValue);
-    eatSpace = newValue;
+    eatSpace = setPref("layout.word_select.", "eat_space_to_next_word", newValue);
   }
 
   function restoreEatSpace() {
-    try {
-      getPrefs().clearUserPref("eat_space_to_next_word");
-    } catch(ex) {}
+    restorePref("layout.word_select.", "eat_space_to_next_word");
+  }
+
+  function setDeleteImmediately(newValue) {
+    deleteImmediately = setPref("bidi.edit.", "delete_immediately", newValue);
+  }
+
+  function restoreDeleteImmediately() {
+    restorePref("bidi.edit.", "delete_immediately");
   }
 
   function doCommand(cmd) {
     var controller = document.commandDispatcher.getControllerForCommand(cmd);
     if (controller) {
       try {
         controller.doCommand(cmd);
         ok(true, 'doCommand(' + cmd + ') succeeded');
@@ -81,30 +98,30 @@ function execTests() {
     is(selRange.startContainer, startNode, selErrString("Word right"));
     is(selRange.startOffset, startOffset, selErrString("Word right"));
     is(selRange.endContainer, endNode, selErrString("Word right"));
     is(selRange.endOffset, endOffset, selErrString("Word right"));
   }
 
   function testDelete(node, offset, text, richtext) {
     doCommand("cmd_deleteCharForward");
-    var msg = "Delete broken in \"" + editor.innerHTML + "\", offset " + offset;
+    var msg = "Delete broken in \"" + editor.innerHTML + "\", offset " + offset + " with deleteImmediately=" + deleteImmediately;
     if(typeof node == 'function'){
       node = node();
     }
     is(sel.anchorNode, node, msg);
 
     is(sel.anchorOffset, offset, msg);
     let text_result = richtext ? editor.innerHTML : editor.textContent;
     is(text_result, text, msg);
   }
 
   function testBackspace(node, offset, text) {
     doCommand("cmd_deleteCharBackward");
-    var msg = "Backspace broken in \"" + editor.innerHTML + "\", offset " + offset;
+    var msg = "Backspace broken in \"" + editor.innerHTML + "\", offset " + offset + " with deleteImmediately=" + deleteImmediately;
     is(sel.anchorNode, node, msg);
 
     is(sel.anchorOffset, offset, msg);
     is(editor.textContent, text, msg);
   }
 
   function testDeletePrevWord(node, offset, text) {
     doCommand("cmd_deleteWordBackward");
@@ -190,20 +207,48 @@ function execTests() {
 
   // Tests for Bug 419217
 
   setupTest("foo<div>bar</div>", 3);
   testDelete(function(){return editor.firstChild;}, 3, "foobar", true);
 
   // Tests for Bug 419406
   var s = "helloשלום";
+
+  setDeleteImmediately(true);
+
+  setupTest(s, 4);
+  testRight(editor.firstChild, 5);
+  testDelete(editor.firstChild, 5, "helloלום");
+
+  setDeleteImmediately(false);
+
   setupTest(s, 4);
   testRight(editor.firstChild, 5);
   testDelete(editor.firstChild, 5, "helloשלום");
 
+  // Tests for bug 1034337
+  s = "اهلاhello";
+
+  setDeleteImmediately(true);
+
+  setupTest(s, 4);
+  // first delete an ltr character to make sure that the caret is ltr
+  testDelete(editor.firstChild, 4, "اهلاello");
+  testBackspace(editor.firstChild, 3, "اهلello");
+
+  setDeleteImmediately(false);
+
+  setupTest(s, 4);
+  // first delete an ltr character to make sure that the caret is ltr
+  testDelete(editor.firstChild, 4, "اهلاello");
+  testBackspace(editor.firstChild, 4, "اهلاello");
+
+  restoreDeleteImmediately();
+
   // Tests for Bug 462188
   setupTest("You should not see this text.", 29);
   testDeletePrevWord(editor.firstChild, 24, "You should not see this ");
   testDeletePrevWord(editor.firstChild, 19, "You should not see ");
   testDeletePrevWord(editor.firstChild, 15, "You should not ");
   testDeletePrevWord(editor.firstChild, 11, "You should ");
   testDeletePrevWord(editor.firstChild,  4, "You ");
   testDeletePrevWord(editor,  0, "");
--- a/mfbt/tests/TestJSONWriter.cpp
+++ b/mfbt/tests/TestJSONWriter.cpp
@@ -289,26 +289,28 @@ void TestBasicElements()
   w.EndArray();
   w.End();
 
   Check(w.WriteFunc(), expected);
 }
 
 void TestStringEscaping()
 {
+  // This test uses hexadecimal character escapes because UTF8 literals cause
+  // problems for some compilers (see bug 1069726).
   const char* expected = "\
 {\n\
- \"ascii\": \"~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#\\\"! \\u001f\\u001e\\u001d\\u001c\\u001b\\u001a\\u0019\\u0018\\u0017\\u0016\\u0015\\u0014\\u0013\\u0012\\u0011\\u0010\\u000f\\u000e\\r\\f\\u000b\\n\\t\\b\\u0007\\u0006\\u0005\\u0004\\u0003\\u0002\\u0001\",\n\
- \"مرحبا هناك\": true,\n\
- \"բարեւ չկա\": -123,\n\
- \"你好\": 1.234,\n\
- \"γεια εκεί\": \"سلام\",\n\
- \"halló þarna\": \"0x1234\",\n\
- \"こんにちは\": {\n\
-  \"привет\": [\n\
+ \"ascii\": \"\x7F~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#\\\"! \\u001f\\u001e\\u001d\\u001c\\u001b\\u001a\\u0019\\u0018\\u0017\\u0016\\u0015\\u0014\\u0013\\u0012\\u0011\\u0010\\u000f\\u000e\\r\\f\\u000b\\n\\t\\b\\u0007\\u0006\\u0005\\u0004\\u0003\\u0002\\u0001\",\n\
+ \"\xD9\x85\xD8\xB1\xD8\xAD\xD8\xA8\xD8\xA7 \xD9\x87\xD9\x86\xD8\xA7\xD9\x83\": true,\n\
+ \"\xD5\xA2\xD5\xA1\xD6\x80\xD5\xA5\xD6\x82 \xD5\xB9\xD5\xAF\xD5\xA1\": -123,\n\
+ \"\xE4\xBD\xA0\xE5\xA5\xBD\": 1.234,\n\
+ \"\xCE\xB3\xCE\xB5\xCE\xB9\xCE\xB1 \xCE\xB5\xCE\xBA\xCE\xB5\xCE\xAF\": \"\xD8\xB3\xD9\x84\xD8\xA7\xD9\x85\",\n\
+ \"hall\xC3\xB3 \xC3\xBE" "arna\": \"0x1234\",\n\
+ \"\xE3\x81\x93\xE3\x82\x93\xE3\x81\xAB\xE3\x81\xA1\xE3\x81\xAF\": {\n\
+  \"\xD0\xBF\xD1\x80\xD0\xB8\xD0\xB2\xD0\xB5\xD1\x82\": [\n\
   ]\n\
  }\n\
 }\n\
 ";
 
   JSONWriter w(MakeUnique<StringWriteFunc>());
 
   // Test the string escaping behaviour.
@@ -318,24 +320,24 @@ void TestStringEscaping()
     // at the end serves as the null char.
     char buf[128];
     for (int i = 0; i < 128; i++) {
       buf[i] = 127 - i;
     }
     w.StringProperty("ascii", buf);
 
     // Test lots of unicode stuff. Note that this file is encoded as UTF-8.
-    w.BoolProperty("مرحبا هناك", true);
-    w.IntProperty("բարեւ չկա", -123);
-    w.DoubleProperty("你好", 1.234);
-    w.StringProperty("γεια εκεί", "سلام");
-    w.PointerProperty("halló þarna", (void*)0x1234);
-    w.StartObjectProperty("こんにちは");
+    w.BoolProperty("\xD9\x85\xD8\xB1\xD8\xAD\xD8\xA8\xD8\xA7 \xD9\x87\xD9\x86\xD8\xA7\xD9\x83", true);
+    w.IntProperty("\xD5\xA2\xD5\xA1\xD6\x80\xD5\xA5\xD6\x82 \xD5\xB9\xD5\xAF\xD5\xA1", -123);
+    w.DoubleProperty("\xE4\xBD\xA0\xE5\xA5\xBD", 1.234);
+    w.StringProperty("\xCE\xB3\xCE\xB5\xCE\xB9\xCE\xB1 \xCE\xB5\xCE\xBA\xCE\xB5\xCE\xAF", "\xD8\xB3\xD9\x84\xD8\xA7\xD9\x85");
+    w.PointerProperty("hall\xC3\xB3 \xC3\xBE" "arna", (void*)0x1234);
+    w.StartObjectProperty("\xE3\x81\x93\xE3\x82\x93\xE3\x81\xAB\xE3\x81\xA1\xE3\x81\xAF");
     {
-      w.StartArrayProperty("привет");
+      w.StartArrayProperty("\xD0\xBF\xD1\x80\xD0\xB8\xD0\xB2\xD0\xB5\xD1\x82");
       w.EndArray();
     }
     w.EndObject();
   }
   w.End();
 
   Check(w.WriteFunc(), expected);
 }
--- a/modules/libjar/nsJARChannel.cpp
+++ b/modules/libjar/nsJARChannel.cpp
@@ -314,17 +314,17 @@ nsJARChannel::CreateJarInput(nsIZipReade
     // Make GetContentLength meaningful
     mContentLength = input->GetContentLength();
 
     input.forget(resultInput);
     return NS_OK;
 }
 
 nsresult
-nsJARChannel::LookupFile()
+nsJARChannel::LookupFile(bool aAllowAsync)
 {
     LOG(("nsJARChannel::LookupFile [this=%x %s]\n", this, mSpec.get()));
 
     if (mJarFile)
         return NS_OK;
 
     nsresult rv;
     nsCOMPtr<nsIURI> uri;
@@ -382,16 +382,21 @@ nsJARChannel::LookupFile()
                     jarCache->GetFd(mJarFile, &fd);
                     if (fd) {
                         return SetRemoteNSPRFileDesc(fd);
                     }
                     #endif
                 }
             }
 
+            if (!aAllowAsync) {
+                mJarFile = nullptr;
+                return NS_OK;
+            }
+
             mOpeningRemote = true;
 
             #if defined(XP_WIN) || defined(MOZ_WIDGET_COCOA)
             #else
             if (mEnsureChildFd && jarCache) {
                 jarCache->SetMustCacheFd(remoteFile, true);
             }
             #endif
@@ -793,17 +798,17 @@ nsJARChannel::Open(nsIInputStream **stre
     LOG(("nsJARChannel::Open [this=%x]\n", this));
 
     NS_ENSURE_TRUE(!mOpened, NS_ERROR_IN_PROGRESS);
     NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
 
     mJarFile = nullptr;
     mIsUnsafe = true;
 
-    nsresult rv = LookupFile();
+    nsresult rv = LookupFile(false);
     if (NS_FAILED(rv))
         return rv;
 
     // If mJarInput was not set by LookupFile, the JAR is a remote jar.
     if (!mJarFile) {
         NS_NOTREACHED("need sync downloader");
         return NS_ERROR_NOT_IMPLEMENTED;
     }
@@ -830,17 +835,17 @@ nsJARChannel::AsyncOpen(nsIStreamListene
     NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
 
     mJarFile = nullptr;
     mIsUnsafe = true;
 
     // Initialize mProgressSink
     NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, mProgressSink);
 
-    nsresult rv = LookupFile();
+    nsresult rv = LookupFile(true);
     if (NS_FAILED(rv))
         return rv;
 
     // These variables must only be set if we're going to trigger an
     // OnStartRequest, either from AsyncRead or OnDownloadComplete.
     // 
     // That means: Do not add early return statements beyond this point!
     mListener = listener;
@@ -905,17 +910,17 @@ nsJARChannel::GetJarFile(nsIFile **aFile
 {
     NS_IF_ADDREF(*aFile = mJarFile);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsJARChannel::GetZipEntry(nsIZipEntry **aZipEntry)
 {
-    nsresult rv = LookupFile();
+    nsresult rv = LookupFile(false);
     if (NS_FAILED(rv))
         return rv;
 
     if (!mJarFile)
         return NS_ERROR_NOT_AVAILABLE;
 
     nsCOMPtr<nsIZipReader> reader;
     rv = gJarHandler->JarCache()->GetZip(mJarFile, getter_AddRefs(reader));
--- a/modules/libjar/nsJARChannel.h
+++ b/modules/libjar/nsJARChannel.h
@@ -53,17 +53,17 @@ public:
     nsJARChannel();
 
     nsresult Init(nsIURI *uri);
 
 private:
     virtual ~nsJARChannel();
 
     nsresult CreateJarInput(nsIZipReaderCache *, nsJARInputThunk **);
-    nsresult LookupFile();
+    nsresult LookupFile(bool aAllowAsync);
     nsresult OpenLocalFile();
     void NotifyError(nsresult aError);
     void FireOnProgress(uint64_t aProgress);
     nsresult SetRemoteNSPRFileDesc(PRFileDesc *fd);
 
 #if defined(PR_LOGGING)
     nsCString                       mSpec;
 #endif
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1919,17 +1919,17 @@ pref("bidi.numeral", 0);
 // ------------------
 // 1 = mozillaBidisupport *
 // 2 = OsBidisupport
 // 3 = disableBidisupport
 pref("bidi.support", 1);
 // Whether delete and backspace should immediately delete characters not
 // visually adjacent to the caret, or adjust the visual position of the caret
 // on the first keypress and delete the character on a second keypress
-pref("bidi.edit.delete_immediately", false);
+pref("bidi.edit.delete_immediately", true);
 
 // Bidi caret movement style:
 // 0 = logical
 // 1 = visual
 // 2 = visual, but logical during selection
 pref("bidi.edit.caret_movement_style", 2);
 
 // Setting this pref to |true| forces Bidi UI menu items and keyboard shortcuts
--- a/testing/marionette/client/marionette/marionette.py
+++ b/testing/marionette/client/marionette/marionette.py
@@ -1555,8 +1555,15 @@ class Marionette(object):
         An error will be returned if the requested window size would result
         in the window being in the maximised state.
 
         :param width: The width to resize the window to.
         :param height: The height to resize the window to.
 
         """
         self._send_message("setWindowSize", "ok", width=width, height=height)
+
+    def maximize_window(self):
+        """ Resize the browser window currently receiving commands. The action
+        should be equivalent to the user pressing the the maximize button
+        """
+
+        return self._send_message("maximizeWindow", "ok")
--- a/testing/marionette/client/marionette/tests/unit/test_set_window_size.py
+++ b/testing/marionette/client/marionette/tests/unit/test_set_window_size.py
@@ -48,8 +48,28 @@ class TestSetWindowSize(MarionetteTestCa
         height = self.max_height - 100
         self.marionette.set_window_size(width, height)
         # invalid size (cannot maximize)
         with self.assertRaisesRegexp(MarionetteException, "Invalid requested size"):
             self.marionette.set_window_size(self.max_width, self.max_height)
         size = self.marionette.window_size
         self.assertEqual(size['width'], width, "Window width should not have changed")
         self.assertEqual(size['height'], height, "Window height should not have changed")
+
+    def test_that_we_can_maximise_the_window(self):
+        # valid size
+        width = self.max_width - 100
+        height = self.max_height - 100
+        self.marionette.set_window_size(width, height)
+
+        # event handler
+        self.marionette.execute_script("""
+        window.wrappedJSObject.rcvd_event = false;
+        window.onresize = function() {
+            window.wrappedJSObject.rcvd_event = true;
+        };
+        """)
+        self.marionette.maximize_window()
+        self.wait_for_condition(lambda m: m.execute_script("return window.wrappedJSObject.rcvd_event;"))
+
+        size = self.marionette.window_size
+        self.assertEqual(size['width'], self.max_width, "Window width does not use availWidth")
+        self.assertEqual(size['height'], self.max_height, "Window height does not use availHeight")
--- a/testing/marionette/marionette-server.js
+++ b/testing/marionette/marionette-server.js
@@ -2440,16 +2440,35 @@ MarionetteServerConnection.prototype = {
       return;
     }
 
     curWindow.resizeTo(width, height);
     this.sendOk(this.command_id);
   },
 
   /**
+   * Maximizes the Browser Window as if the user pressed the maximise button
+   *
+   * Not Supported on B2G or Fennec
+   */
+  maximizeWindow: function MDA_maximizeWindow (aRequest) {
+    this.command_id = this.getCommandId();
+
+    if (appName !== "Firefox") {
+      this.sendError("Not supported for mobile", 405, null, this.command_id);
+      return;
+    }
+
+    let curWindow = this.getCurrentWindow();
+    curWindow.moveTo(0,0);
+    curWindow.resizeTo(curWindow.screen.availWidth, curWindow.screen.availHeight);
+    this.sendOk(this.command_id);
+  },
+
+  /**
    * Helper function to convert an outerWindowID into a UID that Marionette
    * tracks.
    */
   generateFrameId: function MDA_generateFrameId(id) {
     let uid = id + (appName == "B2G" ? "-b2g" : "");
     return uid;
   },
 
@@ -2648,17 +2667,18 @@ MarionetteServerConnection.prototype.req
   "getCookies": MarionetteServerConnection.prototype.getCookies,
   "getAllCookies": MarionetteServerConnection.prototype.getCookies,  // deprecated
   "deleteAllCookies": MarionetteServerConnection.prototype.deleteAllCookies,
   "deleteCookie": MarionetteServerConnection.prototype.deleteCookie,
   "getActiveElement": MarionetteServerConnection.prototype.getActiveElement,
   "getScreenOrientation": MarionetteServerConnection.prototype.getScreenOrientation,
   "setScreenOrientation": MarionetteServerConnection.prototype.setScreenOrientation,
   "getWindowSize": MarionetteServerConnection.prototype.getWindowSize,
-  "setWindowSize": MarionetteServerConnection.prototype.setWindowSize
+  "setWindowSize": MarionetteServerConnection.prototype.setWindowSize,
+  "maximizeWindow": MarionetteServerConnection.prototype.maximizeWindow
 };
 
 /**
  * Creates a BrowserObj. BrowserObjs handle interactions with the
  * browser, according to the current environment (desktop, b2g, etc.)
  *
  * @param nsIDOMWindow win
  *        The window whose browser needs to be accessed
--- a/toolkit/content/tests/chrome/findbar_window.xul
+++ b/toolkit/content/tests/chrome/findbar_window.xul
@@ -387,34 +387,16 @@
          "testQuickFindText: find field is not focused");
 
       enterStringIntoFindField(SEARCH_TEXT);
       ok(gBrowser.contentWindow.getSelection() == SEARCH_TEXT,
          "testQuickFindText: failed to find '" + SEARCH_TEXT + "'");
       testClipboardSearchString(SEARCH_TEXT);
     }
 
-    // Perform an async function in serial on each of the list items.
-    function asyncForEach(list, async, callback) {
-      let i = 0;
-      let len = list.length;
-
-      if (!len)
-        return callback();
-
-      async(list[i], function handler() {
-          i++;
-          if (i < len) {
-            async(list[i], handler, i);
-          } else {
-            callback();
-          }
-      }, i);
-    }
-
     function testFindCountUI(callback) {
       clearFocus();
       document.getElementById("cmd_find").doCommand();
 
       ok(!gFindBar.hidden, "testFindCountUI: failed to open findbar");
       ok(document.commandDispatcher.focusedElement == gFindBar._findField.inputField,
          "testFindCountUI: find field is not focused");
 
@@ -439,56 +421,55 @@
         text: "texxx",
         current: 0,
         total: 0
       }];
       let regex = /([\d]*)\sof\s([\d]*)/;
       let timeout = gFindBar._matchesCountTimeoutLength + 20;
 
       function assertMatches(aTest, aMatches) {
-        window.opener.wrappedJSObject.SimpleTest.is(aTest.current, aMatches[1],
+        window.opener.wrappedJSObject.SimpleTest.is(aMatches[1], aTest.current,
           "Currently highlighted match should be at " + aTest.current);
-        window.opener.wrappedJSObject.SimpleTest.is(aTest.total, aMatches[2],
+        window.opener.wrappedJSObject.SimpleTest.is(aMatches[2], aTest.total,
           "Total amount of matches should be " + aTest.total);
       }
 
-      function testString(aTest, aNext) {
-        gFindBar.clear();
-        enterStringIntoFindField(aTest.text);
-
-        setTimeout(function() {
+      function* generatorTest() {
+        for (let test of tests) {
+          gFindBar.clear();
+          yield;
+          enterStringIntoFindField(test.text);
+          yield;
           let matches = foundMatches.value.match(regex);
-          if (!aTest.total) {
+          if (!test.total) {
             ok(!matches, "No message should be shown when 0 matches are expected");
-            aNext();
           } else {
-            assertMatches(aTest, matches);
-            let cycleTests = [];
-            let cycles = aTest.total;
-            while (--cycles) {
-              aTest.current++;
-              if (aTest.current > aTest.total)
-                aTest.current = 1;
-              cycleTests.push({
-                current: aTest.current,
-                total: aTest.total
-              });
+            assertMatches(test, matches);
+            for (let i = 1; i < test.total; i++) {
+              gFindBar.onFindAgainCommand();
+              yield;
+              // test.current + 1, test.current + 2, ..., test.total, 1, ..., test.current
+              let current = (test.current + i - 1) % test.total + 1;
+              assertMatches({
+                current: current,
+                total: test.total
+              }, foundMatches.value.match(regex));
             }
-            asyncForEach(cycleTests, function(aCycleTest, aNextCycle) {
-              gFindBar.onFindAgainCommand();
-              setTimeout(function() {
-                assertMatches(aCycleTest, foundMatches.value.match(regex));
-                aNextCycle();
-              }, timeout);
-            }, aNext);
           }
-        }, timeout);
+        }
+        callback();
       }
-
-      asyncForEach(tests, testString, callback);
+      let test = generatorTest();
+      let resultListener = {
+        onMatchesCountResult: function() {
+          test.next();
+        }
+      };
+      gFindBar.browser.finder.addResultListener(resultListener);
+      test.next();
     }
 
     function testClipboardSearchString(aExpected) {
       if (!gHasFindClipboard)
         return;
 
       if (!aExpected)
         aExpected = "";
--- a/toolkit/content/widgets/autocomplete.xml
+++ b/toolkit/content/widgets/autocomplete.xml
@@ -1214,17 +1214,17 @@ extends="chrome://global/content/binding
         </xul:hbox>
         <xul:label anonid="title-overflow-ellipsis" xbl:inherits="selected"
                    class="ac-ellipsis-after ac-comment"/>
         <xul:hbox anonid="extra-box" class="ac-extra" align="center" hidden="true">
           <xul:image class="ac-result-type-tag"/>
           <xul:label class="ac-normal-text ac-comment" xbl:inherits="selected" value=":"/>
           <xul:description anonid="extra" class="ac-normal-text ac-comment" xbl:inherits="selected"/>
         </xul:hbox>
-        <xul:image anonid="type-image" class="ac-type-icon"/>
+        <xul:image anonid="type-image" class="ac-type-icon" xbl:inherits="selected"/>
       </xul:hbox>
       <xul:hbox align="center" class="ac-url-box">
         <xul:spacer class="ac-site-icon"/>
         <xul:image class="ac-action-icon"/>
         <xul:hbox anonid="url-box" class="ac-url" flex="1"
                   onunderflow="_doUnderflow('_url');"
                   onoverflow="_doOverflow('_url');">
           <xul:description anonid="url" class="ac-normal-text ac-url-text"
@@ -1514,39 +1514,47 @@ extends="chrome://global/content/binding
             // Each tag is split by a comma in an undefined order, so sort it
             let sortedTags = tags.split(",").sort().join(", ");
 
             // Emphasize the matching text in the tags
             this._setUpDescription(this._extra, sortedTags);
 
             // Treat tagged matches as bookmarks for the star
             type = "bookmark";
-          } else if (type == "keyword") {
+          // keyword and favicon type results for search engines
+          // have an extra magnifying glass icon after them
+          } else if (type == "keyword" || type == "search favicon") {
             // Configure the extra box for keyword display
             this._extraBox.hidden = false;
             this._extraBox.childNodes[0].hidden = true;
-            this._extraBox.childNodes[1].hidden = false;
+            // The second child node is ":" and it should be hidden for non keyword types
+            this._extraBox.childNodes[1].hidden = type == "keyword" ? false : true;
             this._extraBox.pack = "start";
             this._titleBox.flex = 0;
 
             // Hide the ellipsis so it doesn't take up space.
             this._titleOverflowEllipsis.hidden = true;
 
-            // Put the parameters next to the title if we have any
-            let search = this.getAttribute("text");
-            let params = "";
-            let paramsIndex = search.indexOf(' ');
-            if (paramsIndex != -1)
-              params = search.substr(paramsIndex + 1);
+            if (type == "keyword") {
+              // Put the parameters next to the title if we have any
+              let search = this.getAttribute("text");
+              let params = "";
+              let paramsIndex = search.indexOf(" ");
+              if (paramsIndex != -1)
+                params = search.substr(paramsIndex + 1);
 
-            // Emphasize the keyword parameters
-            this._setUpDescription(this._extra, params);
+              // Emphasize the keyword parameters
+              this._setUpDescription(this._extra, params);
 
-            // Don't emphasize keyword searches in the title or url
-            this.setAttribute("text", "");
+              // Don't emphasize keyword searches in the title or url
+              this.setAttribute("text", "");
+            }
+            // If the result has the type favicon and a known search provider,
+            // customize it the same way as a keyword result.
+            type = "keyword";
           }
 
           // Give the image the icon style and a special one for the type
           this._typeImage.className = "ac-type-icon" +
             (type ? " ac-result-type-" + type : "");
 
           // Show the url as the title if we don't have a title
           if (title == "")
--- a/toolkit/content/widgets/findbar.xml
+++ b/toolkit/content/widgets/findbar.xml
@@ -443,50 +443,34 @@
           if (!this._pluralForm) {
             this._pluralForm = Components.utils.import(
                                "resource://gre/modules/PluralForm.jsm", {}).PluralForm;
           }
           return this._pluralForm;
         ]]></getter>
       </property>
 
-      <method name="_updateMatchesCountWorker">
-        <parameter name="aRes"/>
-        <body><![CDATA[
-          let word = this._findField.value;
-          if (aRes == this.nsITypeAheadFind.FIND_NOTFOUND || !word) {
-            this._foundMatches.hidden = true;
-            this._foundMatches.value = "";
-          } else {
-            let matchesCount = this.browser.finder.requestMatchesCount(
-              word, this._matchesCountLimit, this._findMode == this.FIND_LINKS);
-            window.clearTimeout(this._updateMatchesCountTimeout);
-            this._updateMatchesCountTimeout = null;
-          }
-        ]]></body>
-      </method>
-
       <!--
         - Updates the search match count after each find operation on a new string.
         - @param aRes
         -        the result of the find operation
         -->
       <method name="_updateMatchesCount">
-        <parameter name="aRes"/>
         <body><![CDATA[
           if (this._matchesCountLimit == 0 || !this._dispatchFindEvent("matchescount"))
             return;
 
           if (this._updateMatchesCountTimeout) {
             window.clearTimeout(this._updateMatchesCountTimeout);
-            this._updateMatchesCountTimeout = null;
           }
           this._updateMatchesCountTimeout =
-            window.setTimeout(() => this._updateMatchesCountWorker(aRes),
-                              this._matchesCountTimeoutLength);
+            window.setTimeout(() => {
+              this.browser.finder.requestMatchesCount(this._findField.value, this._matchesCountLimit,
+                  this._findMode == this.FIND_LINKS);
+            }, this._matchesCountTimeoutLength);
         ]]></body>
       </method>
 
       <!--
         - Turns highlight on or off.
         - @param aHighlight (boolean)
         -        Whether to turn the highlight on or off
         -->
--- a/toolkit/content/widgets/menulist.xml
+++ b/toolkit/content/widgets/menulist.xml
@@ -588,26 +588,16 @@
               this.open = true;
             }
           }
         ]]>
       </handler>
     </handlers>
   </binding>
 
-  <binding id="menulist-compact" display="xul:menu"
-           extends="chrome://global/content/bindings/menulist.xml#menulist">
-    <content sizetopopup="false">
-      <xul:dropmarker class="menulist-dropmarker" type="menu" xbl:inherits="disabled,open"/>
-      <xul:image class="menulist-icon" xbl:inherits="src=image,src"/>
-      <xul:label class="menulist-label" xbl:inherits="value=label,crop,accesskey" crop="right" flex="1"/>
-      <children includes="menupopup"/>
-    </content>
-  </binding>
-
   <binding id="menulist-description" display="xul:menu"
            extends="chrome://global/content/bindings/menulist.xml#menulist">
     <content sizetopopup="pref">
       <xul:hbox class="menulist-label-box" flex="1">
         <xul:image class="menulist-icon" xbl:inherits="src=image,src"/>
         <xul:label class="menulist-label" xbl:inherits="value=label,crop,accesskey" crop="right" flex="1"/>
         <xul:label class="menulist-label menulist-description" xbl:inherits="value=description" crop="right" flex="10000"/>
       </xul:hbox>
--- a/toolkit/modules/Finder.jsm
+++ b/toolkit/modules/Finder.jsm
@@ -6,27 +6,30 @@ this.EXPORTED_SYMBOLS = ["Finder"];
 
 const Ci = Components.interfaces;
 const Cc = Components.classes;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Geometry.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/Task.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "TextToSubURIService",
                                          "@mozilla.org/intl/texttosuburi;1",
                                          "nsITextToSubURI");
 XPCOMUtils.defineLazyServiceGetter(this, "Clipboard",
                                          "@mozilla.org/widget/clipboard;1",
                                          "nsIClipboard");
 XPCOMUtils.defineLazyServiceGetter(this, "ClipboardHelper",
                                          "@mozilla.org/widget/clipboardhelper;1",
                                          "nsIClipboardHelper");
 
+const kHighlightIterationSizeMax = 100;
+
 function Finder(docShell) {
   this._fastFind = Cc["@mozilla.org/typeaheadfind;1"].createInstance(Ci.nsITypeAheadFind);
   this._fastFind.init(docShell);
 
   this._docShell = docShell;
   this._listeners = [];
   this._previousLink = null;
   this._searchString = null;
@@ -122,42 +125,44 @@ Finder.prototype = {
                                           Ci.nsIClipboard.kFindClipboard,
                                           this._getWindow().document);
   },
 
   set caseSensitive(aSensitive) {
     this._fastFind.caseSensitive = aSensitive;
   },
 
+  _lastFindResult: null,
+
   /**
    * Used for normal search operations, highlights the first match.
    *
    * @param aSearchString String to search for.
    * @param aLinksOnly Only consider nodes that are links for the search.
    * @param aDrawOutline Puts an outline around matched links.
    */
   fastFind: function (aSearchString, aLinksOnly, aDrawOutline) {
-    let result = this._fastFind.find(aSearchString, aLinksOnly);
+    this._lastFindResult = this._fastFind.find(aSearchString, aLinksOnly);
     let searchString = this._fastFind.searchString;
-    this._notify(searchString, result, false, aDrawOutline);
+    this._notify(searchString, this._lastFindResult, false, aDrawOutline);
   },
 
   /**
    * Repeat the previous search. Should only be called after a previous
    * call to Finder.fastFind.
    *
    * @param aFindBackwards Controls the search direction:
    *    true: before current match, false: after current match.
    * @param aLinksOnly Only consider nodes that are links for the search.
    * @param aDrawOutline Puts an outline around matched links.
    */
   findAgain: function (aFindBackwards, aLinksOnly, aDrawOutline) {
-    let result = this._fastFind.findAgain(aFindBackwards, aLinksOnly);
+    this._lastFindResult = this._fastFind.findAgain(aFindBackwards, aLinksOnly);
     let searchString = this._fastFind.searchString;
-    this._notify(searchString, result, aFindBackwards, aDrawOutline);
+    this._notify(searchString, this._lastFindResult, aFindBackwards, aDrawOutline);
   },
 
   /**
    * Forcibly set the search string of the find clipboard to the currently
    * selected text in the window, on supported platforms (i.e. OSX).
    */
   setSearchStringToSelection: function() {
     // Find the selected text.
@@ -169,24 +174,28 @@ Finder.prototype = {
     // Empty strings are rather useless to search for.
     if (!searchString.length)
       return null;
 
     this.clipboardSearchString = searchString;
     return searchString;
   },
 
-  highlight: function (aHighlight, aWord) {
-    let found = this._highlight(aHighlight, aWord, null);
+  highlight: Task.async(function* (aHighlight, aWord) {
+    if (this._abortHighlight) {
+      this._abortHighlight();
+    }
+
+    let found = yield this._highlight(aHighlight, aWord, null);
     if (aHighlight) {
       let result = found ? Ci.nsITypeAheadFind.FIND_FOUND
                          : Ci.nsITypeAheadFind.FIND_NOTFOUND;
       this._notify(aWord, result, false, false, false);
     }
-  },
+  }),
 
   enableSelection: function() {
     this._fastFind.setSelectionModeAndRepaint(Ci.nsISelectionController.SELECTION_ON);
     this._restoreOriginalOutline();
   },
 
   removeSelection: function() {
     this._fastFind.collapseSelection();
@@ -257,38 +266,49 @@ Finder.prototype = {
         controller.scrollLine(false);
         break;
       case Ci.nsIDOMKeyEvent.DOM_VK_DOWN:
         controller.scrollLine(true);
         break;
     }
   },
 
+  _notifyMatchesCount: function(result) {
+    for (let l of this._listeners) {
+      try {
+        l.onMatchesCountResult(result);
+      } catch (ex) {}
+    }
+  },
+
   requestMatchesCount: function(aWord, aMatchLimit, aLinksOnly) {
+    if (this._lastFindResult == Ci.nsITypeAheadFind.FIND_NOTFOUND ||
+        this.searchString == "") {
+      return this._notifyMatchesCount({
+        total: 0,
+        current: 0
+      });
+    }
     let window = this._getWindow();
     let result = this._countMatchesInWindow(aWord, aMatchLimit, aLinksOnly, window);
 
     // Count matches in (i)frames AFTER searching through the main window.
     for (let frame of result._framesToCount) {
       // We've reached our limit; no need to do more work.
       if (result.total == -1 || result.total == aMatchLimit)
         break;
       this._countMatchesInWindow(aWord, aMatchLimit, aLinksOnly, frame, result);
     }
 
     // The `_currentFound` and `_framesToCount` properties are only used for
     // internal bookkeeping between recursive calls.
     delete result._currentFound;
     delete result._framesToCount;
 
-    for (let l of this._listeners) {
-      try {
-        l.onMatchesCountResult(result);
-      } catch (ex) {}
-    }
+    this._notifyMatchesCount(result);
   },
 
   /**
    * Counts the number of matches for the searched word in the passed window's
    * content.
    * @param aWord
    *        the word to search for.
    * @param aMatchLimit
@@ -317,51 +337,47 @@ Finder.prototype = {
       aStats.total = -1;
       return aStats;
     }
 
     this._collectFrames(aWindow, aStats);
 
     let foundRange = this._fastFind.getFoundRange();
 
-    this._findIterator(aWord, aWindow, aRange => {
-      if (!aLinksOnly || this._rangeStartsInLink(aRange)) {
+    for(let range of this._findIterator(aWord, aWindow)) {
+      if (!aLinksOnly || this._rangeStartsInLink(range)) {
         ++aStats.total;
         if (!aStats._currentFound) {
           ++aStats.current;
           aStats._currentFound = (foundRange &&
-            aRange.startContainer == foundRange.startContainer &&
-            aRange.startOffset == foundRange.startOffset &&
-            aRange.endContainer == foundRange.endContainer &&
-            aRange.endOffset == foundRange.endOffset);
+            range.startContainer == foundRange.startContainer &&
+            range.startOffset == foundRange.startOffset &&
+            range.endContainer == foundRange.endContainer &&
+            range.endOffset == foundRange.endOffset);
         }
       }
       if (aStats.total == aMatchLimit) {
         aStats.total = -1;
-        return false;
+        break;
       }
-    });
+    };
 
     return aStats;
   },
 
   /**
-   * Basic wrapper around nsIFind that provides invoking a callback `aOnFind`
-   * each time an occurence of `aWord` string is found.
+   * Basic wrapper around nsIFind that provides a generator yielding
+   * a range each time an occurence of `aWord` string is found.
    *
    * @param aWord
    *        the word to search for.
    * @param aWindow
    *        the window to search in.
-   * @param aOnFind
-   *        the Function to invoke when a word is found. if Boolean `false` is
-   *        returned, the find operation will be stopped and the Function will
-   *        not be invoked again.
    */
-  _findIterator: function(aWord, aWindow, aOnFind) {
+  _findIterator: function* (aWord, aWindow) {
     let doc = aWindow.document;
     let body = (doc instanceof Ci.nsIDOMHTMLDocument && doc.body) ?
                doc.body : doc.documentElement;
 
     if (!body)
       return;
 
     let searchRange = doc.createRange();
@@ -376,23 +392,44 @@ Finder.prototype = {
     let retRange = null;
 
     let finder = Cc["@mozilla.org/embedcomp/rangefind;1"]
                    .createInstance()
                    .QueryInterface(Ci.nsIFind);
     finder.caseSensitive = this._fastFind.caseSensitive;
 
     while ((retRange = finder.Find(aWord, searchRange, startPt, endPt))) {
-      if (aOnFind(retRange) === false)
-        break;
+      yield retRange;
       startPt = retRange.cloneRange();
       startPt.collapse(false);
     }
   },
 
+  _highlightIterator: Task.async(function* (aWord, aWindow, aOnFind) {
+    let count = 0;
+    for (let range of this._findIterator(aWord, aWindow)) {
+      aOnFind(range);
+      if (++count >= kHighlightIterationSizeMax) {
+          count = 0;
+          yield this._highlightSleep(0);
+      }
+    }
+  }),
+
+  _abortHighlight: null,
+  _highlightSleep: function(delay) {
+    return new Promise((resolve, reject) => {
+      this._abortHighlight = () => {
+        this._abortHighlight = null;
+        reject();
+      };
+      this._getWindow().setTimeout(resolve, delay);
+    });
+  },
+
   /**
    * Helper method for `_countMatchesInWindow` that recursively collects all
    * visible (i)frames inside a window.
    *
    * @param aWindow
    *        the window to extract the (i)frames from.
    * @param aStats
    *        Object that contains a Set called '_framesToCount'
@@ -516,35 +553,35 @@ Finder.prototype = {
     // Removes the outline around the last found link.
     if (this._previousLink) {
       this._previousLink.style.outline = this._tmpOutline;
       this._previousLink.style.outlineOffset = this._tmpOutlineOffset;
       this._previousLink = null;
     }
   },
 
-  _highlight: function (aHighlight, aWord, aWindow) {
+  _highlight: Task.async(function* (aHighlight, aWord, aWindow) {
     let win = aWindow || this._getWindow();
 
     let found = false;
     for (let i = 0; win.frames && i < win.frames.length; i++) {
-      if (this._highlight(aHighlight, aWord, win.frames[i]))
+      if (yield this._highlight(aHighlight, aWord, win.frames[i]))
         found = true;
     }
 
     let controller = this._getSelectionController(win);
     let doc = win.document;
     if (!controller || !doc || !doc.documentElement) {
       // Without the selection controller,
       // we are unable to (un)highlight any matches
       return found;
     }
 
     if (aHighlight) {
-      this._findIterator(aWord, win, aRange => {
+      yield this._highlightIterator(aWord, win, aRange => {
         this._highlightRange(aRange, controller);
         found = true;
       });
     } else {
       // First, attempt to remove highlighting from main document
       let sel = controller.getSelection(Ci.nsISelectionController.SELECTION_FIND);
       sel.removeAllRanges();
 
@@ -562,17 +599,17 @@ Finder.prototype = {
         }
       }
 
       // Removing the highlighting always succeeds, so return true.
       found = true;
     }
 
     return found;
-  },
+  }),
 
   _highlightRange: function(aRange, aController) {
     let node = aRange.startContainer;
     let controller = aController;
 
     let editableNode = this._getEditableNode(node);
     if (editableNode)
       controller = editableNode.editor.selectionController;
new file mode 100644
--- /dev/null
+++ b/toolkit/themes/linux/global/icons/autocomplete-search.svg
@@ -0,0 +1,33 @@
+<?xml version="1.0"?>
+<!-- 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/. -->
+<svg xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink"
+     x="0px" y="0px"
+     viewBox="0 0 16 16"
+     enable-background="new 0 0 16 16"
+     xml:space="preserve">
+<style>
+  use:not(:target) {
+    display: none;
+  }
+
+  use {
+    fill: GrayText;
+  }
+
+  use[id$="-inverted"] {
+    fill: highlighttext;
+  }
+</style>
+<defs style="display:none">
+  <path id="search" fill-rule="evenodd" clip-rule="evenodd" d="M9.356,1.178c-3.014,0-5.458,2.45-5.458,5.472c0,1.086,0.32,2.096,0.864,2.947
+    l-3.279,3.287c-0.396,0.397-0.396,1.041,0,1.438l0.202,0.202c0.396,0.397,1.039,0.397,1.435,0l3.275-3.283
+    c0.854,0.554,1.869,0.88,2.962,0.88c3.014,0,5.458-2.45,5.458-5.471C14.814,3.627,12.371,1.178,9.356,1.178z M9.356,10.001
+    c-1.847,0-3.344-1.501-3.344-3.352c0-1.851,1.497-3.352,3.344-3.352c1.846,0,3.344,1.501,3.344,3.352
+    C12.7,8.501,11.203,10.001,9.356,10.001z"/>
+</defs>
+<use id="search-icon"             xlink:href="#search"/>
+<use id="search-icon-inverted"    xlink:href="#search"/>
+</svg>
--- a/toolkit/themes/linux/global/jar.mn
+++ b/toolkit/themes/linux/global/jar.mn
@@ -34,16 +34,17 @@ toolkit.jar:
 +  skin/classic/global/toolbarbutton.css
 +  skin/classic/global/tree.css
 +  skin/classic/global/alerts/alert.css                        (alerts/alert.css)
 +  skin/classic/global/alerts/notification-48.png              (alerts/notification-48.png)
 +  skin/classic/global/console/console.css                     (console/console.css)
 +  skin/classic/global/console/console.png                     (console/console.png)
 +  skin/classic/global/console/console-toolbar.png             (console/console-toolbar.png)
 +  skin/classic/global/dirListing/remote.png                   (dirListing/remote.png)
++  skin/classic/global/icons/autocomplete-search.svg           (icons/autocomplete-search.svg)
 +  skin/classic/global/icons/Authentication.png                (icons/Authentication.png)
 +  skin/classic/global/icons/autoscroll.png                    (icons/autoscroll.png)
 +  skin/classic/global/icons/blacklist_favicon.png             (icons/blacklist_favicon.png)
 +  skin/classic/global/icons/blacklist_large.png               (icons/blacklist_large.png)
 +  skin/classic/global/icons/close.svg                         (icons/close.svg)
 +  skin/classic/global/icons/find.png                          (icons/find.png)
 +  skin/classic/global/icons/loading_16.png                    (icons/loading_16.png)
 +  skin/classic/global/icons/panelarrow-horizontal.svg         (icons/panelarrow-horizontal.svg)
new file mode 100644
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/autocomplete-search.svg
@@ -0,0 +1,33 @@
+<?xml version="1.0"?>
+<!-- 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/. -->
+<svg xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink"
+     x="0px" y="0px"
+     viewBox="0 0 16 16"
+     enable-background="new 0 0 16 16"
+     xml:space="preserve">
+<style>
+  use:not(:target) {
+    display: none;
+  }
+
+  use {
+    fill: GrayText;
+  }
+
+  use[id$="-inverted"] {
+    fill: highlighttext;
+  }
+</style>
+<defs style="display:none">
+  <path id="search" fill-rule="evenodd" clip-rule="evenodd" d="M14.517,12.884l-3.279-3.287c0.545-0.851,0.864-1.861,0.864-2.947
+    c0-3.022-2.444-5.472-5.458-5.472c-3.014,0-5.458,2.45-5.458,5.472c0,3.022,2.444,5.471,5.458,5.471
+    c1.093,0,2.108-0.325,2.962-0.88l3.275,3.283c0.396,0.397,1.039,0.397,1.435,0l0.202-0.202
+    C14.913,13.925,14.913,13.281,14.517,12.884z M6.644,10.001c-1.846,0-3.344-1.501-3.344-3.352c0-1.851,1.497-3.352,3.344-3.352
+    c1.847,0,3.344,1.501,3.344,3.352C9.987,8.501,8.49,10.001,6.644,10.001z"/>
+</defs>
+<use id="search-icon"             xlink:href="#search"/>
+<use id="search-icon-inverted"    xlink:href="#search"/>
+</svg>
--- a/toolkit/themes/osx/global/jar.mn
+++ b/toolkit/themes/osx/global/jar.mn
@@ -87,16 +87,17 @@ toolkit.jar:
   skin/classic/global/console/console-error-dash.gif                 (console/console-error-dash.gif)
 * skin/classic/global/console/console.css                            (console/console.css)
   skin/classic/global/dirListing/dirListing.css                      (dirListing/dirListing.css)
   skin/classic/global/dirListing/folder.png                          (dirListing/folder.png)
   skin/classic/global/dirListing/local.png                           (dirListing/folder.png)
   skin/classic/global/dirListing/remote.png                          (dirListing/remote.png)
   skin/classic/global/dirListing/up.png                              (dirListing/up.png)
   skin/classic/global/icons/autocomplete-dropmarker.png              (icons/autocomplete-dropmarker.png)
+  skin/classic/global/icons/autocomplete-search.svg                  (icons/autocomplete-search.svg)
   skin/classic/global/icons/autoscroll.png                           (icons/autoscroll.png)
   skin/classic/global/icons/blacklist_favicon.png                    (icons/blacklist_favicon.png)
   skin/classic/global/icons/blacklist_64.png                         (icons/blacklist_64.png)
   skin/classic/global/icons/chevron.png                              (icons/chevron.png)
   skin/classic/global/icons/chevron@2x.png                           (icons/chevron@2x.png)
   skin/classic/global/icons/checkbox.png                             (icons/checkbox.png)
   skin/classic/global/icons/checkbox@2x.png                          (icons/checkbox@2x.png)
   skin/classic/global/icons/close.png                                (icons/close.png)
@@ -137,17 +138,17 @@ toolkit.jar:
   skin/classic/global/icons/question-24.png                          (icons/question-24.png)
   skin/classic/global/icons/question-32.png                          (icons/question-32.png)
   skin/classic/global/icons/question-64.png                          (icons/question-64.png)
   skin/classic/global/icons/question-large.png                       (icons/question-large.png)
   skin/classic/global/icons/sslWarning.png                           (icons/sslWarning.png)
   skin/classic/global/icons/webapps-16.png                           (icons/webapps-16.png)
   skin/classic/global/icons/webapps-16@2x.png                        (icons/webapps-16@2x.png)
   skin/classic/global/icons/webapps-64.png                           (icons/webapps-64.png)
-  skin/classic/global/inContentUI/background-texture.png            (inContentUI/background-texture.png)
+  skin/classic/global/inContentUI/background-texture.png             (inContentUI/background-texture.png)
   skin/classic/global/notification/close.png                         (notification/close.png)
   skin/classic/global/notification/critical-bar-background.png       (notification/critical-bar-background.png)
   skin/classic/global/notification/error-icon.png                    (notification/error-icon.png)
   skin/classic/global/notification/info-bar-background.png           (notification/info-bar-background.png)
   skin/classic/global/notification/info-icon.png                     (notification/info-icon.png)
   skin/classic/global/notification/warning-bar-background.png        (notification/warning-bar-background.png)
   skin/classic/global/notification/warning-icon.png                  (notification/warning-icon.png)
   skin/classic/global/media/TopLevelImageDocument.css                (media/TopLevelImageDocument.css)
--- a/toolkit/themes/windows/global/globalBindings.xml
+++ b/toolkit/themes/windows/global/globalBindings.xml
@@ -47,20 +47,9 @@
            extends="chrome://global/content/bindings/toolbar.xml#toolbarpaletteitem">
     <content>
       <xul:spacer class="spacer-left"/>
       <children/>
       <xul:spacer class="spacer-right"/>
     </content>
   </binding>
 
-  <binding id="menulist-compact" display="xul:menu" 
-           extends="chrome://global/content/bindings/menulist.xml#menulist">
-    <content sizetopopup="false">
-      <xul:hbox class="menulist-label-box" flex="1">
-        <xul:image class="menulist-icon" xbl:inherits="src"/>
-        <xul:label class="menulist-label" xbl:inherits="value=label,crop,accesskey" crop="right" flex="1"/>
-      </xul:hbox>
-      <children includes="menupopup"/>
-    </content>
-  </binding>
-
 </bindings>
new file mode 100644
--- /dev/null
+++ b/toolkit/themes/windows/global/icons/autocomplete-search.svg
@@ -0,0 +1,33 @@
+<?xml version="1.0"?>
+<!-- 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/. -->
+<svg xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink"
+     x="0px" y="0px"
+     viewBox="0 0 16 16"
+     enable-background="new 0 0 16 16"
+     xml:space="preserve">
+<style>
+  use:not(:target) {
+    display: none;
+  }
+
+  use {
+    fill: GrayText;
+  }
+
+  use[id$="-inverted"] {
+    fill: highlighttext;
+  }
+</style>
+<defs style="display:none">
+  <path id="search" fill-rule="evenodd" clip-rule="evenodd" d="M9.356,1.178c-3.014,0-5.458,2.45-5.458,5.472c0,1.086,0.32,2.096,0.864,2.947
+    l-3.279,3.287c-0.396,0.397-0.396,1.041,0,1.438l0.202,0.202c0.396,0.397,1.039,0.397,1.435,0l3.275-3.283
+    c0.854,0.554,1.869,0.88,2.962,0.88c3.014,0,5.458-2.45,5.458-5.471C14.814,3.627,12.371,1.178,9.356,1.178z M9.356,10.001
+    c-1.847,0-3.344-1.501-3.344-3.352c0-1.851,1.497-3.352,3.344-3.352c1.846,0,3.344,1.501,3.344,3.352
+    C12.7,8.501,11.203,10.001,9.356,10.001z"/>
+</defs>
+<use id="search-icon"             xlink:href="#search"/>
+<use id="search-icon-inverted"    xlink:href="#search"/>
+</svg>
--- a/toolkit/themes/windows/global/jar.mn
+++ b/toolkit/themes/windows/global/jar.mn
@@ -94,16 +94,17 @@ toolkit.jar:
         skin/classic/global/console/itemSelected.png             (console/itemSelected.png)
 *       skin/classic/global/dirListing/dirListing.css            (dirListing/dirListing.css)
         skin/classic/global/dirListing/folder.png                (dirListing/folder.png)
         skin/classic/global/dirListing/local.png                 (dirListing/local.png)
         skin/classic/global/dirListing/remote.png                (dirListing/remote.png)
         skin/classic/global/dirListing/up.png                    (dirListing/up.png)
         skin/classic/global/Filepicker.png                       (filepicker/Filepicker.png)
         skin/classic/global/icons/autoscroll.png                 (icons/autoscroll.png)
+        skin/classic/global/icons/autocomplete-search.svg        (icons/autocomplete-search.svg)
         skin/classic/global/icons/blacklist_favicon.png          (icons/blacklist_favicon.png)
         skin/classic/global/icons/blacklist_large.png            (icons/blacklist_large.png)
         skin/classic/global/icons/Close.gif                      (icons/Close.gif)
         skin/classic/global/icons/close.png                      (icons/close.png)
         skin/classic/global/icons/close-lunaBlue.png             (icons/close-lunaBlue.png)
         skin/classic/global/icons/close-lunaOlive.png            (icons/close-lunaOlive.png)
         skin/classic/global/icons/close-lunaSilver.png           (icons/close-lunaSilver.png)
         skin/classic/global/icons/collapse.png                   (icons/collapse.png)
@@ -291,16 +292,17 @@ toolkit.jar:
         skin/classic/aero/global/console/itemSelected.png                (console/itemSelected.png)
 *       skin/classic/aero/global/dirListing/dirListing.css               (dirListing/dirListing.css)
         skin/classic/aero/global/dirListing/folder.png                   (dirListing/folder-aero.png)
         skin/classic/aero/global/dirListing/local.png                    (dirListing/local-aero.png)
         skin/classic/aero/global/dirListing/remote.png                   (dirListing/remote-aero.png)
         skin/classic/aero/global/dirListing/up.png                       (dirListing/up-aero.png)
         skin/classic/aero/global/Filepicker.png                          (filepicker/Filepicker.png)
         skin/classic/aero/global/icons/autoscroll.png                    (icons/autoscroll-aero.png)
+        skin/classic/aero/global/icons/autocomplete-search.svg           (icons/autocomplete-search.svg)
         skin/classic/aero/global/icons/blacklist_favicon.png             (icons/blacklist_favicon-aero.png)
         skin/classic/aero/global/icons/blacklist_large.png               (icons/blacklist_large-aero.png)
         skin/classic/aero/global/icons/Close.gif                         (icons/Close.gif)
         skin/classic/aero/global/icons/close.png                         (icons/close.png)
         skin/classic/aero/global/icons/collapse.png                      (icons/collapse.png)
         skin/classic/aero/global/icons/Error.png                         (icons/Error-aero.png)
         skin/classic/aero/global/icons/error-16.png                      (icons/error-16-aero.png)
         skin/classic/aero/global/icons/error-24.png                      (icons/error-24-aero.png)
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -9,16 +9,17 @@
 #include "nsQAppInstance.h"
 #endif // MOZ_WIDGET_QT
 
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/ContentChild.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Attributes.h"
+#include "mozilla/ChaosMode.h"
 #include "mozilla/IOInterposer.h"
 #include "mozilla/Likely.h"
 #include "mozilla/Poison.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Telemetry.h"
 
 #include "nsAppRunner.h"
 #include "mozilla/AppData.h"
@@ -2982,16 +2983,20 @@ int
 XREMain::XRE_mainInit(bool* aExitFlag)
 {
   if (!aExitFlag)
     return 1;
   *aExitFlag = false;
 
   StartupTimeline::Record(StartupTimeline::MAIN);
 
+  if (ChaosMode::isActive()) {
+    printf_stderr("*** You are running in chaos test mode. See ChaosMode.h. ***\n");
+  }
+
   nsresult rv;
   ArgResult ar;
 
 #ifdef DEBUG
   if (PR_GetEnv("XRE_MAIN_BREAK"))
     NS_BREAK();
 #endif