Merge inbound to m-c.
authorRyan VanderMeulen <ryanvm@gmail.com>
Wed, 23 Apr 2014 13:42:46 -0700
changeset 180185 ed0236a51ed3388068434c58c0f5714a935fb3d2
parent 180184 3cd4615c60ba0f4c0197fcf8691edc8e287a8a45 (current diff)
parent 180166 621bb79845e3519cc24866e4c69ba6103ba4bf6d (diff)
child 180186 b5395baf5883f4c3a3d6276e8fec1cf20db96a0b
push id272
push userpvanderbeken@mozilla.com
push dateMon, 05 May 2014 16:31:18 +0000
milestone31.0a1
Merge inbound to m-c.
content/media/webaudio/test/test_AudioParam.html
js/src/jit/IonFrameIterator-inl.h
js/src/jit/IonFrameIterator.h
layout/mathml/mathfontAsanaMath.properties
layout/mathml/mathfontSTIXSize1.properties
--- a/content/base/src/nsDOMFile.cpp
+++ b/content/base/src/nsDOMFile.cpp
@@ -15,17 +15,16 @@
 #include "nsIClassInfo.h"
 #include "nsIConverterInputStream.h"
 #include "nsIDocument.h"
 #include "nsIFileStreams.h"
 #include "nsIInputStream.h"
 #include "nsIIPCSerializableInputStream.h"
 #include "nsIMemoryReporter.h"
 #include "nsIMIMEService.h"
-#include "nsIPlatformCharset.h"
 #include "nsISeekableStream.h"
 #include "nsIUnicharInputStream.h"
 #include "nsIUnicodeDecoder.h"
 #include "nsNetCID.h"
 #include "nsNetUtil.h"
 #include "nsIUUIDGenerator.h"
 #include "nsHostObjectProtocolHandler.h"
 #include "nsStringStream.h"
--- a/content/media/MediaDecoder.cpp
+++ b/content/media/MediaDecoder.cpp
@@ -43,21 +43,25 @@ static const uint32_t STALL_MS = 3000;
 // Number of estimated seconds worth of data we need to have buffered
 // ahead of the current playback position before we allow the media decoder
 // to report that it can play through the entire media without the decode
 // catching up with the download. Having this margin make the
 // MediaDecoder::CanPlayThrough() calculation more stable in the case of
 // fluctuating bitrates.
 static const int64_t CAN_PLAY_THROUGH_MARGIN = 1;
 
+// avoid redefined macro in unified build
+#undef DECODER_LOG
+
 #ifdef PR_LOGGING
 PRLogModuleInfo* gMediaDecoderLog;
-#define DECODER_LOG(type, msg) PR_LOG(gMediaDecoderLog, type, msg)
+#define DECODER_LOG(type, msg, ...) \
+  PR_LOG(gMediaDecoderLog, type, ("Decoder=%p " msg, this, ##__VA_ARGS__))
 #else
-#define DECODER_LOG(type, msg)
+#define DECODER_LOG(type, msg, ...)
 #endif
 
 class MediaMemoryTracker : public nsIMemoryReporter
 {
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIMEMORYREPORTER
 
   MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf);
@@ -309,18 +313,17 @@ void MediaDecoder::UpdateStreamBlockingF
     }
   }
 }
 
 void MediaDecoder::RecreateDecodedStream(int64_t aStartTimeUSecs)
 {
   MOZ_ASSERT(NS_IsMainThread());
   GetReentrantMonitor().AssertCurrentThreadIn();
-  DECODER_LOG(PR_LOG_DEBUG, ("MediaDecoder::RecreateDecodedStream this=%p aStartTimeUSecs=%lld!",
-                             this, (long long)aStartTimeUSecs));
+  DECODER_LOG(PR_LOG_DEBUG, "RecreateDecodedStream aStartTimeUSecs=%lld!", aStartTimeUSecs);
 
   DestroyDecodedStream();
 
   mDecodedStream = new DecodedStreamData(this, aStartTimeUSecs,
     MediaStreamGraph::GetInstance()->CreateSourceStream(nullptr));
 
   // Note that the delay between removing ports in DestroyDecodedStream
   // and adding new ones won't cause a glitch since all graph operations
@@ -342,18 +345,17 @@ void MediaDecoder::RecreateDecodedStream
     mDecodedStream->mStream->ChangeExplicitBlockerCount(1);
   }
 }
 
 void MediaDecoder::AddOutputStream(ProcessedMediaStream* aStream,
                                    bool aFinishWhenEnded)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  DECODER_LOG(PR_LOG_DEBUG, ("MediaDecoder::AddOutputStream this=%p aStream=%p!",
-                             this, aStream));
+  DECODER_LOG(PR_LOG_DEBUG, "AddOutputStream aStream=%p!", aStream);
 
   {
     ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
     if (!mDecodedStream) {
       RecreateDecodedStream(mDecoderStateMachine ?
           int64_t(mDecoderStateMachine->GetCurrentTime()*USECS_PER_S) : 0);
     }
     OutputStreamData* os = mOutputStreams.AppendElement();
@@ -504,49 +506,49 @@ nsresult MediaDecoder::OpenResource(nsIS
   {
     // Hold the lock while we do this to set proper lock ordering
     // expectations for dynamic deadlock detectors: decoder lock(s)
     // should be grabbed before the cache lock
     ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
 
     nsresult rv = mResource->Open(aStreamListener);
     if (NS_FAILED(rv)) {
-      DECODER_LOG(PR_LOG_DEBUG, ("%p Failed to open stream!", this));
+      DECODER_LOG(PR_LOG_WARNING, "Failed to open stream!");
       return rv;
     }
   }
   return NS_OK;
 }
 
 nsresult MediaDecoder::Load(nsIStreamListener** aStreamListener,
                             MediaDecoder* aCloneDonor)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsresult rv = OpenResource(aStreamListener);
   NS_ENSURE_SUCCESS(rv, rv);
 
   mDecoderStateMachine = CreateStateMachine();
   if (!mDecoderStateMachine) {
-    DECODER_LOG(PR_LOG_DEBUG, ("%p Failed to create state machine!", this));
+    DECODER_LOG(PR_LOG_WARNING, "Failed to create state machine!");
     return NS_ERROR_FAILURE;
   }
 
   return InitializeStateMachine(aCloneDonor);
 }
 
 nsresult MediaDecoder::InitializeStateMachine(MediaDecoder* aCloneDonor)
 {
   MOZ_ASSERT(NS_IsMainThread());
   NS_ASSERTION(mDecoderStateMachine, "Cannot initialize null state machine!");
 
   MediaDecoder* cloneDonor = static_cast<MediaDecoder*>(aCloneDonor);
   if (NS_FAILED(mDecoderStateMachine->Init(cloneDonor ?
                                            cloneDonor->mDecoderStateMachine : nullptr))) {
-    DECODER_LOG(PR_LOG_DEBUG, ("%p Failed to init state machine!", this));
+    DECODER_LOG(PR_LOG_WARNING, "Failed to init state machine!");
     return NS_ERROR_FAILURE;
   }
   {
     ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
     mDecoderStateMachine->SetTransportSeekable(mTransportSeekable);
     mDecoderStateMachine->SetMediaSeekable(mMediaSeekable);
     mDecoderStateMachine->SetDuration(mDuration);
     mDecoderStateMachine->SetVolume(mInitialVolume);
@@ -1228,17 +1230,17 @@ void MediaDecoder::DurationChanged()
   int64_t oldDuration = mDuration;
   mDuration = mDecoderStateMachine ? mDecoderStateMachine->GetDuration() : -1;
   // Duration has changed so we should recompute playback rate
   UpdatePlaybackRate();
 
   SetInfinite(mDuration == -1);
 
   if (mOwner && oldDuration != mDuration && !IsInfinite()) {
-    DECODER_LOG(PR_LOG_DEBUG, ("%p duration changed to %lld", this, mDuration));
+    DECODER_LOG(PR_LOG_DEBUG, "Duration changed to %lld", mDuration);
     mOwner->DispatchEvent(NS_LITERAL_STRING("durationchange"));
   }
 }
 
 void MediaDecoder::SetDuration(double aDuration)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mozilla::IsInfinite(aDuration)) {
@@ -1794,8 +1796,10 @@ MediaMemoryTracker::InitMemoryReporter()
 
 MediaMemoryTracker::~MediaMemoryTracker()
 {
   UnregisterWeakMemoryReporter(this);
 }
 
 } // namespace mozilla
 
+// avoid redefined macro in unified build
+#undef DECODER_LOG
--- a/content/media/MediaDecoderStateMachine.cpp
+++ b/content/media/MediaDecoderStateMachine.cpp
@@ -38,21 +38,33 @@
 #include <algorithm>
 
 namespace mozilla {
 
 using namespace mozilla::layers;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 
+// avoid redefined macro in unified build
+#undef DECODER_LOG
+#undef VERBOSE_LOG
+
 #ifdef PR_LOGGING
 extern PRLogModuleInfo* gMediaDecoderLog;
-#define DECODER_LOG(type, msg) PR_LOG(gMediaDecoderLog, type, msg)
+#define DECODER_LOG(type, msg, ...) \
+  PR_LOG(gMediaDecoderLog, type, ("Decoder=%p " msg, mDecoder.get(), ##__VA_ARGS__))
+#define VERBOSE_LOG(msg, ...)                          \
+    PR_BEGIN_MACRO                                     \
+      if (!PR_GetEnv("MOZ_QUIET")) {                   \
+        DECODER_LOG(PR_LOG_DEBUG, msg, ##__VA_ARGS__); \
+      }                                                \
+    PR_END_MACRO
 #else
-#define DECODER_LOG(type, msg)
+#define DECODER_LOG(type, msg, ...)
+#define VERBOSE_LOG(msg, ...)
 #endif
 
 // GetCurrentTime is defined in winbase.h as zero argument macro forwarding to
 // GetTickCount() and conflicts with MediaDecoderStateMachine::GetCurrentTime
 // implementation.  With unified builds, putting this in headers is not enough.
 #ifdef GetCurrentTime
 #undef GetCurrentTime
 #endif
@@ -190,17 +202,18 @@ MediaDecoderStateMachine::MediaDecoderSt
   mAudioCompleted(false),
   mGotDurationFromMetaData(false),
   mDispatchedEventToDecode(false),
   mStopAudioThread(true),
   mQuickBuffering(false),
   mMinimizePreroll(false),
   mDecodeThreadWaiting(false),
   mRealTime(aRealTime),
-  mLastFrameStatus(MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED)
+  mLastFrameStatus(MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED),
+  mTimerId(0)
 {
   MOZ_COUNT_CTOR(MediaDecoderStateMachine);
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
 
   // Only enable realtime mode when "media.realtime_decoder.enabled" is true.
   if (Preferences::GetBool("media.realtime_decoder.enabled", false) == false)
     mRealTime = false;
 
@@ -230,19 +243,18 @@ MediaDecoderStateMachine::~MediaDecoderS
   NS_ASSERTION(!mPendingWakeDecoder.get(),
                "WakeDecoder should have been revoked already");
 
   if (mDecodeTaskQueue) {
     mDecodeTaskQueue->Shutdown();
     mDecodeTaskQueue = nullptr;
   }
 
-  if (mTimer) {
-    mTimer->Cancel();
-  }
+  // No need to cancel the timer here for we've done that in
+  // TimeoutExpired() triggered by Shutdown()
   mTimer = nullptr;
   mReader = nullptr;
 
 #ifdef XP_WIN
   timeEndPeriod(1);
 #endif
 }
 
@@ -294,18 +306,18 @@ void MediaDecoderStateMachine::SendStrea
   // the exact same silences
   CheckedInt64 audioWrittenOffset = UsecsToFrames(mInfo.mAudio.mRate,
       aStream->mInitialTime + mStartTime) + aStream->mAudioFramesWritten;
   CheckedInt64 frameOffset = UsecsToFrames(mInfo.mAudio.mRate, aAudio->mTime);
   if (!audioWrittenOffset.isValid() || !frameOffset.isValid())
     return;
   if (audioWrittenOffset.value() < frameOffset.value()) {
     // Write silence to catch up
-    DECODER_LOG(PR_LOG_DEBUG, ("%p Decoder writing %d frames of silence to MediaStream",
-                               mDecoder.get(), int32_t(frameOffset.value() - audioWrittenOffset.value())));
+    VERBOSE_LOG("writing %d frames of silence to MediaStream",
+                int32_t(frameOffset.value() - audioWrittenOffset.value()));
     AudioSegment silence;
     silence.InsertNullDataAtStart(frameOffset.value() - audioWrittenOffset.value());
     aStream->mAudioFramesWritten += silence.GetDuration();
     aOutput->AppendFrom(&silence);
   }
 
   int64_t offset;
   if (aStream->mAudioFramesWritten == 0) {
@@ -324,18 +336,18 @@ void MediaDecoderStateMachine::SendStrea
   aAudio->EnsureAudioBuffer();
   nsRefPtr<SharedBuffer> buffer = aAudio->mAudioBuffer;
   AudioDataValue* bufferData = static_cast<AudioDataValue*>(buffer->Data());
   nsAutoTArray<const AudioDataValue*,2> channels;
   for (uint32_t i = 0; i < aAudio->mChannels; ++i) {
     channels.AppendElement(bufferData + i*aAudio->mFrames + offset);
   }
   aOutput->AppendFrames(buffer.forget(), channels, aAudio->mFrames);
-  DECODER_LOG(PR_LOG_DEBUG, ("%p Decoder writing %d frames of data to MediaStream for AudioData at %lld",
-                             mDecoder.get(), aAudio->mFrames - int32_t(offset), aAudio->mTime));
+  VERBOSE_LOG("writing %d frames of data to MediaStream for AudioData at %lld",
+              aAudio->mFrames - int32_t(offset), aAudio->mTime);
   aStream->mAudioFramesWritten += aAudio->mFrames - int32_t(offset);
 }
 
 static void WriteVideoToMediaStream(layers::Image* aImage,
                                     int64_t aDuration,
                                     const IntSize& aIntrinsicSize,
                                     VideoSegment* aOutput)
 {
@@ -416,39 +428,37 @@ void MediaDecoderStateMachine::SendStrea
       nsAutoTArray<VideoData*,10> video;
       // It's OK to hold references to the VideoData only the decoder thread
       // pops from the queue.
       mReader->VideoQueue().GetElementsAfter(stream->mNextVideoTime, &video);
       VideoSegment output;
       for (uint32_t i = 0; i < video.Length(); ++i) {
         VideoData* v = video[i];
         if (stream->mNextVideoTime < v->mTime) {
-          DECODER_LOG(PR_LOG_DEBUG, ("%p Decoder writing last video to MediaStream %p for %lldus",
-                                     mDecoder.get(), mediaStream,
-                                     v->mTime - stream->mNextVideoTime));
+          VERBOSE_LOG("writing last video to MediaStream %p for %lldus",
+                      mediaStream, v->mTime - stream->mNextVideoTime);
           // Write last video frame to catch up. mLastVideoImage can be null here
           // which is fine, it just means there's no video.
           WriteVideoToMediaStream(stream->mLastVideoImage,
             v->mTime - stream->mNextVideoTime, stream->mLastVideoImageDisplaySize,
               &output);
           stream->mNextVideoTime = v->mTime;
         }
         if (stream->mNextVideoTime < v->GetEndTime()) {
-          DECODER_LOG(PR_LOG_DEBUG, ("%p Decoder writing video frame %lldus to MediaStream %p for %lldus",
-                                     mDecoder.get(), v->mTime, mediaStream,
-                                     v->GetEndTime() - stream->mNextVideoTime));
+          VERBOSE_LOG("writing video frame %lldus to MediaStream %p for %lldus",
+                      v->mTime, mediaStream, v->GetEndTime() - stream->mNextVideoTime);
           WriteVideoToMediaStream(v->mImage,
               v->GetEndTime() - stream->mNextVideoTime, v->mDisplay,
               &output);
           stream->mNextVideoTime = v->GetEndTime();
           stream->mLastVideoImage = v->mImage;
           stream->mLastVideoImageDisplaySize = v->mDisplay;
         } else {
-          DECODER_LOG(PR_LOG_DEBUG, ("%p Decoder skipping writing video frame %lldus (end %lldus) to MediaStream",
-                                     mDecoder.get(), v->mTime, v->GetEndTime()));
+          VERBOSE_LOG("skipping writing video frame %lldus (end %lldus) to MediaStream",
+                      v->mTime, v->GetEndTime());
         }
       }
       if (output.GetDuration() > 0) {
         mediaStream->AppendToTrack(TRACK_VIDEO, &output);
       }
       if (mReader->VideoQueue().IsFinished() && !stream->mHaveSentFinishVideo) {
         mediaStream->EndTrack(TRACK_VIDEO);
         stream->mHaveSentFinishVideo = true;
@@ -593,17 +603,17 @@ MediaDecoderStateMachine::DecodeVideo()
          // don't skip frame when |clock time| <= |mVideoFrameEndTime| for
          // we are still in the safe range without underrunning video frames
          GetClock() > mVideoFrameEndTime &&
         (static_cast<uint32_t>(mReader->VideoQueue().GetSize())
           < LOW_VIDEO_FRAMES * mPlaybackRate))) &&
       !HasLowUndecodedData())
   {
     mSkipToNextKeyFrame = true;
-    DECODER_LOG(PR_LOG_DEBUG, ("%p Skipping video decode to the next keyframe", mDecoder.get()));
+    DECODER_LOG(PR_LOG_DEBUG, "Skipping video decode to the next keyframe");
   }
 
   // Time the video decode, so that if it's slow, we can increase our low
   // audio threshold to reduce the chance of an audio underrun while we're
   // waiting for a video decode to complete.
   TimeDuration decodeTime;
   {
     int64_t currentTime = GetMediaTime();
@@ -620,19 +630,18 @@ MediaDecoderStateMachine::DecodeVideo()
 
   if (THRESHOLD_FACTOR * DurationToUsecs(decodeTime) > mLowAudioThresholdUsecs &&
       !HasLowUndecodedData())
   {
     mLowAudioThresholdUsecs =
       std::min(THRESHOLD_FACTOR * DurationToUsecs(decodeTime), AMPLE_AUDIO_USECS);
     mAmpleAudioThresholdUsecs = std::max(THRESHOLD_FACTOR * mLowAudioThresholdUsecs,
                                           mAmpleAudioThresholdUsecs);
-    DECODER_LOG(PR_LOG_DEBUG,
-                ("Slow video decode, set mLowAudioThresholdUsecs=%lld mAmpleAudioThresholdUsecs=%lld",
-                mLowAudioThresholdUsecs, mAmpleAudioThresholdUsecs));
+    DECODER_LOG(PR_LOG_DEBUG, "Slow video decode, set mLowAudioThresholdUsecs=%lld mAmpleAudioThresholdUsecs=%lld",
+                mLowAudioThresholdUsecs, mAmpleAudioThresholdUsecs);
   }
 
   SendStreamData();
 
   // The ready state can change when we've decoded data, so update the
   // ready state, so that DOM events can fire.
   UpdateReadyState();
 
@@ -710,19 +719,18 @@ MediaDecoderStateMachine::CheckIfDecodeC
   MOZ_ASSERT(!mReader->VideoQueue().IsFinished() || !mIsVideoDecoding);
   if (!mIsVideoDecoding && !mIsAudioDecoding) {
     // We've finished decoding all active streams,
     // so move to COMPLETED state.
     mState = DECODER_STATE_COMPLETED;
     DispatchDecodeTasksIfNeeded();
     ScheduleStateMachine();
   }
-  DECODER_LOG(PR_LOG_DEBUG,
-    ("%p CheckIfDecodeComplete %scompleted", mDecoder.get(),
-    ((mState == DECODER_STATE_COMPLETED) ? "" : "NOT ")));
+  DECODER_LOG(PR_LOG_DEBUG, "CheckIfDecodeComplete %scompleted",
+              ((mState == DECODER_STATE_COMPLETED) ? "" : "NOT "));
 }
 
 bool MediaDecoderStateMachine::IsPlaying()
 {
   AssertCurrentThreadInMonitor();
 
   return !mPlayStartTime.IsNull();
 }
@@ -748,17 +756,17 @@ static void WriteSilence(AudioStream* aS
   aStream->Write(buf.Elements(), aFrames);
 
   StartAudioStreamPlaybackIfNeeded(aStream);
 }
 
 void MediaDecoderStateMachine::AudioLoop()
 {
   NS_ASSERTION(OnAudioThread(), "Should be on audio thread.");
-  DECODER_LOG(PR_LOG_DEBUG, ("%p Begun audio thread/loop", mDecoder.get()));
+  DECODER_LOG(PR_LOG_DEBUG, "Begun audio thread/loop");
   int64_t audioDuration = 0;
   int64_t audioStartTime = -1;
   uint32_t channels, rate;
   double volume = -1;
   bool setVolume;
   double playbackRate = -1;
   bool setPlaybackRate;
   bool preservesPitch;
@@ -886,18 +894,17 @@ void MediaDecoderStateMachine::AudioLoop
 
     int64_t framesWritten = 0;
     if (missingFrames.value() > 0) {
       // The next audio chunk begins some time after the end of the last chunk
       // we pushed to the audio hardware. We must push silence into the audio
       // hardware so that the next audio chunk begins playback at the correct
       // time.
       missingFrames = std::min<int64_t>(UINT32_MAX, missingFrames.value());
-      DECODER_LOG(PR_LOG_DEBUG, ("%p Decoder playing %d frames of silence",
-                                 mDecoder.get(), int32_t(missingFrames.value())));
+      VERBOSE_LOG("playing %d frames of silence", int32_t(missingFrames.value()));
       framesWritten = PlaySilence(static_cast<uint32_t>(missingFrames.value()),
                                   channels, playedFrames.value());
     } else {
       framesWritten = PlayFromAudioQueue(sampleTime.value(), channels);
     }
     audioDuration += framesWritten;
     {
       ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
@@ -940,32 +947,32 @@ void MediaDecoderStateMachine::AudioLoop
       if (!seeking && !mAudioStream->IsPaused()) {
         {
           ReentrantMonitorAutoExit exit(mDecoder->GetReentrantMonitor());
           mAudioStream->Drain();
         }
       }
     }
   }
-  DECODER_LOG(PR_LOG_DEBUG, ("%p Reached audio stream end.", mDecoder.get()));
+  DECODER_LOG(PR_LOG_DEBUG, "Reached audio stream end.");
   {
     // Must hold lock while shutting down and anulling the audio stream to prevent
     // state machine thread trying to use it while we're destroying it.
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
     mAudioStream->Shutdown();
     mAudioStream = nullptr;
     if (!mAudioCaptured) {
       mAudioCompleted = true;
       UpdateReadyState();
       // Kick the decode thread; it may be sleeping waiting for this to finish.
       mDecoder->GetReentrantMonitor().NotifyAll();
     }
   }
 
-  DECODER_LOG(PR_LOG_DEBUG, ("%p Audio stream finished playing, audio thread exit", mDecoder.get()));
+  DECODER_LOG(PR_LOG_DEBUG, "Audio stream finished playing, audio thread exit");
 }
 
 uint32_t MediaDecoderStateMachine::PlaySilence(uint32_t aFrames,
                                                    uint32_t aChannels,
                                                    uint64_t aFrameOffset)
 
 {
   NS_ASSERTION(OnAudioThread(), "Only call on audio thread.");
@@ -986,20 +993,18 @@ uint32_t MediaDecoderStateMachine::PlayF
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
     NS_WARN_IF_FALSE(IsPlaying(), "Should be playing");
     // Awaken the decode loop if it's waiting for space to free up in the
     // audio queue.
     mDecoder->GetReentrantMonitor().NotifyAll();
   }
   int64_t offset = -1;
   uint32_t frames = 0;
-  if (!PR_GetEnv("MOZ_QUIET")) {
-    DECODER_LOG(PR_LOG_DEBUG, ("%p Decoder playing %d frames of data to stream for AudioData at %lld",
-                               mDecoder.get(), audio->mFrames, audio->mTime));
-  }
+  VERBOSE_LOG("playing %d frames of data to stream for AudioData at %lld",
+              audio->mFrames, audio->mTime);
   mAudioStream->Write(audio->mAudioData,
                       audio->mFrames);
 
   aChannels = mAudioStream->GetOutChannels();
 
   StartAudioStreamPlaybackIfNeeded(mAudioStream);
 
   offset = audio->mOffset;
@@ -1040,17 +1045,17 @@ nsresult MediaDecoderStateMachine::Init(
   rv = mTimer->SetTarget(GetStateMachineThread());
   NS_ENSURE_SUCCESS(rv, rv);
 
   return mReader->Init(cloneReader);
 }
 
 void MediaDecoderStateMachine::StopPlayback()
 {
-  DECODER_LOG(PR_LOG_DEBUG, ("%p StopPlayback()", mDecoder.get()));
+  DECODER_LOG(PR_LOG_DEBUG, "StopPlayback()");
 
   AssertCurrentThreadInMonitor();
 
   mDecoder->NotifyPlaybackStopped();
 
   if (IsPlaying()) {
     mPlayDuration = GetClock();
     mPlayStartTime = TimeStamp();
@@ -1083,17 +1088,17 @@ int64_t MediaDecoderStateMachine::GetCur
   NS_ASSERTION(mSyncPointInDecodedStream >= 0, "Should have set up sync point");
   DecodedStreamData* stream = mDecoder->GetDecodedStream();
   StreamTime streamDelta = stream->GetLastOutputTime() - mSyncPointInMediaStream;
   return mSyncPointInDecodedStream + MediaTimeToMicroseconds(streamDelta);
 }
 
 void MediaDecoderStateMachine::StartPlayback()
 {
-  DECODER_LOG(PR_LOG_DEBUG, ("%p StartPlayback()", mDecoder.get()));
+  DECODER_LOG(PR_LOG_DEBUG, "StartPlayback()");
 
   NS_ASSERTION(!IsPlaying(), "Shouldn't be playing when StartPlayback() is called");
   AssertCurrentThreadInMonitor();
 
   mDecoder->NotifyPlaybackStarted();
   mPlayStartTime = TimeStamp::Now();
 
   NS_ASSERTION(IsPlaying(), "Should report playing by end of StartPlayback()");
@@ -1295,17 +1300,17 @@ void MediaDecoderStateMachine::Shutdown(
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
 
   // Once we've entered the shutdown state here there's no going back.
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
 
   // Change state before issuing shutdown request to threads so those
   // threads can start exiting cleanly during the Shutdown call.
-  DECODER_LOG(PR_LOG_DEBUG, ("%p Changed state to SHUTDOWN", mDecoder.get()));
+  DECODER_LOG(PR_LOG_DEBUG, "Changed state to SHUTDOWN");
   ScheduleStateMachine();
   mState = DECODER_STATE_SHUTDOWN;
   mDecoder->GetReentrantMonitor().NotifyAll();
 }
 
 void MediaDecoderStateMachine::StartDecoding()
 {
   NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(),
@@ -1363,17 +1368,17 @@ void MediaDecoderStateMachine::NotifyWai
 void MediaDecoderStateMachine::Play()
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   // When asked to play, switch to decoding state only if
   // we are currently buffering. In other cases, we'll start playing anyway
   // when the state machine notices the decoder's state change to PLAYING.
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   if (mState == DECODER_STATE_BUFFERING) {
-    DECODER_LOG(PR_LOG_DEBUG, ("%p Changed state from BUFFERING to DECODING", mDecoder.get()));
+    DECODER_LOG(PR_LOG_DEBUG, "Changed state from BUFFERING to DECODING");
     mState = DECODER_STATE_DECODING;
     mDecodeStartTime = TimeStamp::Now();
   }
   // Once we start playing, we don't want to minimize our prerolling, as we
   // assume the user is likely to want to keep playing in future.
   mMinimizePreroll = false;
   ScheduleStateMachine();
 }
@@ -1437,17 +1442,17 @@ void MediaDecoderStateMachine::Seek(cons
   int64_t seekTime = aTarget.mTime + mStartTime;
   seekTime = std::min(seekTime, mEndTime);
   seekTime = std::max(mStartTime, seekTime);
   NS_ASSERTION(seekTime >= mStartTime && seekTime <= mEndTime,
                "Can only seek in range [0,duration]");
   mSeekTarget = SeekTarget(seekTime, aTarget.mType);
 
   mBasePosition = seekTime - mStartTime;
-  DECODER_LOG(PR_LOG_DEBUG, ("%p Changed state to SEEKING (to %ld)", mDecoder.get(), mSeekTarget.mTime));
+  DECODER_LOG(PR_LOG_DEBUG, "Changed state to SEEKING (to %ld)", mSeekTarget.mTime);
   mState = DECODER_STATE_SEEKING;
   if (mDecoder->GetDecodedStream()) {
     mDecoder->RecreateDecodedStream(seekTime - mStartTime);
   }
   ScheduleStateMachine();
 }
 
 void MediaDecoderStateMachine::StopAudioThread()
@@ -1459,17 +1464,17 @@ void MediaDecoderStateMachine::StopAudio
   if (mStopAudioThread) {
     // Nothing to do, since the thread is already stopping
     return;
   }
 
   mStopAudioThread = true;
   mDecoder->GetReentrantMonitor().NotifyAll();
   if (mAudioThread) {
-    DECODER_LOG(PR_LOG_DEBUG, ("%p Shutdown audio thread", mDecoder.get()));
+    DECODER_LOG(PR_LOG_DEBUG, "Shutdown audio thread");
     {
       ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
       mAudioThread->Shutdown();
     }
     mAudioThread = nullptr;
     // Now that the audio thread is dead, try sending data to our MediaStream(s).
     // That may have been waiting for the audio thread to stop.
     SendStreamData();
@@ -1507,30 +1512,29 @@ MediaDecoderStateMachine::EnsureActive()
 }
 
 void
 MediaDecoderStateMachine::SetReaderIdle()
 {
 #ifdef PR_LOGGING
   {
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
-    DECODER_LOG(PR_LOG_DEBUG, ("%p SetReaderIdle() audioQueue=%lld videoQueue=%lld",
-                               mDecoder.get(),
-                               GetDecodedAudioDuration(),
-                               mReader->VideoQueue().Duration()));
+    DECODER_LOG(PR_LOG_DEBUG, "SetReaderIdle() audioQueue=%lld videoQueue=%lld",
+                GetDecodedAudioDuration(),
+                mReader->VideoQueue().Duration());
   }
 #endif
   MOZ_ASSERT(OnDecodeThread());
   mReader->SetIdle();
 }
 
 void
 MediaDecoderStateMachine::SetReaderActive()
 {
-  DECODER_LOG(PR_LOG_DEBUG, ("%p SetReaderActive()", mDecoder.get()));
+  DECODER_LOG(PR_LOG_DEBUG, "SetReaderActive()");
   MOZ_ASSERT(OnDecodeThread());
   mReader->SetActive();
 }
 
 void
 MediaDecoderStateMachine::DispatchDecodeTasksIfNeeded()
 {
   AssertCurrentThreadInMonitor();
@@ -1694,17 +1698,17 @@ MediaDecoderStateMachine::StartAudioThre
 
   mStopAudioThread = false;
   if (HasAudio() && !mAudioThread) {
     nsresult rv = NS_NewNamedThread("Media Audio",
                                     getter_AddRefs(mAudioThread),
                                     nullptr,
                                     MEDIA_THREAD_STACK_SIZE);
     if (NS_FAILED(rv)) {
-      DECODER_LOG(PR_LOG_DEBUG, ("%p Changed state to SHUTDOWN because failed to create audio thread", mDecoder.get()));
+      DECODER_LOG(PR_LOG_WARNING, "Changed state to SHUTDOWN because failed to create audio thread");
       mState = DECODER_STATE_SHUTDOWN;
       return rv;
     }
 
     nsCOMPtr<nsIRunnable> event =
       NS_NewRunnableMethod(this, &MediaDecoderStateMachine::AudioLoop);
     mAudioThread->Dispatch(event, NS_DISPATCH_NORMAL);
   }
@@ -1771,17 +1775,17 @@ void
 MediaDecoderStateMachine::DecodeError()
 {
   AssertCurrentThreadInMonitor();
   NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
 
   // Change state to shutdown before sending error report to MediaDecoder
   // and the HTMLMediaElement, so that our pipeline can start exiting
   // cleanly during the sync dispatch below.
-  DECODER_LOG(PR_LOG_DEBUG, ("%p Changed state to SHUTDOWN", mDecoder.get()));
+  DECODER_LOG(PR_LOG_WARNING, "Decode error, changed state to SHUTDOWN");
   ScheduleStateMachine();
   mState = DECODER_STATE_SHUTDOWN;
   mDecoder->GetReentrantMonitor().NotifyAll();
 
   // Dispatch the event to call DecodeError synchronously. This ensures
   // we're in shutdown state by the time we exit the decode thread.
   // If we just moved to shutdown state here on the decode thread, we may
   // cause the state machine to shutdown/free memory without closing its
@@ -1798,26 +1802,26 @@ MediaDecoderStateMachine::DecodeError()
 void
 MediaDecoderStateMachine::CallDecodeMetadata()
 {
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   if (mState != DECODER_STATE_DECODING_METADATA) {
     return;
   }
   if (NS_FAILED(DecodeMetadata())) {
-    DECODER_LOG(PR_LOG_DEBUG, ("Decode metadata failed, shutting down decoder"));
+    DECODER_LOG(PR_LOG_WARNING, "Decode metadata failed, shutting down decoder");
     DecodeError();
   }
 }
 
 nsresult MediaDecoderStateMachine::DecodeMetadata()
 {
   AssertCurrentThreadInMonitor();
   NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
-  DECODER_LOG(PR_LOG_DEBUG, ("%p Decoding Media Headers", mDecoder.get()));
+  DECODER_LOG(PR_LOG_DEBUG, "Decoding Media Headers");
   if (mState != DECODER_STATE_DECODING_METADATA) {
     return NS_ERROR_FAILURE;
   }
   EnsureActive();
 
   nsresult res;
   MediaInfo info;
   MetadataTags* tags;
@@ -1852,20 +1856,19 @@ nsresult MediaDecoderStateMachine::Decod
   }
 
   NS_ASSERTION(mStartTime != -1, "Must have start time");
   MOZ_ASSERT((!HasVideo() && !HasAudio()) ||
               !(mMediaSeekable && mTransportSeekable) || mEndTime != -1,
               "Active seekable media should have end time");
   MOZ_ASSERT(!(mMediaSeekable && mTransportSeekable) ||
              GetDuration() != -1, "Seekable media should have duration");
-  DECODER_LOG(PR_LOG_DEBUG, ("%p Media goes from %lld to %lld (duration %lld)"
-                             " transportSeekable=%d, mediaSeekable=%d",
-                             mDecoder.get(), mStartTime, mEndTime, GetDuration(),
-                             mTransportSeekable, mMediaSeekable));
+  DECODER_LOG(PR_LOG_DEBUG, "Media goes from %lld to %lld (duration %lld) "
+              "transportSeekable=%d, mediaSeekable=%d",
+              mStartTime, mEndTime, GetDuration(), mTransportSeekable, mMediaSeekable);
 
   if (HasAudio() && !HasVideo()) {
     // We're playing audio only. We don't need to worry about slow video
     // decodes causing audio underruns, so don't buffer so much audio in
     // order to reduce memory usage.
     mAmpleAudioThresholdUsecs /= NO_VIDEO_AMPLE_AUDIO_DIVISOR;
     mLowAudioThresholdUsecs /= NO_VIDEO_AMPLE_AUDIO_DIVISOR;
   }
@@ -1887,17 +1890,17 @@ nsresult MediaDecoderStateMachine::Decod
   }
   if (HasVideo()) {
     RefPtr<nsIRunnable> decodeTask(
       NS_NewRunnableMethod(this, &MediaDecoderStateMachine::DispatchVideoDecodeTaskIfNeeded));
     mReader->VideoQueue().AddPopListener(decodeTask, mDecodeTaskQueue);
   }
 
   if (mState == DECODER_STATE_DECODING_METADATA) {
-    DECODER_LOG(PR_LOG_DEBUG, ("%p Changed state from DECODING_METADATA to DECODING", mDecoder.get()));
+    DECODER_LOG(PR_LOG_DEBUG, "Changed state from DECODING_METADATA to DECODING");
     StartDecoding();
   }
 
   // For very short media FindStartTime() can decode the entire media.
   // So we need to check if this has occurred, else our decode pipeline won't
   // run (since it doesn't need to) and we won't detect end of stream.
   CheckIfDecodeComplete();
 
@@ -2020,42 +2023,39 @@ void MediaDecoderStateMachine::DecodeSee
   // if we need to seek again.
 
   nsCOMPtr<nsIRunnable> stopEvent;
   bool isLiveStream = mDecoder->GetResource()->GetLength() == -1;
   if (GetMediaTime() == mEndTime && !isLiveStream) {
     // Seeked to end of media, move to COMPLETED state. Note we don't do
     // this if we're playing a live stream, since the end of media will advance
     // once we download more data!
-    DECODER_LOG(PR_LOG_DEBUG, ("%p Changed state from SEEKING (to %lld) to COMPLETED",
-                               mDecoder.get(), seekTime));
+    DECODER_LOG(PR_LOG_DEBUG, "Changed state from SEEKING (to %lld) to COMPLETED", seekTime);
     stopEvent = NS_NewRunnableMethod(mDecoder, &MediaDecoder::SeekingStoppedAtEnd);
     // Explicitly set our state so we don't decode further, and so
     // we report playback ended to the media element.
     mState = DECODER_STATE_COMPLETED;
     mIsAudioDecoding = false;
     mIsVideoDecoding = false;
     DispatchDecodeTasksIfNeeded();
   } else {
-    DECODER_LOG(PR_LOG_DEBUG, ("%p Changed state from SEEKING (to %lld) to DECODING",
-                               mDecoder.get(), seekTime));
+    DECODER_LOG(PR_LOG_DEBUG, "Changed state from SEEKING (to %lld) to DECODING", seekTime);
     stopEvent = NS_NewRunnableMethod(mDecoder, &MediaDecoder::SeekingStopped);
     StartDecoding();
   }
 
   if (newCurrentTime != mediaTime) {
     UpdatePlaybackPositionInternal(newCurrentTime);
     if (mDecoder->GetDecodedStream()) {
       SetSyncPointForMediaStream();
     }
   }
 
   // Try to decode another frame to detect if we're at the end...
-  DECODER_LOG(PR_LOG_DEBUG, ("%p Seek completed, mCurrentFrameTime=%lld\n",
-              mDecoder.get(), mCurrentFrameTime));
+  DECODER_LOG(PR_LOG_DEBUG, "Seek completed, mCurrentFrameTime=%lld", mCurrentFrameTime);
 
   {
     ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
     NS_DispatchToMainThread(stopEvent, NS_DISPATCH_SYNC);
   }
 
   // Reset quick buffering status. This ensures that if we began the
   // seek while quick-buffering, we won't bypass quick buffering mode
@@ -2221,29 +2221,24 @@ nsresult MediaDecoderStateMachine::RunSt
       bool isLiveStream = resource->GetLength() == -1;
       if ((isLiveStream || !mDecoder->CanPlayThrough()) &&
             elapsed < TimeDuration::FromSeconds(mBufferingWait * mPlaybackRate) &&
             (mQuickBuffering ? HasLowDecodedData(QUICK_BUFFERING_LOW_DATA_USECS)
                             : HasLowUndecodedData(mBufferingWait * USECS_PER_S)) &&
             !mDecoder->IsDataCachedToEndOfResource() &&
             !resource->IsSuspended())
       {
-        DECODER_LOG(PR_LOG_DEBUG,
-                    ("%p Buffering: wait %ds, timeout in %.3lfs %s",
-                      mDecoder.get(),
-                      mBufferingWait,
-                      mBufferingWait - elapsed.ToSeconds(),
-                      (mQuickBuffering ? "(quick exit)" : "")));
+        DECODER_LOG(PR_LOG_DEBUG, "Buffering: wait %ds, timeout in %.3lfs %s",
+                    mBufferingWait, mBufferingWait - elapsed.ToSeconds(),
+                    (mQuickBuffering ? "(quick exit)" : ""));
         ScheduleStateMachine(USECS_PER_S);
         return NS_OK;
       } else {
-        DECODER_LOG(PR_LOG_DEBUG, ("%p Changed state from BUFFERING to DECODING", mDecoder.get()));
-        DECODER_LOG(PR_LOG_DEBUG, ("%p Buffered for %.3lfs",
-                                   mDecoder.get(),
-                                   (now - mBufferingStart).ToSeconds()));
+        DECODER_LOG(PR_LOG_DEBUG, "Changed state from BUFFERING to DECODING");
+        DECODER_LOG(PR_LOG_DEBUG, "Buffered for %.3lfs", (now - mBufferingStart).ToSeconds());
         StartDecoding();
       }
 
       // Notify to allow blocked decoder thread to continue
       mDecoder->GetReentrantMonitor().NotifyAll();
       UpdateReadyState();
       if (mDecoder->GetState() == MediaDecoder::PLAY_STATE_PLAYING &&
           !IsPlaying())
@@ -2319,20 +2314,17 @@ void MediaDecoderStateMachine::RenderVid
   NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(),
                "Should be on state machine or decode thread.");
   mDecoder->GetReentrantMonitor().AssertNotCurrentThreadIn();
 
   if (aData->mDuplicate) {
     return;
   }
 
-  if (!PR_GetEnv("MOZ_QUIET")) {
-    DECODER_LOG(PR_LOG_DEBUG, ("%p Decoder playing video frame %lld",
-                               mDecoder.get(), aData->mTime));
-  }
+  VERBOSE_LOG("playing video frame %lld", aData->mTime);
 
   VideoFrameContainer* container = mDecoder->GetVideoFrameContainer();
   if (container) {
     container->SetCurrentFrame(ThebesIntSize(aData->mDisplay), aData->mImage,
                                aTarget);
   }
 }
 
@@ -2433,22 +2425,19 @@ void MediaDecoderStateMachine::AdvanceFr
   int32_t droppedFrames = 0;
 #endif
   if (mReader->VideoQueue().GetSize() > 0) {
     VideoData* frame = mReader->VideoQueue().PeekFront();
     while (mRealTime || clock_time >= frame->mTime) {
       mVideoFrameEndTime = frame->GetEndTime();
       currentFrame = frame;
 #ifdef PR_LOGGING
-      if (!PR_GetEnv("MOZ_QUIET")) {
-        DECODER_LOG(PR_LOG_DEBUG, ("%p Decoder discarding video frame %lld", mDecoder.get(), frame->mTime));
-        if (droppedFrames++) {
-          DECODER_LOG(PR_LOG_DEBUG, ("%p Decoder discarding video frame %lld (%d so far)",
-                      mDecoder.get(), frame->mTime, droppedFrames - 1));
-        }
+      VERBOSE_LOG("discarding video frame %lld", frame->mTime);
+      if (droppedFrames++) {
+        VERBOSE_LOG("discarding video frame %lld (%d so far)", frame->mTime, droppedFrames-1);
       }
 #endif
       mReader->VideoQueue().PopFront();
       // Notify the decode thread that the video queue's buffers may have
       // free'd up space for more frames.
       mDecoder->GetReentrantMonitor().NotifyAll();
       mDecoder->UpdatePlaybackOffset(frame->mOffset);
       if (mReader->VideoQueue().GetSize() == 0)
@@ -2576,17 +2565,17 @@ VideoData* MediaDecoderStateMachine::Fin
       // duration.
       mEndTime = mStartTime + mEndTime;
     }
   }
   // Set the audio start time to be start of media. If this lies before the
   // first actual audio frame we have, we'll inject silence during playback
   // to ensure the audio starts at the correct time.
   mAudioStartTime = mStartTime;
-  DECODER_LOG(PR_LOG_DEBUG, ("%p Media start time is %lld", mDecoder.get(), mStartTime));
+  DECODER_LOG(PR_LOG_DEBUG, "Media start time is %lld", mStartTime);
   return v;
 }
 
 void MediaDecoderStateMachine::UpdateReadyState() {
   AssertCurrentThreadInMonitor();
 
   MediaDecoderOwner::NextFrameStatus nextFrameStatus = GetNextFrameStatus();
   if (nextFrameStatus == mLastFrameStatus) {
@@ -2646,24 +2635,23 @@ void MediaDecoderStateMachine::StartBuff
   // there might be pending main-thread events, such as "data
   // received" notifications, that mean we're not actually still
   // buffering by the time this runnable executes. So instead
   // we just trigger UpdateReadyStateForData; when it runs, it
   // will check the current state and decide whether to tell
   // the element we're buffering or not.
   UpdateReadyState();
   mState = DECODER_STATE_BUFFERING;
-  DECODER_LOG(PR_LOG_DEBUG, ("%p Changed state from DECODING to BUFFERING, decoded for %.3lfs",
-                             mDecoder.get(), decodeDuration.ToSeconds()));
+  DECODER_LOG(PR_LOG_DEBUG, "Changed state from DECODING to BUFFERING, decoded for %.3lfs",
+              decodeDuration.ToSeconds());
 #ifdef PR_LOGGING
   MediaDecoder::Statistics stats = mDecoder->GetStatistics();
-  DECODER_LOG(PR_LOG_DEBUG, ("%p Playback rate: %.1lfKB/s%s download rate: %.1lfKB/s%s",
-              mDecoder.get(),
+  DECODER_LOG(PR_LOG_DEBUG, "Playback rate: %.1lfKB/s%s download rate: %.1lfKB/s%s",
               stats.mPlaybackRate/1024, stats.mPlaybackRateReliable ? "" : " (unreliable)",
-              stats.mDownloadRate/1024, stats.mDownloadRateReliable ? "" : " (unreliable)"));
+              stats.mDownloadRate/1024, stats.mDownloadRateReliable ? "" : " (unreliable)");
 #endif
 }
 
 nsresult MediaDecoderStateMachine::GetBuffered(dom::TimeRanges* aBuffered) {
   MediaResource* resource = mDecoder->GetResource();
   NS_ENSURE_TRUE(resource, NS_ERROR_FAILURE);
   resource->Pin();
   nsresult res = mReader->GetBuffered(aBuffered, mStartTime);
@@ -2684,36 +2672,54 @@ nsresult MediaDecoderStateMachine::CallR
   MOZ_ASSERT(!mInRunningStateMachine, "State machine cycles must run in sequence!");
   mTimeout = TimeStamp();
   mInRunningStateMachine = true;
   nsresult res = RunStateMachine();
   mInRunningStateMachine = false;
   return res;
 }
 
-static void TimeoutExpired(nsITimer *aTimer, void *aClosure) {
-  MediaDecoderStateMachine *machine =
-    static_cast<MediaDecoderStateMachine*>(aClosure);
-  NS_ASSERTION(machine, "Must have been passed state machine");
-  machine->TimeoutExpired();
-}
-
-void MediaDecoderStateMachine::TimeoutExpired()
+nsresult MediaDecoderStateMachine::TimeoutExpired(int aTimerId)
 {
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   NS_ASSERTION(OnStateMachineThread(), "Must be on state machine thread");
-  CallRunStateMachine();
+  mTimer->Cancel();
+  if (mTimerId == aTimerId) {
+    return CallRunStateMachine();
+  } else {
+    return NS_OK;
+  }
 }
 
 void MediaDecoderStateMachine::ScheduleStateMachineWithLockAndWakeDecoder() {
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   DispatchAudioDecodeTaskIfNeeded();
   DispatchVideoDecodeTaskIfNeeded();
 }
 
+class TimerEvent : public nsITimerCallback, public nsRunnable {
+  NS_DECL_THREADSAFE_ISUPPORTS
+public:
+  TimerEvent(MediaDecoderStateMachine* aStateMachine, int aTimerId)
+    : mStateMachine(aStateMachine), mTimerId(aTimerId) {}
+
+  NS_IMETHOD Run() MOZ_OVERRIDE {
+    return mStateMachine->TimeoutExpired(mTimerId);
+  }
+
+  NS_IMETHOD Notify(nsITimer* aTimer) {
+    return mStateMachine->TimeoutExpired(mTimerId);
+  }
+private:
+  const nsRefPtr<MediaDecoderStateMachine> mStateMachine;
+  int mTimerId;
+};
+
+NS_IMPL_ISUPPORTS2(TimerEvent, nsITimerCallback, nsIRunnable);
+
 nsresult MediaDecoderStateMachine::ScheduleStateMachine(int64_t aUsecs) {
   AssertCurrentThreadInMonitor();
   NS_ABORT_IF_FALSE(GetStateMachineThread(),
     "Must have a state machine thread to schedule");
 
   if (mState == DECODER_STATE_SHUTDOWN) {
     return NS_ERROR_FAILURE;
   }
@@ -2725,25 +2731,42 @@ nsresult MediaDecoderStateMachine::Sched
     // or have an event dispatched to run the state machine.
     return NS_OK;
   }
 
   uint32_t ms = static_cast<uint32_t>((aUsecs / USECS_PER_MS) & 0xFFFFFFFF);
   if (mRealTime && ms > 40) {
     ms = 40;
   }
-  mTimeout = timeout;
-  // Cancel existing timer if any since we are going to schedule a new one.
-  mTimer->Cancel();
-  nsresult rv = mTimer->InitWithFuncCallback(mozilla::TimeoutExpired,
-                                             this,
-                                             ms,
-                                             nsITimer::TYPE_ONE_SHOT);
-  NS_ENSURE_SUCCESS(rv, rv);
-  return NS_OK;
+
+  // Don't cancel the timer here for this function will be called from
+  // different threads.
+
+  nsresult rv = NS_ERROR_FAILURE;
+  nsRefPtr<TimerEvent> event = new TimerEvent(this, mTimerId+1);
+
+  if (ms == 0) {
+    // Dispatch a runnable to the state machine thread when delay is 0.
+    // It will has less latency than dispatching a runnable to the state
+    // machine thread which will then schedule a zero-delay timer.
+    rv = GetStateMachineThread()->Dispatch(event, NS_DISPATCH_NORMAL);
+  } else if (OnStateMachineThread()) {
+    rv = mTimer->InitWithCallback(event, ms, nsITimer::TYPE_ONE_SHOT);
+  } else {
+    MOZ_ASSERT(false, "non-zero delay timer should be only scheduled in state machine thread");
+  }
+
+  if (NS_SUCCEEDED(rv)) {
+    mTimeout = timeout;
+    ++mTimerId;
+  } else {
+    NS_WARNING("Failed to schedule state machine");
+  }
+
+  return rv;
 }
 
 bool MediaDecoderStateMachine::OnDecodeThread() const
 {
   return mDecodeTaskQueue->IsCurrentThreadIn();
 }
 
 bool MediaDecoderStateMachine::OnStateMachineThread() const
@@ -2822,8 +2845,11 @@ void MediaDecoderStateMachine::QueueMeta
   metadata->mHasAudio = aHasAudio;
   metadata->mHasVideo = aHasVideo;
   metadata->mTags = aTags;
   mMetadataManager.QueueMetadata(metadata);
 }
 
 } // namespace mozilla
 
+// avoid redefined macro in unified build
+#undef DECODER_LOG
+#undef VERBOSE_LOG
--- a/content/media/MediaDecoderStateMachine.h
+++ b/content/media/MediaDecoderStateMachine.h
@@ -302,17 +302,17 @@ public:
   void ScheduleStateMachineWithLockAndWakeDecoder();
 
   // Schedules the shared state machine thread to run the state machine
   // in aUsecs microseconds from now, if it's not already scheduled to run
   // earlier, in which case the request is discarded.
   nsresult ScheduleStateMachine(int64_t aUsecs = 0);
 
   // Timer function to implement ScheduleStateMachine(aUsecs).
-  void TimeoutExpired();
+  nsresult TimeoutExpired(int aGeneration);
 
   // Set the media fragment end time. aEndTime is in microseconds.
   void SetFragmentEndTime(int64_t aEndTime);
 
   // Drop reference to decoder.  Only called during shutdown dance.
   void ReleaseDecoder() {
     MOZ_ASSERT(mReader);
     if (mReader) {
@@ -932,12 +932,15 @@ private:
 
   // Stores presentation info required for playback. The decoder monitor
   // must be held when accessing this.
   MediaInfo mInfo;
 
   mozilla::MediaMetadataManager mMetadataManager;
 
   MediaDecoderOwner::NextFrameStatus mLastFrameStatus;
+
+  // The id of timer tasks, used to ignore tasks that are scheduled previously.
+  int mTimerId;
 };
 
 } // namespace mozilla;
 #endif
--- a/content/media/webaudio/AudioBuffer.cpp
+++ b/content/media/webaudio/AudioBuffer.cpp
@@ -223,43 +223,16 @@ AudioBuffer::GetThreadSharedChannelsForR
 
     mSharedChannels =
       StealJSArrayDataIntoThreadSharedFloatArrayBufferList(aJSContext, mJSChannels);
   }
 
   return mSharedChannels;
 }
 
-void
-AudioBuffer::MixToMono(JSContext* aJSContext)
-{
-  if (mJSChannels.Length() == 1) {
-    // The buffer is already mono
-    return;
-  }
-
-  // Prepare the input channels
-  nsAutoTArray<const void*, GUESS_AUDIO_CHANNELS> channels;
-  channels.SetLength(mJSChannels.Length());
-  for (uint32_t i = 0; i < mJSChannels.Length(); ++i) {
-    channels[i] = JS_GetFloat32ArrayData(mJSChannels[i]);
-  }
-
-  // Prepare the output channels
-  float* downmixBuffer = new float[mLength];
-
-  // Perform the down-mix
-  AudioChannelsDownMix(channels, &downmixBuffer, 1, mLength);
-
-  // Truncate the shared channels and copy the downmixed data over
-  mJSChannels.SetLength(1);
-  SetRawChannelContents(aJSContext, 0, downmixBuffer);
-  delete[] downmixBuffer;
-}
-
 size_t
 AudioBuffer::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
 {
   size_t amount = aMallocSizeOf(this);
   amount += mJSChannels.SizeOfExcludingThis(aMallocSizeOf);
   if (mSharedChannels) {
     amount += mSharedChannels->SizeOfExcludingThis(aMallocSizeOf);
   }
--- a/content/media/webaudio/AudioBuffer.h
+++ b/content/media/webaudio/AudioBuffer.h
@@ -96,18 +96,16 @@ public:
   // This replaces the contents of the JS array for the given channel.
   // This function needs to be called on an AudioBuffer which has not been
   // handed off to the content yet, and right after the object has been
   // initialized.
   void SetRawChannelContents(JSContext* aJSContext,
                              uint32_t aChannel,
                              float* aContents);
 
-  void MixToMono(JSContext* aJSContext);
-
 protected:
   bool RestoreJSChannelData(JSContext* aJSContext);
   void ClearJSChannels();
 
   nsRefPtr<AudioContext> mContext;
   // Float32Arrays
   AutoFallibleTArray<JS::Heap<JSObject*>, 2> mJSChannels;
 
--- a/content/media/webaudio/AudioBufferSourceNode.h
+++ b/content/media/webaudio/AudioBufferSourceNode.h
@@ -39,32 +39,17 @@ public:
   }
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(AudioBufferSourceNode, AudioNode)
 
   virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
   void Start(double aWhen, double aOffset,
              const Optional<double>& aDuration, ErrorResult& aRv);
-  void NoteOn(double aWhen, ErrorResult& aRv)
-  {
-    Start(aWhen, 0.0, Optional<double>(), aRv);
-  }
-  void NoteGrainOn(double aWhen, double aOffset,
-                   double aDuration, ErrorResult& aRv)
-  {
-    Optional<double> duration;
-    duration.Construct(aDuration);
-    Start(aWhen, aOffset, duration, aRv);
-  }
   void Stop(double aWhen, ErrorResult& aRv);
-  void NoteOff(double aWhen, ErrorResult& aRv)
-  {
-    Stop(aWhen, aRv);
-  }
 
   AudioBuffer* GetBuffer(JSContext* aCx) const
   {
     return mBuffer;
   }
   void SetBuffer(JSContext* aCx, AudioBuffer* aBuffer)
   {
     mBuffer = aBuffer;
--- a/content/media/webaudio/AudioContext.cpp
+++ b/content/media/webaudio/AudioContext.cpp
@@ -199,49 +199,16 @@ AudioContext::CreateBuffer(JSContext* aJ
   if (!buffer->InitializeBuffers(aNumberOfChannels, aJSContext)) {
     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return nullptr;
   }
 
   return buffer.forget();
 }
 
-already_AddRefed<AudioBuffer>
-AudioContext::CreateBuffer(JSContext* aJSContext, const ArrayBuffer& aBuffer,
-                          bool aMixToMono, ErrorResult& aRv)
-{
-  // Do not accept this method unless the legacy pref has been set.
-  if (!Preferences::GetBool("media.webaudio.legacy.AudioContext")) {
-    aRv.ThrowNotEnoughArgsError();
-    return nullptr;
-  }
-
-  // Sniff the content of the media.
-  // Failed type sniffing will be handled by SyncDecodeMedia.
-  nsAutoCString contentType;
-  NS_SniffContent(NS_DATA_SNIFFER_CATEGORY, nullptr,
-                  aBuffer.Data(), aBuffer.Length(),
-                  contentType);
-
-  nsRefPtr<WebAudioDecodeJob> job =
-    new WebAudioDecodeJob(contentType, this, aBuffer);
-
-  if (mDecoder.SyncDecodeMedia(contentType.get(),
-                               aBuffer.Data(), aBuffer.Length(), *job) &&
-      job->mOutput) {
-    nsRefPtr<AudioBuffer> buffer = job->mOutput.forget();
-    if (aMixToMono) {
-      buffer->MixToMono(aJSContext);
-    }
-    return buffer.forget();
-  }
-
-  return nullptr;
-}
-
 namespace {
 
 bool IsValidBufferSize(uint32_t aBufferSize) {
   switch (aBufferSize) {
   case 0:       // let the implementation choose the buffer size
   case 256:
   case 512:
   case 1024:
--- a/content/media/webaudio/AudioContext.h
+++ b/content/media/webaudio/AudioContext.h
@@ -121,68 +121,42 @@ public:
 
   already_AddRefed<AudioBufferSourceNode> CreateBufferSource();
 
   already_AddRefed<AudioBuffer>
   CreateBuffer(JSContext* aJSContext, uint32_t aNumberOfChannels,
                uint32_t aLength, float aSampleRate,
                ErrorResult& aRv);
 
-  already_AddRefed<AudioBuffer>
-  CreateBuffer(JSContext* aJSContext, const ArrayBuffer& aBuffer,
-               bool aMixToMono, ErrorResult& aRv);
-
   already_AddRefed<MediaStreamAudioDestinationNode>
   CreateMediaStreamDestination(ErrorResult& aRv);
 
   already_AddRefed<ScriptProcessorNode>
   CreateScriptProcessor(uint32_t aBufferSize,
                         uint32_t aNumberOfInputChannels,
                         uint32_t aNumberOfOutputChannels,
                         ErrorResult& aRv);
 
-  already_AddRefed<ScriptProcessorNode>
-  CreateJavaScriptNode(uint32_t aBufferSize,
-                       uint32_t aNumberOfInputChannels,
-                       uint32_t aNumberOfOutputChannels,
-                       ErrorResult& aRv)
-  {
-    return CreateScriptProcessor(aBufferSize, aNumberOfInputChannels,
-                                 aNumberOfOutputChannels, aRv);
-  }
-
   already_AddRefed<AnalyserNode>
   CreateAnalyser();
 
   already_AddRefed<GainNode>
   CreateGain();
 
   already_AddRefed<WaveShaperNode>
   CreateWaveShaper();
 
-  already_AddRefed<GainNode>
-  CreateGainNode()
-  {
-    return CreateGain();
-  }
-
   already_AddRefed<MediaElementAudioSourceNode>
   CreateMediaElementSource(HTMLMediaElement& aMediaElement, ErrorResult& aRv);
   already_AddRefed<MediaStreamAudioSourceNode>
   CreateMediaStreamSource(DOMMediaStream& aMediaStream, ErrorResult& aRv);
 
   already_AddRefed<DelayNode>
   CreateDelay(double aMaxDelayTime, ErrorResult& aRv);
 
-  already_AddRefed<DelayNode>
-  CreateDelayNode(double aMaxDelayTime, ErrorResult& aRv)
-  {
-    return CreateDelay(aMaxDelayTime, aRv);
-  }
-
   already_AddRefed<PannerNode>
   CreatePanner();
 
   already_AddRefed<ConvolverNode>
   CreateConvolver();
 
   already_AddRefed<ChannelSplitterNode>
   CreateChannelSplitter(uint32_t aNumberOfOutputs, ErrorResult& aRv);
--- a/content/media/webaudio/AudioParam.h
+++ b/content/media/webaudio/AudioParam.h
@@ -104,20 +104,16 @@ public:
     if (!WebAudioUtils::IsTimeValid(aStartTime) ||
         !WebAudioUtils::IsTimeValid(aTimeConstant)) {
       aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
       return;
     }
     AudioParamTimeline::SetTargetAtTime(aTarget, DOMTimeToStreamTime(aStartTime), aTimeConstant, aRv);
     mCallback(mNode);
   }
-  void SetTargetValueAtTime(float aTarget, double aStartTime, double aTimeConstant, ErrorResult& aRv)
-  {
-    SetTargetAtTime(aTarget, aStartTime, aTimeConstant, aRv);
-  }
   void CancelScheduledValues(double aStartTime, ErrorResult& aRv)
   {
     if (!WebAudioUtils::IsTimeValid(aStartTime)) {
       aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
       return;
     }
     AudioParamTimeline::CancelScheduledValues(DOMTimeToStreamTime(aStartTime));
     mCallback(mNode);
--- a/content/media/webaudio/MediaBufferDecoder.cpp
+++ b/content/media/webaudio/MediaBufferDecoder.cpp
@@ -127,18 +127,16 @@ private:
                               NS_DISPATCH_NORMAL);
 
       nsCOMPtr<nsIRunnable> event =
         new ReportResultTask(mDecodeJob, &WebAudioDecodeJob::OnFailure, aErrorCode);
       NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
     }
   }
 
-  void RunNextPhase();
-
   void Decode();
   void AllocateBuffer();
   void CallbackTheResult();
 
   void Cleanup()
   {
     MOZ_ASSERT(NS_IsMainThread());
     // MediaDecoderReader expects that BufferDecoder is alive.
@@ -202,40 +200,16 @@ MediaDecodeTask::CreateReader()
   nsresult rv = mDecoderReader->Init(nullptr);
   if (NS_FAILED(rv)) {
     return false;
   }
 
   return true;
 }
 
-void
-MediaDecodeTask::RunNextPhase()
-{
-  // This takes care of handling the logic of where to run the next phase.
-  // If we were invoked synchronously, we do not have a thread pool and
-  // everything happens on the main thread. Just invoke Run() in that case.
-  // Otherwise, some things happen on the main thread and others are run
-  // in the thread pool.
-  if (!mThreadPool) {
-    Run();
-    return;
-  }
-
-  switch (mPhase) {
-  case PhaseEnum::AllocateBuffer:
-    MOZ_ASSERT(!NS_IsMainThread());
-    NS_DispatchToMainThread(this);
-    break;
-  case PhaseEnum::Decode:
-  case PhaseEnum::Done:
-    MOZ_CRASH("Invalid phase Decode");
-  }
-}
-
 class AutoResampler {
 public:
   AutoResampler()
     : mResampler(nullptr)
   {}
   ~AutoResampler()
   {
     if (mResampler) {
@@ -254,18 +228,17 @@ public:
 
 private:
   SpeexResamplerState* mResampler;
 };
 
 void
 MediaDecodeTask::Decode()
 {
-  MOZ_ASSERT(!mThreadPool == NS_IsMainThread(),
-             "We should be on the main thread only if we don't have a thread pool");
+  MOZ_ASSERT(!NS_IsMainThread());
 
   mBufferDecoder->BeginDecoding(NS_GetCurrentThread());
 
   // Tell the decoder reader that we are not going to play the data directly,
   // and that we should not reject files with more channels than the audio
   // bakend support.
   mDecoderReader->SetIgnoreAudioOutputFormat();
 
@@ -390,17 +363,17 @@ MediaDecodeTask::Decode()
         mDecodeJob.mWriteIndex += outSamples;
         MOZ_ASSERT(mDecodeJob.mWriteIndex <= resampledFrames);
         MOZ_ASSERT(inSamples == inputLatency);
       }
     }
   }
 
   mPhase = PhaseEnum::AllocateBuffer;
-  RunNextPhase();
+  NS_DispatchToMainThread(this);
 }
 
 void
 MediaDecodeTask::AllocateBuffer()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!mDecodeJob.AllocateBuffer()) {
@@ -485,39 +458,16 @@ MediaBufferDecoder::AsyncDecodeMedia(con
                            WebAudioDecodeJob::UnknownError);
     NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
   } else {
     mThreadPool->Dispatch(task, nsIThreadPool::DISPATCH_NORMAL);
   }
 }
 
 bool
-MediaBufferDecoder::SyncDecodeMedia(const char* aContentType, uint8_t* aBuffer,
-                                    uint32_t aLength,
-                                    WebAudioDecodeJob& aDecodeJob)
-{
-  // Do not attempt to decode the media if we were not successful at sniffing
-  // the content type.
-  if (!*aContentType ||
-      strcmp(aContentType, APPLICATION_OCTET_STREAM) == 0) {
-    return false;
-  }
-
-  nsRefPtr<MediaDecodeTask> task =
-    new MediaDecodeTask(aContentType, aBuffer, aLength, aDecodeJob, nullptr);
-  if (!task->CreateReader()) {
-    return false;
-  }
-
-  task->Run();
-  return true;
-}
-
-
-bool
 MediaBufferDecoder::EnsureThreadPoolInitialized()
 {
   if (!mThreadPool) {
     mThreadPool = SharedThreadPool::Get(NS_LITERAL_CSTRING("MediaBufferDecoder"));
     if (!mThreadPool) {
       return false;
     }
   }
@@ -531,25 +481,22 @@ WebAudioDecodeJob::WebAudioDecodeJob(con
                                      DecodeErrorCallback* aFailureCallback)
   : mContentType(aContentType)
   , mWriteIndex(0)
   , mContext(aContext)
   , mSuccessCallback(aSuccessCallback)
   , mFailureCallback(aFailureCallback)
 {
   MOZ_ASSERT(aContext);
+  MOZ_ASSERT(aSuccessCallback);
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_COUNT_CTOR(WebAudioDecodeJob);
 
   mArrayBuffer = aBuffer.Obj();
 
-  MOZ_ASSERT(aSuccessCallback ||
-             (!aSuccessCallback && !aFailureCallback),
-             "If a success callback is not passed, no failure callback should be passed either");
-
   mozilla::HoldJSObjects(this);
 }
 
 WebAudioDecodeJob::~WebAudioDecodeJob()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_COUNT_DTOR(WebAudioDecodeJob);
   mArrayBuffer = nullptr;
@@ -559,20 +506,18 @@ WebAudioDecodeJob::~WebAudioDecodeJob()
 void
 WebAudioDecodeJob::OnSuccess(ErrorCode aErrorCode)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aErrorCode == NoError);
 
   // Ignore errors in calling the callback, since there is not much that we can
   // do about it here.
-  if (mSuccessCallback) {
-    ErrorResult rv;
-    mSuccessCallback->Call(*mOutput, rv);
-  }
+  ErrorResult rv;
+  mSuccessCallback->Call(*mOutput, rv);
 
   mContext->RemoveFromDecodeQueue(this);
 }
 
 void
 WebAudioDecodeJob::OnFailure(ErrorCode aErrorCode)
 {
   MOZ_ASSERT(NS_IsMainThread());
--- a/content/media/webaudio/MediaBufferDecoder.h
+++ b/content/media/webaudio/MediaBufferDecoder.h
@@ -75,19 +75,16 @@ struct WebAudioDecodeJob MOZ_FINAL
  * thread-pool) and provides a clean external interface.
  */
 class MediaBufferDecoder
 {
 public:
   void AsyncDecodeMedia(const char* aContentType, uint8_t* aBuffer,
                         uint32_t aLength, WebAudioDecodeJob& aDecodeJob);
 
-  bool SyncDecodeMedia(const char* aContentType, uint8_t* aBuffer,
-                       uint32_t aLength, WebAudioDecodeJob& aDecodeJob);
-
   size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
   {
     return 0;
   }
 
 private:
   bool EnsureThreadPoolInitialized();
 
--- a/content/media/webaudio/test/mochitest.ini
+++ b/content/media/webaudio/test/mochitest.ini
@@ -21,17 +21,16 @@ support-files =
   ting-44.1k-2ch.wav
   ting-48k-1ch.wav
   ting-48k-2ch.wav
   webaudio.js
 
 [test_AudioBuffer.html]
 [test_AudioContext.html]
 [test_AudioListener.html]
-[test_AudioParam.html]
 [test_OfflineAudioContext.html]
 [test_analyserNode.html]
 [test_audioBufferSourceNode.html]
 [test_audioBufferSourceNodeEnded.html]
 [test_audioBufferSourceNodeLazyLoopParam.html]
 [test_audioBufferSourceNodeLoop.html]
 [test_audioBufferSourceNodeLoopStartEnd.html]
 [test_audioBufferSourceNodeLoopStartEndSame.html]
deleted file mode 100644
--- a/content/media/webaudio/test/test_AudioParam.html
+++ /dev/null
@@ -1,36 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Test AudioParam</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="text/javascript" src="webaudio.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-</head>
-<body>
-<pre id="test">
-<script class="testbody" type="text/javascript">
-
-var context = new AudioContext();
-var gain = context.createGain().gain;
-
-ok("value" in gain, "The value attr must exist");
-gain.value = 0.5;
-ok("defaultValue" in gain, "The defaultValue attr must exist");
-(function() {
-  "use strict"; // in order to get the readOnly setter to throw
-  expectTypeError(function() {
-    gain.defaultValue = 0.5;
-  });
-})();
-
-gain.setValueAtTime(1, 0.25);
-gain.linearRampToValueAtTime(0.75, 0.5);
-gain.exponentialRampToValueAtTime(0.1, 0.75);
-gain.setTargetAtTime(0.2, 1, 0.5);
-gain.setTargetValueAtTime(0.3, 1.25, 0.5);
-gain.cancelScheduledValues(1.5);
-
-</script>
-</pre>
-</body>
-</html>
--- a/content/media/webaudio/test/test_delayNode.html
+++ b/content/media/webaudio/test/test_delayNode.html
@@ -18,40 +18,30 @@ var gTest = {
     for (var i = 0; i < 2048; ++i) {
       buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
     }
 
     var source = context.createBufferSource();
 
     var delay = context.createDelay();
 
-    var delay2 = context.createDelayNode();
-    isnot(delay, delay2, "createDelayNode should create a different delay node");
-
     source.buffer = buffer;
 
     source.connect(delay);
 
     ok(delay.delayTime, "The audioparam member must exist");
     is(delay.delayTime.value, 0, "Correct initial value");
     is(delay.delayTime.defaultValue, 0, "Correct default value");
     delay.delayTime.value = 0.5;
     is(delay.delayTime.value, 0.5, "Correct initial value");
     is(delay.delayTime.defaultValue, 0, "Correct default value");
     is(delay.channelCount, 2, "delay node has 2 input channels by default");
     is(delay.channelCountMode, "max", "Correct channelCountMode for the delay node");
     is(delay.channelInterpretation, "speakers", "Correct channelCountInterpretation for the delay node");
 
-    var delay2 = context.createDelay(2);
-    is(delay2.delayTime.value, 0, "Correct initial value");
-    is(delay2.delayTime.defaultValue, 0, "Correct default value");
-    delay2.delayTime.value = 0.5;
-    is(delay2.delayTime.value, 0.5, "Correct initial value");
-    is(delay2.delayTime.defaultValue, 0, "Correct default value");
-
     expectException(function() {
       context.createDelay(0);
     }, DOMException.NOT_SUPPORTED_ERR);
     expectException(function() {
       context.createDelay(180);
     }, DOMException.NOT_SUPPORTED_ERR);
     expectTypeError(function() {
       context.createDelay(NaN);
--- a/content/media/webaudio/test/test_gainNode.html
+++ b/content/media/webaudio/test/test_gainNode.html
@@ -18,19 +18,16 @@ var gTest = {
     for (var i = 0; i < 2048; ++i) {
       buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
     }
 
     var source = context.createBufferSource();
 
     var gain = context.createGain();
 
-    var gain2 = context.createGainNode();
-    isnot(gain, gain2, "createGainNode should create a different gain node");
-
     source.buffer = buffer;
 
     source.connect(gain);
 
     ok(gain.gain, "The audioparam member must exist");
     is(gain.gain.value, 1.0, "Correct initial value");
     is(gain.gain.defaultValue, 1.0, "Correct default value");
     gain.gain.value = 0.5;
--- a/content/media/webaudio/test/test_mediaDecoding.html
+++ b/content/media/webaudio/test/test_mediaDecoding.html
@@ -297,36 +297,32 @@ function checkResampledBuffer(buffer, te
   cx.startRendering();
 }
 
 function runResampling(test, response, callback) {
   var sampleRate = test.sampleRate == 44100 ? 48000 : 44100;
   var cx = new OfflineAudioContext(1, 1, sampleRate);
   cx.decodeAudioData(response, function onSuccess(asyncResult) {
     is(asyncResult.sampleRate, sampleRate, "Correct sample rate");
-    syncResult = cx.createBuffer(response, false);
-    compareBuffers(syncResult, asyncResult);
 
     checkResampledBuffer(asyncResult, test, callback);
   }, function onFailure() {
     ok(false, "Expected successful decode with resample");
     callback();
   });
 }
 
 function runTest(test, response, callback) {
   var expectCallback = false;
   var cx = new OfflineAudioContext(test.numberOfChannels || 1,
                                    test.frames || 1, test.sampleRate);
   cx.decodeAudioData(response, function onSuccess(asyncResult) {
     ok(expectCallback, "Success callback should fire asynchronously");
     ok(test.valid, "Did expect success for test " + test.url);
 
-    syncResult = cx.createBuffer(response, false);
-    compareBuffers(syncResult, asyncResult);
     checkAudioBuffer(asyncResult, test);
 
     test.expectedBuffer = asyncResult;
     test.nativeContext = cx;
     runResampling(test, response, callback);
   }, function onFailure() {
     ok(expectCallback, "Failure callback should fire asynchronously");
     ok(!test.valid, "Did expect failure for test " + test.url);
--- a/content/media/webaudio/test/test_scriptProcessorNode.html
+++ b/content/media/webaudio/test/test_scriptProcessorNode.html
@@ -15,17 +15,17 @@
 // not be easy to map to OfflineAudioContext, as ScriptProcessorNodes
 // can experience delays.
 
 SimpleTest.waitForExplicitFinish();
 addLoadEvent(function() {
   var context = new AudioContext();
   var buffer = null;
 
-  var sourceSP = context.createJavaScriptNode(2048);
+  var sourceSP = context.createScriptProcessor(2048);
   sourceSP.addEventListener("audioprocess", function(e) {
     // generate the audio
     for (var i = 0; i < 2048; ++i) {
       // Make sure our first sample won't be zero
       e.outputBuffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * (i + 1) / context.sampleRate);
       e.outputBuffer.getChannelData(0)[i] = Math.sin(880 * 2 * Math.PI * (i + 1) / context.sampleRate);
     }
     // Remember our generated audio
--- a/content/media/webaudio/test/test_singleSourceDest.html
+++ b/content/media/webaudio/test/test_singleSourceDest.html
@@ -23,64 +23,16 @@ addLoadEvent(function() {
   is(destination.context, context, "Destination node has proper context");
   is(destination.numberOfInputs, 1, "Destination node has 1 inputs");
   is(destination.numberOfOutputs, 0, "Destination node has 0 outputs");
   is(destination.channelCount, 2, "Destination node has 2 input channels by default");
   is(destination.channelCountMode, "explicit", "Correct channelCountMode for the destination node");
   is(destination.channelInterpretation, "speakers", "Correct channelCountInterpretation for the destination node");
   ok(destination instanceof EventTarget, "AudioNodes must be EventTargets");
 
-  testWith(context, buffer, destination, function(source) {
-    source.start(0);
-  }, function(source) {
-    source.stop();
-  }, function() {
-    testWith(context, buffer, destination, function(source) {
-      source.start(0, 1);
-    }, function(source) {
-      expectTypeError(function() {
-        source.noteOff();
-      });
-      source.noteOff(0);
-    }, function() {
-      testWith(context, buffer, destination, function(source) {
-        source.start(0, 1, 0.5);
-      }, function(source) {
-        source.stop(0);
-      }, function() {
-        testWith(context, buffer, destination, function(source) {
-          source.noteOn(0);
-        }, function(source) {
-          source.noteOff(0);
-        }, function() {
-          testWith(context, buffer, destination, function(source) {
-            source.noteGrainOn(0, 1, 0.5);
-          }, function(source) {
-            source.stop();
-          }, function() {
-            SimpleTest.finish();
-          });
-        });
-      });
-    });
-  });
-});
-
-function testWith(context, buffer, destination, start, stop, callback)
-{
-  var source = createNode(context, buffer, destination);
-  start(source);
-  SimpleTest.executeSoon(function() {
-    stop(source);
-    callback();
-    source.disconnect();
-  });
-}
-
-function createNode(context, buffer, destination) {
   var source = context.createBufferSource();
   is(source.context, context, "Source node has proper context");
   is(source.numberOfInputs, 0, "Source node has 0 inputs");
   is(source.numberOfOutputs, 1, "Source node has 1 outputs");
   is(source.loop, false, "Source node is not looping");
   is(source.loopStart, 0, "Correct default value for loopStart");
   is(source.loopEnd, 0, "Correct default value for loopEnd");
   ok(!source.buffer, "Source node should not have a buffer when it's created");
@@ -97,15 +49,22 @@ function createNode(context, buffer, des
 
   source.connect(destination);
 
   is(source.numberOfInputs, 0, "Source node has 0 inputs");
   is(source.numberOfOutputs, 1, "Source node has 0 outputs");
   is(destination.numberOfInputs, 1, "Destination node has 0 inputs");
   is(destination.numberOfOutputs, 0, "Destination node has 0 outputs");
 
-  return source;
-}
+  source.start(0);
+  SimpleTest.executeSoon(function() {
+    source.stop(0);
+    source.disconnect();
+
+    SpecialPowers.clearUserPref("media.webaudio.enabled");
+    SimpleTest.finish();
+  });
+});
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/webm/EbmlComposer.cpp
+++ b/content/media/webm/EbmlComposer.cpp
@@ -26,17 +26,18 @@ void EbmlComposer::GenerateHeader()
   ebml.buf = buffer.get();
   ebml.offset = 0;
   writeHeader(&ebml);
   {
     EbmlLoc segEbmlLoc, ebmlLocseg, ebmlLoc;
     Ebml_StartSubElement(&ebml, &segEbmlLoc, Segment);
     {
       Ebml_StartSubElement(&ebml, &ebmlLocseg, SeekHead);
-      // Todo: We don't know the exact sizes of encoded data and ignore this section.
+      // Todo: We don't know the exact sizes of encoded data and
+      // ignore this section.
       Ebml_EndSubElement(&ebml, &ebmlLocseg);
       writeSegmentInformation(&ebml, &ebmlLoc, TIME_CODE_SCALE, 0);
       {
         EbmlLoc trackLoc;
         Ebml_StartSubElement(&ebml, &trackLoc, Tracks);
         {
           // Video
           if (mWidth > 0 && mHeight > 0) {
@@ -49,82 +50,106 @@ void EbmlComposer::GenerateHeader()
             writeAudioTrack(&ebml, 0x2, 0x0, "A_VORBIS", mSampleFreq,
                             mChannels, mCodecPrivateData.Elements(),
                             mCodecPrivateData.Length());
           }
         }
         Ebml_EndSubElement(&ebml, &trackLoc);
       }
     }
-    // The Recording length is unknow and ignore write the whole Segment element size
+    // The Recording length is unknown and
+    // ignore write the whole Segment element size
   }
   MOZ_ASSERT(ebml.offset <= DEFAULT_HEADER_SIZE + mCodecPrivateData.Length(),
              "write more data > EBML_BUFFER_SIZE");
-  mClusterBuffs.AppendElement();
-  mClusterBuffs.LastElement().SetLength(ebml.offset);
-  memcpy(mClusterBuffs.LastElement().Elements(), ebml.buf, ebml.offset);
+  auto block = mClusterBuffs.AppendElement();
+  block->SetLength(ebml.offset);
+  memcpy(block->Elements(), ebml.buf, ebml.offset);
+  mFlushState |= FLUSH_METADATA;
+}
+
+void EbmlComposer::FinishMetadata()
+{
+  if (mFlushState & FLUSH_METADATA) {
+    // We don't remove the first element of mClusterBuffs because the
+    // |mClusterHeaderIndex| may have value.
+    mClusterCanFlushBuffs.AppendElement()->SwapElements(mClusterBuffs[0]);
+    mFlushState &= ~FLUSH_METADATA;
+  }
 }
 
 void EbmlComposer::FinishCluster()
 {
-  MOZ_ASSERT(mClusterLengthLoc > 0 );
-  MOZ_ASSERT(mClusterHeaderIndex > 0);
-  for (uint32_t i = 0; i < mClusterBuffs.Length(); i ++ ) {
-    mClusterCanFlushBuffs.AppendElement()->SwapElements(mClusterBuffs[i]);
+  FinishMetadata();
+  if (!(mFlushState & FLUSH_CLUSTER)) {
+    // No completed cluster available.
+    return;
   }
-  mClusterBuffs.Clear();
+
+  MOZ_ASSERT(mClusterLengthLoc > 0);
   EbmlGlobal ebml;
   EbmlLoc ebmlLoc;
   ebmlLoc.offset = mClusterLengthLoc;
-  ebml.offset = mClusterCanFlushBuffs[mClusterHeaderIndex].Length();
-  ebml.buf = mClusterCanFlushBuffs[mClusterHeaderIndex].Elements();
+  ebml.offset = mClusterBuffs[mClusterHeaderIndex].Length();
+  ebml.buf = mClusterBuffs[mClusterHeaderIndex].Elements();
   Ebml_EndSubElement(&ebml, &ebmlLoc);
+  // Move the mClusterBuffs data from mClusterHeaderIndex that we can skip
+  // the metadata and the rest P-frames after ContainerWriter::FLUSH_NEEDED.
+  for (uint32_t i = mClusterHeaderIndex; i < mClusterBuffs.Length(); i++) {
+    mClusterCanFlushBuffs.AppendElement()->SwapElements(mClusterBuffs[i]);
+  }
+
   mClusterHeaderIndex = 0;
   mClusterLengthLoc = 0;
+  mClusterBuffs.Clear();
+  mFlushState &= ~FLUSH_CLUSTER;
 }
 
 void
 EbmlComposer::WriteSimpleBlock(EncodedFrame* aFrame)
 {
   EbmlGlobal ebml;
   ebml.offset = 0;
 
-  if (aFrame->GetFrameType() == EncodedFrame::FrameType::VP8_I_FRAME && mClusterHeaderIndex > 0) {
+  if (aFrame->GetFrameType() == EncodedFrame::FrameType::VP8_I_FRAME) {
     FinishCluster();
   }
 
-  mClusterBuffs.AppendElement();
-  mClusterBuffs.LastElement().SetLength(aFrame->GetFrameData().Length() + DEFAULT_HEADER_SIZE);
-  ebml.buf = mClusterBuffs.LastElement().Elements();
+  auto block = mClusterBuffs.AppendElement();
+  block->SetLength(aFrame->GetFrameData().Length() + DEFAULT_HEADER_SIZE);
+  ebml.buf = block->Elements();
 
   if (aFrame->GetFrameType() == EncodedFrame::FrameType::VP8_I_FRAME) {
     EbmlLoc ebmlLoc;
     Ebml_StartSubElement(&ebml, &ebmlLoc, Cluster);
-    mClusterHeaderIndex = mClusterBuffs.Length() - 1; // current cluster header array index
+    MOZ_ASSERT(mClusterBuffs.Length() > 0);
+    // current cluster header array index
+    mClusterHeaderIndex = mClusterBuffs.Length() - 1;
     mClusterLengthLoc = ebmlLoc.offset;
-    if (aFrame->GetFrameType() != EncodedFrame::FrameType::VORBIS_AUDIO_FRAME) {
-      mClusterTimecode = aFrame->GetTimeStamp() / PR_USEC_PER_MSEC;
-    }
+    mClusterTimecode = aFrame->GetTimeStamp() / PR_USEC_PER_MSEC;
     Ebml_SerializeUnsigned(&ebml, Timecode, mClusterTimecode);
+    mFlushState |= FLUSH_CLUSTER;
   }
 
   if (aFrame->GetFrameType() != EncodedFrame::FrameType::VORBIS_AUDIO_FRAME) {
-    short timeCode = aFrame->GetTimeStamp() / PR_USEC_PER_MSEC - mClusterTimecode;
+    short timeCode = aFrame->GetTimeStamp() / PR_USEC_PER_MSEC
+                     - mClusterTimecode;
     writeSimpleBlock(&ebml, 0x1, timeCode, aFrame->GetFrameType() ==
                      EncodedFrame::FrameType::VP8_I_FRAME,
                      0, 0, (unsigned char*)aFrame->GetFrameData().Elements(),
                      aFrame->GetFrameData().Length());
   } else {
     writeSimpleBlock(&ebml, 0x2, 0, false,
                      0, 0, (unsigned char*)aFrame->GetFrameData().Elements(),
                      aFrame->GetFrameData().Length());
   }
-  MOZ_ASSERT(ebml.offset <= DEFAULT_HEADER_SIZE + aFrame->GetFrameData().Length(),
+  MOZ_ASSERT(ebml.offset <= DEFAULT_HEADER_SIZE +
+             aFrame->GetFrameData().Length(),
              "write more data > EBML_BUFFER_SIZE");
-  mClusterBuffs.LastElement().SetLength(ebml.offset);
+  block->SetLength(ebml.offset);
 }
 
 void
 EbmlComposer::SetVideoConfig(uint32_t aWidth, uint32_t aHeight,
                              uint32_t aDisplayWidth, uint32_t aDisplayHeight,
                              float aFrameRate)
 {
   MOZ_ASSERT(aWidth > 0, "Width should > 0");
@@ -150,28 +175,35 @@ EbmlComposer::SetAudioConfig(uint32_t aS
   mBitDepth = aBitDepth;
   mChannels = aChannels;
 }
 
 void
 EbmlComposer::ExtractBuffer(nsTArray<nsTArray<uint8_t> >* aDestBufs,
                             uint32_t aFlag)
 {
-  if ((aFlag & ContainerWriter::FLUSH_NEEDED) && mClusterHeaderIndex > 0) {
+  if ((aFlag & ContainerWriter::FLUSH_NEEDED) ||
+      (aFlag & ContainerWriter::GET_HEADER))
+  {
+    FinishMetadata();
+  }
+  if (aFlag & ContainerWriter::FLUSH_NEEDED)
+  {
     FinishCluster();
   }
   // aDestBufs may have some element
-  for (uint32_t i = 0; i < mClusterCanFlushBuffs.Length(); i ++ ) {
+  for (uint32_t i = 0; i < mClusterCanFlushBuffs.Length(); i++) {
     aDestBufs->AppendElement()->SwapElements(mClusterCanFlushBuffs[i]);
   }
   mClusterCanFlushBuffs.Clear();
 }
 
 EbmlComposer::EbmlComposer()
-  : mClusterHeaderIndex(0)
+  : mFlushState(FLUSH_NONE)
+  , mClusterHeaderIndex(0)
   , mClusterLengthLoc(0)
   , mClusterTimecode(0)
   , mWidth(0)
   , mHeight(0)
   , mFrameRate(0)
   , mSampleFreq(0)
   , mBitDepth(0)
   , mChannels(0)
--- a/content/media/webm/EbmlComposer.h
+++ b/content/media/webm/EbmlComposer.h
@@ -41,23 +41,33 @@ public:
    */
   void WriteSimpleBlock(EncodedFrame* aFrame);
   /*
    * Get valid cluster data.
    */
   void ExtractBuffer(nsTArray<nsTArray<uint8_t> >* aDestBufs,
                      uint32_t aFlag = 0);
 private:
+  // Move the metadata data to mClusterCanFlushBuffs.
+  void FinishMetadata();
   // Close current cluster and move data to mClusterCanFlushBuffs.
   void FinishCluster();
   // The temporary storage for cluster data.
   nsTArray<nsTArray<uint8_t> > mClusterBuffs;
   // The storage which contain valid cluster data.
   nsTArray<nsTArray<uint8_t> > mClusterCanFlushBuffs;
-  // Indicate the header index in mClusterBuffs.
+
+  // Indicate the data types in mClusterBuffs.
+  enum {
+    FLUSH_NONE = 0,
+    FLUSH_METADATA = 1 << 0,
+    FLUSH_CLUSTER = 1 << 1
+  };
+  uint32_t mFlushState;
+  // Indicate the cluster header index in mClusterBuffs.
   uint32_t mClusterHeaderIndex;
   // The cluster length position.
   uint64_t mClusterLengthLoc;
   // Audio codec specific header data.
   nsTArray<uint8_t> mCodecPrivateData;
 
   // The timecode of the cluster.
   uint64_t mClusterTimecode;
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -477,17 +477,17 @@ public:
             sameOrigin = false;
           }
         }
 
         NS_NAMED_LITERAL_STRING(xoriginMsg, "Script error.");
         if (sameOrigin) {
           init.mMessage = mErrorMsg;
           init.mLineno = mLineNumber;
-          init.mColumn = mColumn;
+          init.mColno = mColumn;
           init.mError = mError;
         } else {
           NS_WARNING("Not same origin error!");
           init.mMessage = xoriginMsg;
           init.mLineno = 0;
         }
 
         nsRefPtr<ErrorEvent> event =
--- a/dom/events/JSEventHandler.cpp
+++ b/dom/events/JSEventHandler.cpp
@@ -137,17 +137,17 @@ JSEventHandler::HandleEvent(nsIDOMEvent*
 
       scriptEvent->GetFilename(file);
       fileName = &file;
 
       lineNumber.Construct();
       lineNumber.Value() = scriptEvent->Lineno();
 
       columnNumber.Construct();
-      columnNumber.Value() = scriptEvent->Column();
+      columnNumber.Value() = scriptEvent->Colno();
 
       ThreadsafeAutoJSContext cx;
       error.Construct(cx);
       error.Value() = scriptEvent->Error(cx);
     } else {
       msgOrEvent.SetAsEvent() = aEvent->InternalDOMEvent();
     }
 
--- a/dom/events/test/test_error_events.html
+++ b/dom/events/test/test_error_events.html
@@ -32,17 +32,17 @@
     [ "Event filename", errorEvent.filename, location.href ],
     [ "Callback filename", file, location.href ],
     [ "Event line number", errorEvent.lineno, 27 ],
     [ "Callback line number", line, 27 ],
     [ "Event message", errorEvent.message, "Error: hello" ],
     [ "Callback message", msg, "Error: hello" ],
     [ "Event error-object", errorEvent.error, thrown],
     [ "Callback error-object", error, thrown ],
-    [ "Event column", errorEvent.column, 0 ], // Sadly not correct right now
+    [ "Event column", errorEvent.colno, 0 ], // Sadly not correct right now
     [ "Callback column", column, 0 ]
   ]);
 </script>
 <script>
   var workerLocation = location.protocol + "//" + location.host +
     location.pathname.replace("test_error_events.html", "error_event_worker.js");
   var eventFileTest = async_test("Worker event filename");
   var eventLineTest = async_test("Worker event line number");
--- a/dom/file/ArchiveZipEvent.cpp
+++ b/dom/file/ArchiveZipEvent.cpp
@@ -3,18 +3,16 @@
 /* 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 "ArchiveZipEvent.h"
 #include "ArchiveZipFile.h"
 
 #include "nsContentUtils.h"
-#include "nsIPlatformCharset.h"
-#include "nsNativeCharsetUtils.h"
 #include "nsCExternalHandlerService.h"
 
 using namespace mozilla::dom;
 
 USING_FILE_NAMESPACE
 
 #ifndef PATH_MAX
 #  define PATH_MAX 65536 // The filename length is stored in 2 bytes
--- a/dom/identity/nsDOMIdentity.js
+++ b/dom/identity/nsDOMIdentity.js
@@ -164,24 +164,23 @@ nsDOMIdentity.prototype = {
     }
 
     let util = this._window.QueryInterface(Ci.nsIInterfaceRequestor)
                            .getInterface(Ci.nsIDOMWindowUtils);
 
     let message = this.DOMIdentityMessage(aOptions);
 
     // We permit calling of request() outside of a user input handler only when
-    // we are handling the (deprecated) get() or getVerifiedEmail() calls,
-    // which make use of an RP context marked as _internal, or when a certified
-    // app is calling.
-    //
-    // XXX Bug 982460 - grant the same privilege to packaged apps
+    // a certified or privileged app is calling, or when we are handling the
+    // (deprecated) get() or getVerifiedEmail() calls, which make use of an RP
+    // context marked as _internal.
 
     if (!aOptions._internal &&
-        this._appStatus !== Ci.nsIPrincipal.APP_STATUS_CERTIFIED) {
+        this._appStatus !== Ci.nsIPrincipal.APP_STATUS_CERTIFIED &&
+        this._appStatus !== Ci.nsIPrincipal.APP_STATUS_PRIVILEGED) {
 
       // If the caller is not special in one of those ways, see if the user has
       // preffed on 'syntheticEventsOk' (useful for testing); otherwise, if
       // this is a non-native event, reject it.
       let util = this._window.QueryInterface(Ci.nsIInterfaceRequestor)
                              .getInterface(Ci.nsIDOMWindowUtils);
 
       if (!util.isHandlingUserInput && this.nativeEventsRequired) {
--- a/dom/identity/tests/mochitest/file_syntheticEvents.html
+++ b/dom/identity/tests/mochitest/file_syntheticEvents.html
@@ -2,17 +2,18 @@
   * 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/. */
  -->
 <!DOCTYPE html>
 <html>
   <!--
   Certified and privileged apps can call mozId outside an event handler
-  https://bugzilla.mozilla.org/show_bug.cgi?id=971379
+  Certified apps:  https://bugzilla.mozilla.org/show_bug.cgi?id=971379
+  Privileged apps: https://bugzilla.mozilla.org/show_bug.cgi?id=982460
   -->
 <head>
   <meta charset="utf-8">
   <title>Test app for bug 971379</title>
 </head>
 
 <body>
     <div id='test'>
--- a/dom/identity/tests/mochitest/test_syntheticEvents.html
+++ b/dom/identity/tests/mochitest/test_syntheticEvents.html
@@ -89,26 +89,23 @@ let apps = [
     expected: {
       success: false,
       errors: [
         "ERROR_NOT_AUTHORIZED_FOR_FIREFOX_ACCOUNTS",
       ],
     },
   },
   {
-    title: "a privileged app, which may not use synthetic events (until bug 982460 lands)",
+    title: "a privileged app, which may use synthetic events",
     manifest: "https://example.com/manifest_priv.webapp",
     origin: "https://example.com",
     uri: "https://example.com/chrome/dom/identity/tests/mochitest/file_syntheticEvents.html",
     wantIssuer: "firefox-accounts",
     expected: {
-      success: false,
-      errors: [
-        "ERROR_REQUEST_WHILE_NOT_HANDLING_USER_INPUT",
-      ],
+      success: true,
     },
   },
   {
     title: "a certified app, which may use synthetic events",
     manifest: "https://example.com/manifest_cert.webapp",
     origin: "https://example.com",
     uri: "https://example.com/chrome/dom/identity/tests/mochitest/file_syntheticEvents.html",
     wantIssuer: "firefox-accounts",
--- a/dom/webidl/AudioBufferSourceNode.webidl
+++ b/dom/webidl/AudioBufferSourceNode.webidl
@@ -23,25 +23,8 @@ interface AudioBufferSourceNode : AudioN
     [Throws]
     void start(optional double when = 0, optional double grainOffset = 0,
                optional double grainDuration);
     [Throws]
     void stop(optional double when = 0);
 
     attribute EventHandler onended;
 };
-
-/*
- * The origin of this IDL file is
- * https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html#AlternateNames
- */
-partial interface AudioBufferSourceNode {
-    // Same as start()
-    [Throws,Pref="media.webaudio.legacy.AudioBufferSourceNode"]
-    void noteOn(double when);
-    [Throws,Pref="media.webaudio.legacy.AudioBufferSourceNode"]
-    void noteGrainOn(double when, double grainOffset, double grainDuration);
-    
-    [Throws,Pref="media.webaudio.legacy.AudioBufferSourceNode"]
-    // Same as stop()
-    void noteOff(double when);
-};
-
--- a/dom/webidl/AudioContext.webidl
+++ b/dom/webidl/AudioContext.webidl
@@ -69,37 +69,14 @@ interface AudioContext : EventTarget {
 
     [NewObject]
     OscillatorNode createOscillator();
     [NewObject, Throws]
     PeriodicWave createPeriodicWave(Float32Array real, Float32Array imag);
 
 };
 
-/*
- * The origin of this IDL file is
- * https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html#AlternateNames
- */
-partial interface AudioContext {
-    [NewObject, Throws]
-    AudioBuffer? createBuffer(ArrayBuffer buffer, boolean mixToMono);
-
-    // Same as createGain()
-    [NewObject,Pref="media.webaudio.legacy.AudioContext"]
-    GainNode createGainNode();
-
-    // Same as createDelay()
-    [NewObject, Throws, Pref="media.webaudio.legacy.AudioContext"]
-    DelayNode createDelayNode(optional double maxDelayTime = 1);
-
-    // Same as createScriptProcessor()
-    [NewObject, Throws, Pref="media.webaudio.legacy.AudioContext"]
-    ScriptProcessorNode createJavaScriptNode(optional unsigned long bufferSize = 0,
-                                             optional unsigned long numberOfInputChannels = 2,
-                                             optional unsigned long numberOfOutputChannels = 2);
-};
-
 // Mozilla extensions
 partial interface AudioContext {
   // Read AudioChannel.webidl for more information about this attribute.
   [Pref="media.useAudioChannelService", SetterThrows]
   attribute AudioChannel mozAudioChannelType;
 };
--- a/dom/webidl/AudioParam.webidl
+++ b/dom/webidl/AudioParam.webidl
@@ -32,19 +32,8 @@ interface AudioParam {
     [Throws]
     void setValueCurveAtTime(Float32Array values, double startTime, double duration);
 
     // Cancels all scheduled parameter changes with times greater than or equal to startTime. 
     [Throws]
     void cancelScheduledValues(double startTime);
 
 };
-
-/*
- * The origin of this IDL file is
- * https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html#AlternateNames
- */
-partial interface AudioParam {
-    // Same as setTargetAtTime()
-    [Throws,Pref="media.webaudio.legacy.AudioParam"]
-    void setTargetValueAtTime(float target, double startTime, double timeConstant);
-};
-
--- a/dom/webidl/ErrorEvent.webidl
+++ b/dom/webidl/ErrorEvent.webidl
@@ -4,20 +4,20 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 [Constructor(DOMString type, optional ErrorEventInit eventInitDict)]
 interface ErrorEvent : Event
 {
   readonly attribute DOMString message;
   readonly attribute DOMString filename;
   readonly attribute unsigned long lineno;
-  readonly attribute unsigned long column;
+  readonly attribute unsigned long colno;
   readonly attribute any error;
 };
 
 dictionary ErrorEventInit : EventInit
 {
   DOMString message = "";
   DOMString filename = "";
   unsigned long lineno = 0;
-  unsigned long column = 0;
+  unsigned long colno = 0;
   any error = null;
 };
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -7,17 +7,16 @@
 #include "RuntimeService.h"
 
 #include "nsIChannel.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsIDocument.h"
 #include "nsIDOMChromeWindow.h"
 #include "nsIEffectiveTLDService.h"
 #include "nsIObserverService.h"
-#include "nsIPlatformCharset.h"
 #include "nsIPrincipal.h"
 #include "nsIScriptContext.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsISupportsPriority.h"
 #include "nsITimer.h"
 #include "nsIURI.h"
 #include "nsPIDOMWindow.h"
 
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -3142,17 +3142,17 @@ WorkerPrivateParent<Derived>::BroadcastE
     }
 
     RootedDictionary<ErrorEventInit> errorInit(aCx);
     errorInit.mBubbles = false;
     errorInit.mCancelable = true;
     errorInit.mMessage = aMessage;
     errorInit.mFilename = aFilename;
     errorInit.mLineno = aLineNumber;
-    errorInit.mColumn = aColumnNumber;
+    errorInit.mColno = aColumnNumber;
 
     nsRefPtr<ErrorEvent> errorEvent =
       ErrorEvent::Constructor(sharedWorker, NS_LITERAL_STRING("error"),
                               errorInit);
     if (!errorEvent) {
       Throw(cx, NS_ERROR_UNEXPECTED);
       JS_ReportPendingException(cx);
       continue;
--- a/dom/workers/test/test_sharedWorker.html
+++ b/dom/workers/test/test_sharedWorker.html
@@ -52,17 +52,17 @@
             receivedMessage = event.data;
           };
 
           worker.onerror = function(event) {
             ok(event instanceof ErrorEvent, "Got an ErrorEvent");
             is(event.message, "Error: " + sentMessage, "Got correct error");
             is(event.filename, errorFilename, "Got correct filename");
             is(event.lineno, errorLine, "Got correct lineno");
-            is(event.column, errorColumn, "Got correct column");
+            is(event.colno, errorColumn, "Got correct column");
             ok(receivedMessage !== undefined, "Got message already");
             ok(receivedError === undefined, "Haven't gotten error yet");
             receivedError = event.message;
             event.preventDefault();
             SimpleTest.finish();
           };
 
           worker.port.postMessage(sentMessage);
--- a/gfx/layers/apz/src/APZCTreeManager.h
+++ b/gfx/layers/apz/src/APZCTreeManager.h
@@ -27,18 +27,19 @@ namespace mozilla {
 class InputData;
 
 namespace layers {
 
 enum AllowedTouchBehavior {
   NONE =               0,
   VERTICAL_PAN =       1 << 0,
   HORIZONTAL_PAN =     1 << 1,
-  ZOOM =               1 << 2,
-  UNKNOWN =            1 << 3
+  PINCH_ZOOM =         1 << 2,
+  DOUBLE_TAP_ZOOM =    1 << 3,
+  UNKNOWN =            1 << 4
 };
 
 class Layer;
 class AsyncPanZoomController;
 class CompositorParent;
 
 /**
  * ****************** NOTE ON LOCK ORDERING IN APZ **************************
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -228,17 +228,18 @@ typedef GeckoContentController::APZState
  * documentation for the skate size multipliers above).
  */
 
 /**
  * Default touch behavior (is used when not touch behavior is set).
  */
 static const uint32_t DefaultTouchBehavior = AllowedTouchBehavior::VERTICAL_PAN |
                                              AllowedTouchBehavior::HORIZONTAL_PAN |
-                                             AllowedTouchBehavior::ZOOM;
+                                             AllowedTouchBehavior::PINCH_ZOOM |
+                                             AllowedTouchBehavior::DOUBLE_TAP_ZOOM;
 
 /**
  * Angle from axis within which we stay axis-locked
  */
 static const double AXIS_LOCK_ANGLE = M_PI / 6.0; // 30 degrees
 
 /**
  * The distance in inches the user must pan before axis lock can be broken
@@ -776,17 +777,17 @@ nsEventStatus AsyncPanZoomController::On
   OnTouchEndOrCancel();
   SetState(NOTHING);
   return nsEventStatus_eConsumeNoDefault;
 }
 
 nsEventStatus AsyncPanZoomController::OnScaleBegin(const PinchGestureInput& aEvent) {
   APZC_LOG("%p got a scale-begin in state %d\n", this, mState);
 
-  if (!TouchActionAllowZoom()) {
+  if (!TouchActionAllowPinchZoom()) {
     return nsEventStatus_eIgnore;
   }
 
   if (!mZoomConstraints.mAllowZoom) {
     return nsEventStatus_eConsumeNoDefault;
   }
 
   SetState(PINCHING);
@@ -967,39 +968,38 @@ void AsyncPanZoomController::OnTouchEndO
         GetGuid(), APZStateChange::EndTouch, mTouchBlockState.mSingleTapOccurred);
   }
 }
 
 nsEventStatus AsyncPanZoomController::OnSingleTapUp(const TapGestureInput& aEvent) {
   APZC_LOG("%p got a single-tap-up in state %d\n", this, mState);
   // If mZoomConstraints.mAllowDoubleTapZoom is true we wait for a call to OnSingleTapConfirmed before
   // sending event to content
-  if (!mZoomConstraints.mAllowDoubleTapZoom) {
+  if (!(mZoomConstraints.mAllowDoubleTapZoom && TouchActionAllowDoubleTapZoom())) {
     return GenerateSingleTap(aEvent.mPoint, aEvent.modifiers);
   }
   return nsEventStatus_eIgnore;
 }
 
 nsEventStatus AsyncPanZoomController::OnSingleTapConfirmed(const TapGestureInput& aEvent) {
   APZC_LOG("%p got a single-tap-confirmed in state %d\n", this, mState);
   return GenerateSingleTap(aEvent.mPoint, aEvent.modifiers);
 }
 
 nsEventStatus AsyncPanZoomController::OnDoubleTap(const TapGestureInput& aEvent) {
   APZC_LOG("%p got a double-tap in state %d\n", this, mState);
   nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
   if (controller) {
-    if (mZoomConstraints.mAllowDoubleTapZoom) {
+    if (mZoomConstraints.mAllowDoubleTapZoom && TouchActionAllowDoubleTapZoom()) {
       int32_t modifiers = WidgetModifiersToDOMModifiers(aEvent.modifiers);
       CSSPoint geckoScreenPoint;
       if (ConvertToGecko(aEvent.mPoint, &geckoScreenPoint)) {
         controller->HandleDoubleTap(geckoScreenPoint, modifiers, GetGuid());
       }
     }
-
     return nsEventStatus_eConsumeNoDefault;
   }
   return nsEventStatus_eIgnore;
 }
 
 nsEventStatus AsyncPanZoomController::OnCancelTap(const TapGestureInput& aEvent) {
   APZC_LOG("%p got a cancel-tap in state %d\n", this, mState);
   // XXX: Implement this.
@@ -1956,29 +1956,39 @@ void AsyncPanZoomController::CheckConten
 
       mTouchQueue.RemoveElementAt(0);
     }
 
     mHandlingTouchQueue = false;
   }
 }
 
-bool AsyncPanZoomController::TouchActionAllowZoom() {
+bool AsyncPanZoomController::TouchActionAllowPinchZoom() {
   if (!mTouchActionPropertyEnabled) {
     return true;
   }
-
   // Pointer events specification implies all touch points to allow zoom
   // to perform it.
   for (size_t i = 0; i < mTouchBlockState.mAllowedTouchBehaviors.Length(); i++) {
-    if (!(mTouchBlockState.mAllowedTouchBehaviors[i] & AllowedTouchBehavior::ZOOM)) {
+    if (!(mTouchBlockState.mAllowedTouchBehaviors[i] & AllowedTouchBehavior::PINCH_ZOOM)) {
       return false;
     }
   }
+  return true;
+}
 
+bool AsyncPanZoomController::TouchActionAllowDoubleTapZoom() {
+  if (!mTouchActionPropertyEnabled) {
+    return true;
+  }
+  for (size_t i = 0; i < mTouchBlockState.mAllowedTouchBehaviors.Length(); i++) {
+    if (!(mTouchBlockState.mAllowedTouchBehaviors[i] & AllowedTouchBehavior::DOUBLE_TAP_ZOOM)) {
+      return false;
+    }
+  }
   return true;
 }
 
 AsyncPanZoomController::TouchBehaviorFlags
 AsyncPanZoomController::GetTouchBehavior(uint32_t touchIndex) {
   if (touchIndex < mTouchBlockState.mAllowedTouchBehaviors.Length()) {
     return mTouchBlockState.mAllowedTouchBehaviors[touchIndex];
   }
--- a/gfx/layers/apz/src/AsyncPanZoomController.h
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -590,19 +590,24 @@ private:
     // Specifies whether mPreventDefault property is set for this touch events block.
     bool mPreventDefaultSet;
 
     // Specifies whether a single tap event was generated during this touch block.
     bool mSingleTapOccurred;
   };
 
   /*
-   * Returns whether current touch behavior values allow zooming.
+   * Returns whether current touch behavior values allow pinch-zooming.
    */
-  bool TouchActionAllowZoom();
+  bool TouchActionAllowPinchZoom();
+
+  /*
+   * Returns whether current touch behavior values allow double-tap-zooming.
+   */
+  bool TouchActionAllowDoubleTapZoom();
 
   /*
    * Returns allowed touch behavior from the mAllowedTouchBehavior array.
    * In case apzc didn't receive touch behavior values within the timeout
    * it returns default value.
    */
   TouchBehaviorFlags GetTouchBehavior(uint32_t touchIndex);
 
--- a/gfx/layers/client/ClientTiledThebesLayer.cpp
+++ b/gfx/layers/client/ClientTiledThebesLayer.cpp
@@ -81,16 +81,42 @@ ClientTiledThebesLayer::BeginPaint()
     //       CSS transforms.
     //
     //       Because of this, there may be unintended behaviour when setting
     //       2d CSS translations on the children of scrollable displayport
     //       layers.
     return;
   }
 
+#ifdef MOZ_WIDGET_ANDROID
+  // Subframes on Fennec are not async scrollable because they have no displayport.
+  // However, the code in RenderLayer() picks up a displayport from the nearest
+  // scrollable ancestor container layer anyway, which is incorrect for Fennec. This
+  // behaviour results in the subframe getting clipped improperly and perma-blank areas
+  // while scrolling the subframe. To work around this, we detect if this layer is
+  // the primary scrollable layer and disable the tiling behaviour if it is not.
+  bool isPrimaryScrollableThebesLayer = false;
+  if (Layer* scrollable = ClientManager()->GetPrimaryScrollableLayer()) {
+    if (GetParent() == scrollable) {
+      for (Layer* child = scrollable->GetFirstChild(); child; child = child->GetNextSibling()) {
+        if (child->GetType() == Layer::TYPE_THEBES) {
+          if (child == this) {
+            // |this| is the first thebes layer child of the GetPrimaryScrollableLayer()
+            isPrimaryScrollableThebesLayer = true;
+          }
+          break;
+        }
+      }
+    }
+  }
+  if (!isPrimaryScrollableThebesLayer) {
+    return;
+  }
+#endif
+
   // Get the metrics of the nearest scrollable layer and the nearest layer
   // with a displayport.
   ContainerLayer* displayPortParent = nullptr;
   ContainerLayer* scrollParent = nullptr;
   for (ContainerLayer* parent = GetParent(); parent; parent = parent->GetParent()) {
     const FrameMetrics& metrics = parent->GetFrameMetrics();
     if (!scrollParent && metrics.GetScrollId() != FrameMetrics::NULL_SCROLL_ID) {
       scrollParent = parent;
--- a/gfx/layers/composite/ContentHost.h
+++ b/gfx/layers/composite/ContentHost.h
@@ -147,16 +147,19 @@ public:
   virtual void PrintInfo(nsACString& aTo, const char* aPrefix) MOZ_OVERRIDE;
 
   virtual void UseTextureHost(TextureHost* aTexture) MOZ_OVERRIDE;
   virtual void UseComponentAlphaTextures(TextureHost* aTextureOnBlack,
                                          TextureHost* aTextureOnWhite) MOZ_OVERRIDE;
 
   virtual bool Lock() {
     MOZ_ASSERT(!mLocked);
+    if (!mTextureHost) {
+      return false;
+    }
     if (!mTextureHost->Lock()) {
       return false;
     }
 
     if (mTextureHostOnWhite && !mTextureHostOnWhite->Lock()) {
       return false;
     }
 
--- a/gfx/tests/gtest/TestAsyncPanZoomController.cpp
+++ b/gfx/tests/gtest/TestAsyncPanZoomController.cpp
@@ -410,17 +410,17 @@ TEST_F(AsyncPanZoomControllerTester, Pin
 
   // Apzc's OnScaleEnd method calls once SendAsyncScrollDOMEvent and RequestContentRepaint methods,
   // therefore we're setting these specific values.
   EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtMost(1));
   EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(AtMost(1));
 
   nsTArray<uint32_t> values;
   values.AppendElement(mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN);
-  values.AppendElement(mozilla::layers::AllowedTouchBehavior::ZOOM);
+  values.AppendElement(mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM);
   apzc->SetTouchActionEnabled(true);
 
   apzc->SetAllowedTouchBehavior(values);
   ApzcPinch(apzc, 250, 300, 1.25);
 
   // The frame metrics should stay the same since touch-action:none makes
   // apzc ignore pinch gestures.
   fm = apzc->GetFrameMetrics();
@@ -901,27 +901,27 @@ DoLongPressPreventDefaultTest(bool aShou
 
 TEST_F(AsyncPanZoomControllerTester, LongPress) {
   DoLongPressTest(false, mozilla::layers::AllowedTouchBehavior::NONE);
 }
 
 TEST_F(AsyncPanZoomControllerTester, LongPressWithTouchAction) {
   DoLongPressTest(true, mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN
                       | mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN
-                      | mozilla::layers::AllowedTouchBehavior::ZOOM);
+                      | mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM);
 }
 
 TEST_F(AsyncPanZoomControllerTester, LongPressPreventDefault) {
   DoLongPressPreventDefaultTest(false, mozilla::layers::AllowedTouchBehavior::NONE);
 }
 
 TEST_F(AsyncPanZoomControllerTester, LongPressPreventDefaultWithTouchAction) {
   DoLongPressPreventDefaultTest(true, mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN
                                     | mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN
-                                    | mozilla::layers::AllowedTouchBehavior::ZOOM);
+                                    | mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM);
 }
 
 // Layer tree for HitTesting1
 static already_AddRefed<mozilla::layers::Layer>
 CreateTestLayerTree1(nsRefPtr<LayerManager>& aLayerManager, nsTArray<nsRefPtr<Layer> >& aLayers) {
   const char* layerTreeSyntax = "c(ttcc)";
   // LayerID                     0 1234
   nsIntRegion layerVisibleRegion[] = {
new file mode 100644
--- /dev/null
+++ b/gfx/thebes/MathTableStructures.h
@@ -0,0 +1,121 @@
+/* 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/. */
+
+// This file contains the structures described in Microsoft's document
+// "The MATH table and OpenType Features for Math Processing" (not yet public).
+//
+// Arrays of varying size are indicated in comments. Typically, gfxMathTable
+// will read the header of the structure first, verify that there is enough
+// space for the specified arrays and then use a pointer to browse these arrays.
+
+#ifndef MATH_TABLE_STRUCTURE_H
+#define MATH_TABLE_STRUCTURE_H
+
+#include "gfxFontUtils.h"
+
+typedef mozilla::AutoSwap_PRUint16 Count16;
+typedef mozilla::AutoSwap_PRUint16 GlyphID;
+typedef mozilla::AutoSwap_PRUint16 Offset;
+
+struct MathValueRecord {
+  mozilla::AutoSwap_PRInt16  mValue;
+  Offset                     mDeviceTable;
+};
+
+struct RangeRecord {
+  GlyphID                    mStart;
+  GlyphID                    mEnd;
+  mozilla::AutoSwap_PRUint16 mStartCoverageIndex;
+};
+
+struct Coverage {
+  mozilla::AutoSwap_PRUint16 mFormat;
+};
+
+struct CoverageFormat1 {
+  mozilla::AutoSwap_PRUint16 mFormat;
+  Count16                    mGlyphCount;
+  // GlyphID                 mGlyphArray[mGlyphCount]
+};
+
+struct CoverageFormat2 {
+  mozilla::AutoSwap_PRUint16 mFormat;
+  Count16                    mRangeCount;
+  // RangeRecord             mRangeArray[mRangeCount];
+};
+
+struct MATHTableHeader {
+  mozilla::AutoSwap_PRUint32 mVersion;
+  Offset                     mMathConstants;
+  Offset                     mMathGlyphInfo;
+  Offset                     mMathVariants;
+};
+
+struct MathConstants {
+  mozilla::AutoSwap_PRInt16  mInt16[gfxFontEntry::ScriptScriptPercentScaleDown -
+                                    gfxFontEntry::ScriptPercentScaleDown + 1];
+  mozilla::AutoSwap_PRUint16 mUint16[gfxFontEntry::DisplayOperatorMinHeight -
+                                     gfxFontEntry::
+                                     DelimitedSubFormulaMinHeight + 1];
+  MathValueRecord            mMathValues[gfxFontEntry::RadicalKernAfterDegree -
+                                         gfxFontEntry::MathLeading + 1];
+  mozilla::AutoSwap_PRUint16 mRadicalDegreeBottomRaisePercent;
+};
+
+struct MathGlyphInfo {
+  Offset mMathItalicsCorrectionInfo;
+  Offset mMathTopAccentAttachment;
+  Offset mExtendedShapeCoverage;
+  Offset mMathKernInfo;
+};
+
+struct MathItalicsCorrectionInfo {
+  Offset  mCoverage;
+  Count16 mItalicsCorrectionCount;
+  // MathValueRecord mItalicsCorrection[mItalicsCorrectionCount]
+};
+
+struct MathVariants {
+  mozilla::AutoSwap_PRUint16 mMinConnectorOverlap;
+  Offset                     mVertGlyphCoverage;
+  Offset                     mHorizGlyphCoverage;
+  Count16                    mVertGlyphCount;
+  Count16                    mHorizGlyphCount;
+  // Offset                  mVertGlyphConstruction[mVertGlyphCount];
+  // Offset                  mHorizGlyphConstruction[mHorizGlyphCount];
+};
+
+struct MathGlyphVariantRecord {
+  GlyphID                    mVariantGlyph;
+  mozilla::AutoSwap_PRUint16 mAdvanceMeasurement;
+};
+
+struct MathGlyphConstruction {
+  Offset                    mGlyphAssembly;
+  Count16                   mVariantCount;
+  // MathGlyphVariantRecord mMathGlyphVariantRecord[mVariantCount]
+};
+
+struct GlyphPartRecord {
+  GlyphID	              mGlyph;
+  mozilla::AutoSwap_PRUint16 mStartConnectorLength;
+  mozilla::AutoSwap_PRUint16 mEndConnectorLength;
+  mozilla::AutoSwap_PRUint16 mFullAdvance;
+  mozilla::AutoSwap_PRUint16 mPartFlags;
+};
+
+// PartFlags enumeration currently uses only one bit:
+// 0x0001 If set, the part can be skipped or repeated.
+// 0xFFFE Reserved.
+enum {
+  PART_FLAG_EXTENDER = 0x01
+};
+
+struct GlyphAssembly {
+  MathValueRecord    mItalicsCorrection;
+  Count16            mPartCount;
+  // GlyphPartRecord mPartRecords[mPartCount]
+};
+
+#endif
--- a/gfx/thebes/gfxFT2FontBase.cpp
+++ b/gfx/thebes/gfxFT2FontBase.cpp
@@ -112,17 +112,19 @@ gfxFT2FontBase::GetMetrics()
 {
     if (mHasMetrics)
         return mMetrics;
 
     if (MOZ_UNLIKELY(GetStyle()->size <= 0.0)) {
         new(&mMetrics) gfxFont::Metrics(); // zero initialize
         mSpaceGlyph = 0;
     } else {
-        gfxFT2LockedFace(this).GetMetrics(&mMetrics, &mSpaceGlyph);
+        gfxFT2LockedFace face(this);
+        mFUnitsConvFactor = face.XScale();
+        face.GetMetrics(&mMetrics, &mSpaceGlyph);
     }
 
     SanitizeMetrics(&mMetrics, false);
 
 #if 0
     //    printf("font name: %s %f\n", NS_ConvertUTF16toUTF8(GetName()).get(), GetStyle()->size);
     //    printf ("pango font %s\n", pango_font_description_to_string (pango_font_describe (font)));
 
--- a/gfx/thebes/gfxFT2Fonts.cpp
+++ b/gfx/thebes/gfxFT2Fonts.cpp
@@ -421,19 +421,16 @@ gfxFT2Font::ShapeText(gfxContext      *a
             ok = mGraphiteShaper->ShapeText(aContext, aText,
                                             aOffset, aLength,
                                             aScript, aShapedText);
         }
     }
 
     if (!ok && gfxPlatform::GetPlatform()->UseHarfBuzzForScript(aScript)) {
         if (!mHarfBuzzShaper) {
-            gfxFT2LockedFace face(this);
-            mFUnitsConvFactor = face.XScale();
-
             mHarfBuzzShaper = new gfxHarfBuzzShaper(this);
         }
         ok = mHarfBuzzShaper->ShapeText(aContext, aText,
                                         aOffset, aLength,
                                         aScript, aShapedText);
     }
 
     if (!ok) {
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -18,31 +18,33 @@
 
 #include "gfxFont.h"
 #include "gfxPlatform.h"
 #include "nsGkAtoms.h"
 
 #include "gfxTypes.h"
 #include "gfxContext.h"
 #include "gfxFontMissingGlyphs.h"
+#include "gfxHarfBuzzShaper.h"
 #include "gfxUserFontSet.h"
 #include "gfxPlatformFontList.h"
 #include "gfxScriptItemizer.h"
 #include "nsUnicodeProperties.h"
 #include "nsMathUtils.h"
 #include "nsBidiUtils.h"
 #include "nsUnicodeRange.h"
 #include "nsStyleConsts.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/Likely.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/Telemetry.h"
 #include "gfxSVGGlyphs.h"
+#include "gfxMathTable.h"
 #include "gfx2DGlue.h"
 
 #if defined(XP_MACOSX)
 #include "nsCocoaFeatures.h"
 #endif
 
 #include "cairo.h"
 #include "gfxFontTest.h"
@@ -107,16 +109,17 @@ gfxFontEntry::gfxFontEntry() :
     mIsBadUnderlineFont(false),
     mIsUserFont(false),
     mIsLocalUserFont(false),
     mStandardFace(false),
     mSymbolFont(false),
     mIgnoreGDEF(false),
     mIgnoreGSUB(false),
     mSVGInitialized(false),
+    mMathInitialized(false),
     mHasSpaceFeaturesInitialized(false),
     mHasSpaceFeatures(false),
     mHasSpaceFeaturesKerning(false),
     mHasSpaceFeaturesNonKerning(false),
     mSkipDefaultFeatureSpaceCheck(false),
     mCheckedForGraphiteTables(false),
     mHasCmapTable(false),
     mGrFaceInitialized(false),
@@ -136,16 +139,17 @@ gfxFontEntry::gfxFontEntry(const nsAStri
     mName(aName), mItalic(false), mFixedPitch(false),
     mIsProxy(false), mIsValid(true),
     mIsBadUnderlineFont(false), mIsUserFont(false),
     mIsLocalUserFont(false), mStandardFace(aIsStandardFace),
     mSymbolFont(false),
     mIgnoreGDEF(false),
     mIgnoreGSUB(false),
     mSVGInitialized(false),
+    mMathInitialized(false),
     mHasSpaceFeaturesInitialized(false),
     mHasSpaceFeatures(false),
     mHasSpaceFeaturesKerning(false),
     mHasSpaceFeaturesNonKerning(false),
     mSkipDefaultFeatureSpaceCheck(false),
     mCheckedForGraphiteTables(false),
     mHasCmapTable(false),
     mGrFaceInitialized(false),
@@ -384,16 +388,88 @@ void
 gfxFontEntry::NotifyGlyphsChanged()
 {
     for (uint32_t i = 0, count = mFontsUsingSVGGlyphs.Length(); i < count; ++i) {
         gfxFont* font = mFontsUsingSVGGlyphs[i];
         font->NotifyGlyphsChanged();
     }
 }
 
+bool
+gfxFontEntry::TryGetMathTable(gfxFont* aFont)
+{
+    if (!mMathInitialized) {
+        mMathInitialized = true;
+
+        // If UnitsPerEm is not known/valid, we can't use MATH table
+        if (UnitsPerEm() == kInvalidUPEM) {
+            return false;
+        }
+
+        // We don't use AutoTable here because we'll pass ownership of this
+        // blob to the gfxMathTable, once we've confirmed the table exists
+        hb_blob_t *mathTable = GetFontTable(TRUETYPE_TAG('M','A','T','H'));
+        if (!mathTable) {
+            return false;
+        }
+
+        // gfxMathTable will hb_blob_destroy() the table when it is finished
+        // with it.
+        mMathTable = new gfxMathTable(mathTable);
+        if (!mMathTable->HasValidHeaders()) {
+            mMathTable = nullptr;
+            return false;
+        }
+    }
+
+    return !!mMathTable;
+}
+
+gfxFloat
+gfxFontEntry::GetMathConstant(gfxFontEntry::MathConstant aConstant)
+{
+    NS_ASSERTION(mMathTable, "Math data has not yet been loaded. TryGetMathData() first.");
+    gfxFloat value = mMathTable->GetMathConstant(aConstant);
+    if (aConstant == gfxFontEntry::ScriptPercentScaleDown ||
+        aConstant == gfxFontEntry::ScriptScriptPercentScaleDown ||
+        aConstant == gfxFontEntry::RadicalDegreeBottomRaisePercent) {
+        return value / 100.0;
+    }
+    return value / mUnitsPerEm;
+}
+
+bool
+gfxFontEntry::GetMathItalicsCorrection(uint32_t aGlyphID,
+                                       gfxFloat* aItalicCorrection)
+{
+    NS_ASSERTION(mMathTable, "Math data has not yet been loaded. TryGetMathData() first.");
+    int16_t italicCorrection;
+    if (!mMathTable->GetMathItalicsCorrection(aGlyphID, &italicCorrection)) {
+        return false;
+    }
+    *aItalicCorrection = gfxFloat(italicCorrection) / mUnitsPerEm;
+    return true;
+}
+
+uint32_t
+gfxFontEntry::GetMathVariantsSize(uint32_t aGlyphID, bool aVertical,
+                                  uint16_t aSize)
+{
+    NS_ASSERTION(mMathTable, "Math data has not yet been loaded. TryGetMathData() first.");
+    return mMathTable->GetMathVariantsSize(aGlyphID, aVertical, aSize);
+}
+
+bool
+gfxFontEntry::GetMathVariantsParts(uint32_t aGlyphID, bool aVertical,
+                                   uint32_t aGlyphs[4])
+{
+    NS_ASSERTION(mMathTable, "Math data has not yet been loaded. TryGetMathData() first.");
+    return mMathTable->GetMathVariantsParts(aGlyphID, aVertical, aGlyphs);
+}
+
 /**
  * FontTableBlobData
  *
  * See FontTableHashEntry for the general strategy.
  */
 
 class gfxFontEntry::FontTableBlobData {
 public:
@@ -1947,16 +2023,38 @@ gfxFont::~gfxFont()
 
     mFontEntry->NotifyFontDestroyed(this);
 
     if (mGlyphChangeObservers) {
         mGlyphChangeObservers->EnumerateEntries(NotifyFontDestroyed, nullptr);
     }
 }
 
+gfxFloat
+gfxFont::GetGlyphHAdvance(gfxContext *aCtx, uint16_t aGID)
+{
+    if (ProvidesGlyphWidths()) {
+        return GetGlyphWidth(aCtx, aGID) / 65536.0;
+    }
+    if (mFUnitsConvFactor == 0.0f) {
+        GetMetrics();
+    }
+    NS_ASSERTION(mFUnitsConvFactor > 0.0f,
+                 "missing font unit conversion factor");
+    if (!mHarfBuzzShaper) {
+        mHarfBuzzShaper = new gfxHarfBuzzShaper(this);
+    }
+    gfxHarfBuzzShaper* shaper =
+        static_cast<gfxHarfBuzzShaper*>(mHarfBuzzShaper.get());
+    if (!shaper->Initialize() || !SetupCairoFont(aCtx)) {
+        return 0;
+    }
+    return shaper->GetGlyphHAdvance(aCtx, aGID) / 65536.0;
+}
+
 /*static*/
 PLDHashOperator
 gfxFont::AgeCacheEntry(CacheHashEntry *aEntry, void *aUserData)
 {
     if (!aEntry->mShapedWord) {
         NS_ASSERTION(aEntry->mShapedWord, "cache entry has no gfxShapedWord!");
         return PL_DHASH_REMOVE;
     }
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -38,21 +38,24 @@ typedef struct gr_face            gr_fac
 #include <stdio.h>
 #endif
 
 class gfxContext;
 class gfxTextRun;
 class gfxFont;
 class gfxFontFamily;
 class gfxFontGroup;
+class gfxGraphiteShaper;
+class gfxHarfBuzzShaper;
 class gfxUserFontSet;
 class gfxUserFontData;
 class gfxShapedText;
 class gfxShapedWord;
 class gfxSVGGlyphs;
+class gfxMathTable;
 class gfxTextContextPaint;
 class FontInfoData;
 
 class nsILanguageAtomService;
 
 #define FONT_MAX_SIZE                  2000.0
 
 #define NO_FONT_LANGUAGE_OVERRIDE      0
@@ -306,16 +309,89 @@ public:
     bool GetSVGGlyphExtents(gfxContext *aContext, uint32_t aGlyphId,
                             gfxRect *aResult);
     bool RenderSVGGlyph(gfxContext *aContext, uint32_t aGlyphId, int aDrawMode,
                         gfxTextContextPaint *aContextPaint);
     // Call this when glyph geometry or rendering has changed
     // (e.g. animated SVG glyphs)
     void NotifyGlyphsChanged();
 
+    enum MathConstant {
+        // The order of the constants must match the order of the fields
+        // defined in the MATH table.
+        ScriptPercentScaleDown,
+        ScriptScriptPercentScaleDown,
+        DelimitedSubFormulaMinHeight,
+        DisplayOperatorMinHeight,
+        MathLeading,
+        AxisHeight,
+        AccentBaseHeight,
+        FlattenedAccentBaseHeight,
+        SubscriptShiftDown,
+        SubscriptTopMax,
+        SubscriptBaselineDropMin,
+        SuperscriptShiftUp,
+        SuperscriptShiftUpCramped,
+        SuperscriptBottomMin,
+        SuperscriptBaselineDropMax,
+        SubSuperscriptGapMin,
+        SuperscriptBottomMaxWithSubscript,
+        SpaceAfterScript,
+        UpperLimitGapMin,
+        UpperLimitBaselineRiseMin,
+        LowerLimitGapMin,
+        LowerLimitBaselineDropMin,
+        StackTopShiftUp,
+        StackTopDisplayStyleShiftUp,
+        StackBottomShiftDown,
+        StackBottomDisplayStyleShiftDown,
+        StackGapMin,
+        StackDisplayStyleGapMin,
+        StretchStackTopShiftUp,
+        StretchStackBottomShiftDown,
+        StretchStackGapAboveMin,
+        StretchStackGapBelowMin,
+        FractionNumeratorShiftUp,
+        FractionNumeratorDisplayStyleShiftUp,
+        FractionDenominatorShiftDown,
+        FractionDenominatorDisplayStyleShiftDown,
+        FractionNumeratorGapMin,
+        FractionNumDisplayStyleGapMin,
+        FractionRuleThickness,
+        FractionDenominatorGapMin,
+        FractionDenomDisplayStyleGapMin,
+        SkewedFractionHorizontalGap,
+        SkewedFractionVerticalGap,
+        OverbarVerticalGap,
+        OverbarRuleThickness,
+        OverbarExtraAscender,
+        UnderbarVerticalGap,
+        UnderbarRuleThickness,
+        UnderbarExtraDescender,
+        RadicalVerticalGap,
+        RadicalDisplayStyleVerticalGap,
+        RadicalRuleThickness,
+        RadicalExtraAscender,
+        RadicalKernBeforeDegree,
+        RadicalKernAfterDegree,
+        RadicalDegreeBottomRaisePercent
+    };
+
+    // Call TryGetMathTable to try to load the Open Type MATH table. The other
+    // functions forward the call to the gfxMathTable class. The GetMath...()
+    // functions MUST NOT be called unless TryGetMathTable() has returned true.
+    bool     TryGetMathTable(gfxFont* aFont);
+    gfxFloat GetMathConstant(MathConstant aConstant);
+    bool     GetMathItalicsCorrection(uint32_t aGlyphID,
+                                      gfxFloat* aItalicCorrection);
+    uint32_t GetMathVariantsSize(uint32_t aGlyphID, bool aVertical,
+                                 uint16_t aSize);
+    bool     GetMathVariantsParts(uint32_t aGlyphID, bool aVertical,
+                                  uint32_t aGlyphs[4]);
+
     virtual bool MatchesGenericFamily(const nsACString& aGeneric) const {
         return true;
     }
     virtual bool SupportsLangGroup(nsIAtom *aLangGroup) const {
         return true;
     }
 
     // Access to raw font table data (needed for Harfbuzz):
@@ -428,16 +504,17 @@ public:
     bool             mIsBadUnderlineFont : 1;
     bool             mIsUserFont  : 1;
     bool             mIsLocalUserFont  : 1;
     bool             mStandardFace : 1;
     bool             mSymbolFont  : 1;
     bool             mIgnoreGDEF  : 1;
     bool             mIgnoreGSUB  : 1;
     bool             mSVGInitialized : 1;
+    bool             mMathInitialized : 1;
     bool             mHasSpaceFeaturesInitialized : 1;
     bool             mHasSpaceFeatures : 1;
     bool             mHasSpaceFeaturesKerning : 1;
     bool             mHasSpaceFeaturesNonKerning : 1;
     bool             mSkipDefaultFeatureSpaceCheck : 1;
     bool             mHasGraphiteTables : 1;
     bool             mCheckedForGraphiteTables : 1;
     bool             mHasCmapTable : 1;
@@ -453,16 +530,17 @@ public:
 
     nsRefPtr<gfxCharacterMap> mCharacterMap;
     uint32_t         mUVSOffset;
     nsAutoArrayPtr<uint8_t> mUVSData;
     nsAutoPtr<gfxUserFontData> mUserFontData;
     nsAutoPtr<gfxSVGGlyphs> mSVGGlyphs;
     // list of gfxFonts that are using SVG glyphs
     nsTArray<gfxFont*> mFontsUsingSVGGlyphs;
+    nsAutoPtr<gfxMathTable> mMathTable;
     nsTArray<gfxFontFeature> mFeatureSettings;
     uint32_t         mLanguageOverride;
 
 protected:
     friend class gfxPlatformFontList;
     friend class gfxMacPlatformFontList;
     friend class gfxUserFcFontEntry;
     friend class gfxFontFamily;
@@ -1376,16 +1454,20 @@ public:
 
 protected:
     // the font this shaper is working with
     gfxFont * mFont;
 };
 
 /* a SPECIFIC single font family */
 class gfxFont {
+
+    friend class gfxHarfBuzzShaper;
+    friend class gfxGraphiteShaper;
+
 public:
     nsrefcnt AddRef(void) {
         NS_PRECONDITION(int32_t(mRefCnt) >= 0, "illegal refcnt");
         if (mExpirationState.IsTracked()) {
             gfxFontCache::GetCache()->RemoveObject(this);
         }
         ++mRefCnt;
         NS_LOG_ADDREF(this, mRefCnt, "gfxFont", sizeof(*this));
@@ -1503,29 +1585,18 @@ public:
     virtual bool ProvidesGetGlyph() const {
         return false;
     }
     // Map unicode character to glyph ID.
     // Only used if ProvidesGetGlyph() returns true.
     virtual uint32_t GetGlyph(uint32_t unicode, uint32_t variation_selector) {
         return 0;
     }
-
-    // subclasses may provide (possibly hinted) glyph widths (in font units);
-    // if they do not override this, harfbuzz will use unhinted widths
-    // derived from the font tables
-    virtual bool ProvidesGlyphWidths() {
-        return false;
-    }
-
-    // The return value is interpreted as a horizontal advance in 16.16 fixed
-    // point format.
-    virtual int32_t GetGlyphWidth(gfxContext *aCtx, uint16_t aGID) {
-        return -1;
-    }
+    // Return the horizontal advance of a glyph.
+    gfxFloat GetGlyphHAdvance(gfxContext *aCtx, uint16_t aGID);
 
     // Return Azure GlyphRenderingOptions for drawing this font.
     virtual mozilla::TemporaryRef<mozilla::gfx::GlyphRenderingOptions>
       GetGlyphRenderingOptions() { return nullptr; }
 
     gfxFloat SynthesizeSpaceWidth(uint32_t aCh);
 
     // Font metrics
@@ -1811,16 +1882,29 @@ public:
     }
 
     static void DestroySingletons() {
         delete sScriptTagToCode;
         delete sDefaultFeatures;
     }
 
 protected:
+    // subclasses may provide (possibly hinted) glyph widths (in font units);
+    // if they do not override this, harfbuzz will use unhinted widths
+    // derived from the font tables
+    virtual bool ProvidesGlyphWidths() {
+        return false;
+    }
+
+    // The return value is interpreted as a horizontal advance in 16.16 fixed
+    // point format.
+    virtual int32_t GetGlyphWidth(gfxContext *aCtx, uint16_t aGID) {
+        return -1;
+    }
+
     void AddGlyphChangeObserver(GlyphChangeObserver *aObserver);
     void RemoveGlyphChangeObserver(GlyphChangeObserver *aObserver);
 
     // whether font contains substitution lookups containing spaces
     bool HasSubstitutionRulesWithSpaceLookups(int32_t aRunScript);
 
     // do spaces participate in shaping rules? if so, can't used word cache
     bool SpaceMayParticipateInShaping(int32_t aRunScript);
--- a/gfx/thebes/gfxHarfBuzzShaper.cpp
+++ b/gfx/thebes/gfxHarfBuzzShaper.cpp
@@ -183,20 +183,16 @@ struct HMetrics {
 // the variable-length metrics[] array is immediately followed by:
 //  AutoSwap_PRUint16    leftSideBearing[];
 };
 
 hb_position_t
 gfxHarfBuzzShaper::GetGlyphHAdvance(gfxContext *aContext,
                                     hb_codepoint_t glyph) const
 {
-    if (mUseFontGlyphWidths) {
-        return mFont->GetGlyphWidth(aContext, glyph);
-    }
-
     // font did not implement GetHintedGlyphWidth, so get an unhinted value
     // directly from the font tables
 
     NS_ASSERTION((mNumLongMetrics > 0) && mHmtxTable != nullptr,
                  "font is lacking metrics, we shouldn't be here");
 
     if (glyph >= uint32_t(mNumLongMetrics)) {
         glyph = mNumLongMetrics - 1;
@@ -206,23 +202,29 @@ gfxHarfBuzzShaper::GetGlyphHAdvance(gfxC
     // that mNumLongMetrics is > 0, and that the hmtx table is large enough
     // to contain mNumLongMetrics records
     const HMetrics* hmtx =
         reinterpret_cast<const HMetrics*>(hb_blob_get_data(mHmtxTable, nullptr));
     return FloatToFixed(mFont->FUnitsToDevUnitsFactor() *
                         uint16_t(hmtx->metrics[glyph].advanceWidth));
 }
 
-static hb_position_t
-HBGetGlyphHAdvance(hb_font_t *font, void *font_data,
-                   hb_codepoint_t glyph, void *user_data)
+/* static */
+hb_position_t
+gfxHarfBuzzShaper::HBGetGlyphHAdvance(hb_font_t *font, void *font_data,
+                                      hb_codepoint_t glyph, void *user_data)
 {
     const gfxHarfBuzzShaper::FontCallbackData *fcd =
         static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
-    return fcd->mShaper->GetGlyphHAdvance(fcd->mContext, glyph);
+    gfxFont *gfxfont = fcd->mShaper->GetFont();
+    if (gfxfont->ProvidesGlyphWidths()) {
+        return gfxfont->GetGlyphWidth(fcd->mContext, glyph);
+    } else {
+        return fcd->mShaper->GetGlyphHAdvance(fcd->mContext, glyph);
+    }
 }
 
 static hb_bool_t
 HBGetContourPoint(hb_font_t *font, void *font_data,
                   unsigned int point_index, hb_codepoint_t glyph,
                   hb_position_t *x, hb_position_t *y,
                   void *user_data)
 {
@@ -813,143 +815,155 @@ AddOpenTypeFeature(const uint32_t& aTag,
  */
 
 static hb_font_funcs_t * sHBFontFuncs = nullptr;
 static hb_unicode_funcs_t * sHBUnicodeFuncs = nullptr;
 static const hb_script_t sMathScript =
     hb_ot_tag_to_script(HB_TAG('m','a','t','h'));
 
 bool
+gfxHarfBuzzShaper::Initialize()
+{
+    if (mInitialized) {
+        return mHBFont != nullptr;
+    }
+    mInitialized = true;
+    mCallbackData.mShaper = this;
+
+    mUseFontGlyphWidths = mFont->ProvidesGlyphWidths();
+
+    if (!sHBFontFuncs) {
+        // static function callback pointers, initialized by the first
+        // harfbuzz shaper used
+        sHBFontFuncs = hb_font_funcs_create();
+        hb_font_funcs_set_glyph_func(sHBFontFuncs, HBGetGlyph,
+                                     nullptr, nullptr);
+        hb_font_funcs_set_glyph_h_advance_func(sHBFontFuncs,
+                                               HBGetGlyphHAdvance,
+                                               nullptr, nullptr);
+        hb_font_funcs_set_glyph_contour_point_func(sHBFontFuncs,
+                                                   HBGetContourPoint,
+                                                   nullptr, nullptr);
+        hb_font_funcs_set_glyph_h_kerning_func(sHBFontFuncs,
+                                               HBGetHKerning,
+                                               nullptr, nullptr);
+
+        sHBUnicodeFuncs =
+            hb_unicode_funcs_create(hb_unicode_funcs_get_empty());
+        hb_unicode_funcs_set_mirroring_func(sHBUnicodeFuncs,
+                                            HBGetMirroring,
+                                            nullptr, nullptr);
+        hb_unicode_funcs_set_script_func(sHBUnicodeFuncs, HBGetScript,
+                                         nullptr, nullptr);
+        hb_unicode_funcs_set_general_category_func(sHBUnicodeFuncs,
+                                                   HBGetGeneralCategory,
+                                                   nullptr, nullptr);
+        hb_unicode_funcs_set_combining_class_func(sHBUnicodeFuncs,
+                                                  HBGetCombiningClass,
+                                                  nullptr, nullptr);
+        hb_unicode_funcs_set_eastasian_width_func(sHBUnicodeFuncs,
+                                                  HBGetEastAsianWidth,
+                                                  nullptr, nullptr);
+        hb_unicode_funcs_set_compose_func(sHBUnicodeFuncs,
+                                          HBUnicodeCompose,
+                                          nullptr, nullptr);
+        hb_unicode_funcs_set_decompose_func(sHBUnicodeFuncs,
+                                            HBUnicodeDecompose,
+                                            nullptr, nullptr);
+    }
+
+    gfxFontEntry *entry = mFont->GetFontEntry();
+    if (!mUseFontGetGlyph) {
+        // get the cmap table and find offset to our subtable
+        mCmapTable = entry->GetFontTable(TRUETYPE_TAG('c','m','a','p'));
+        if (!mCmapTable) {
+            NS_WARNING("failed to load cmap, glyphs will be missing");
+            return false;
+        }
+        uint32_t len;
+        const uint8_t* data = (const uint8_t*)hb_blob_get_data(mCmapTable, &len);
+        bool symbol;
+        mCmapFormat = gfxFontUtils::
+            FindPreferredSubtable(data, len,
+                                  &mSubtableOffset, &mUVSTableOffset,
+                                  &symbol);
+        if (mCmapFormat <= 0) {
+            return false;
+        }
+    }
+
+    if (!mUseFontGlyphWidths) {
+        // if font doesn't implement GetGlyphWidth, we will be reading
+        // the hmtx table directly;
+        // read mNumLongMetrics from hhea table without caching its blob,
+        // and preload/cache the hmtx table
+        gfxFontEntry::AutoTable hheaTable(entry, TRUETYPE_TAG('h','h','e','a'));
+        if (hheaTable) {
+            uint32_t len;
+            const HMetricsHeader* hhea =
+                reinterpret_cast<const HMetricsHeader*>
+                (hb_blob_get_data(hheaTable, &len));
+            if (len >= sizeof(HMetricsHeader)) {
+                mNumLongMetrics = hhea->numberOfHMetrics;
+                if (mNumLongMetrics > 0 &&
+                    int16_t(hhea->metricDataFormat) == 0) {
+                    // no point reading hmtx if number of entries is zero!
+                    // in that case, we won't be able to use this font
+                    // (this method will return FALSE below if mHmtx is null)
+                    mHmtxTable =
+                        entry->GetFontTable(TRUETYPE_TAG('h','m','t','x'));
+                    if (hb_blob_get_length(mHmtxTable) <
+                        mNumLongMetrics * sizeof(HLongMetric)) {
+                        // hmtx table is not large enough for the claimed
+                        // number of entries: invalid, do not use.
+                        hb_blob_destroy(mHmtxTable);
+                        mHmtxTable = nullptr;
+                    }
+                }
+            }
+        }
+        if (!mHmtxTable) {
+            return false;
+        }
+    }
+
+    mHBFont = hb_font_create(mHBFace);
+    hb_font_set_funcs(mHBFont, sHBFontFuncs, &mCallbackData, nullptr);
+    hb_font_set_ppem(mHBFont, mFont->GetAdjustedSize(), mFont->GetAdjustedSize());
+    uint32_t scale = FloatToFixed(mFont->GetAdjustedSize()); // 16.16 fixed-point
+    hb_font_set_scale(mHBFont, scale, scale);
+
+    return true;
+}
+
+bool
 gfxHarfBuzzShaper::ShapeText(gfxContext      *aContext,
                              const char16_t *aText,
                              uint32_t         aOffset,
                              uint32_t         aLength,
                              int32_t          aScript,
                              gfxShapedText   *aShapedText)
 {
     // some font back-ends require this in order to get proper hinted metrics
     if (!mFont->SetupCairoFont(aContext)) {
         return false;
     }
 
     mCallbackData.mContext = aContext;
-    gfxFontEntry *entry = mFont->GetFontEntry();
 
-    if (!mInitialized) {
-        mInitialized = true;
-        mCallbackData.mShaper = this;
-
-        mUseFontGlyphWidths = mFont->ProvidesGlyphWidths();
-
-        if (!sHBFontFuncs) {
-            // static function callback pointers, initialized by the first
-            // harfbuzz shaper used
-            sHBFontFuncs = hb_font_funcs_create();
-            hb_font_funcs_set_glyph_func(sHBFontFuncs, HBGetGlyph,
-                                         nullptr, nullptr);
-            hb_font_funcs_set_glyph_h_advance_func(sHBFontFuncs,
-                                                   HBGetGlyphHAdvance,
-                                                   nullptr, nullptr);
-            hb_font_funcs_set_glyph_contour_point_func(sHBFontFuncs,
-                                                       HBGetContourPoint,
-                                                       nullptr, nullptr);
-            hb_font_funcs_set_glyph_h_kerning_func(sHBFontFuncs,
-                                                   HBGetHKerning,
-                                                   nullptr, nullptr);
-
-            sHBUnicodeFuncs =
-                hb_unicode_funcs_create(hb_unicode_funcs_get_empty());
-            hb_unicode_funcs_set_mirroring_func(sHBUnicodeFuncs,
-                                                HBGetMirroring,
-                                                nullptr, nullptr);
-            hb_unicode_funcs_set_script_func(sHBUnicodeFuncs, HBGetScript,
-                                             nullptr, nullptr);
-            hb_unicode_funcs_set_general_category_func(sHBUnicodeFuncs,
-                                                       HBGetGeneralCategory,
-                                                       nullptr, nullptr);
-            hb_unicode_funcs_set_combining_class_func(sHBUnicodeFuncs,
-                                                      HBGetCombiningClass,
-                                                      nullptr, nullptr);
-            hb_unicode_funcs_set_eastasian_width_func(sHBUnicodeFuncs,
-                                                      HBGetEastAsianWidth,
-                                                      nullptr, nullptr);
-            hb_unicode_funcs_set_compose_func(sHBUnicodeFuncs,
-                                              HBUnicodeCompose,
-                                              nullptr, nullptr);
-            hb_unicode_funcs_set_decompose_func(sHBUnicodeFuncs,
-                                                HBUnicodeDecompose,
-                                                nullptr, nullptr);
-        }
-
-        if (!mUseFontGetGlyph) {
-            // get the cmap table and find offset to our subtable
-            mCmapTable = entry->GetFontTable(TRUETYPE_TAG('c','m','a','p'));
-            if (!mCmapTable) {
-                NS_WARNING("failed to load cmap, glyphs will be missing");
-                return false;
-            }
-            uint32_t len;
-            const uint8_t* data = (const uint8_t*)hb_blob_get_data(mCmapTable, &len);
-            bool symbol;
-            mCmapFormat = gfxFontUtils::
-                FindPreferredSubtable(data, len,
-                                      &mSubtableOffset, &mUVSTableOffset,
-                                      &symbol);
-        }
-
-        if (!mUseFontGlyphWidths) {
-            // if font doesn't implement GetGlyphWidth, we will be reading
-            // the hmtx table directly;
-            // read mNumLongMetrics from hhea table without caching its blob,
-            // and preload/cache the hmtx table
-            gfxFontEntry::AutoTable hheaTable(entry, TRUETYPE_TAG('h','h','e','a'));
-            if (hheaTable) {
-                uint32_t len;
-                const HMetricsHeader* hhea =
-                    reinterpret_cast<const HMetricsHeader*>
-                        (hb_blob_get_data(hheaTable, &len));
-                if (len >= sizeof(HMetricsHeader)) {
-                    mNumLongMetrics = hhea->numberOfHMetrics;
-                    if (mNumLongMetrics > 0 &&
-                        int16_t(hhea->metricDataFormat) == 0) {
-                        // no point reading hmtx if number of entries is zero!
-                        // in that case, we won't be able to use this font
-                        // (this method will return FALSE below if mHmtx is null)
-                        mHmtxTable =
-                            entry->GetFontTable(TRUETYPE_TAG('h','m','t','x'));
-                        if (hb_blob_get_length(mHmtxTable) <
-                            mNumLongMetrics * sizeof(HLongMetric)) {
-                            // hmtx table is not large enough for the claimed
-                            // number of entries: invalid, do not use.
-                            hb_blob_destroy(mHmtxTable);
-                            mHmtxTable = nullptr;
-                        }
-                    }
-                }
-            }
-        }
-
-        mHBFont = hb_font_create(mHBFace);
-        hb_font_set_funcs(mHBFont, sHBFontFuncs, &mCallbackData, nullptr);
-        hb_font_set_ppem(mHBFont, mFont->GetAdjustedSize(), mFont->GetAdjustedSize());
-        uint32_t scale = FloatToFixed(mFont->GetAdjustedSize()); // 16.16 fixed-point
-        hb_font_set_scale(mHBFont, scale, scale);
-    }
-
-    if ((!mUseFontGetGlyph && mCmapFormat <= 0) ||
-        (!mUseFontGlyphWidths && !mHmtxTable)) {
-        // unable to shape with this font
+    if (!Initialize()) {
         return false;
     }
 
     const gfxFontStyle *style = mFont->GetStyle();
 
     nsAutoTArray<hb_feature_t,20> features;
     nsDataHashtable<nsUint32HashKey,uint32_t> mergedFeatures;
 
+    gfxFontEntry *entry = mFont->GetFontEntry();
     if (MergeFontFeatures(style,
                           entry->mFeatureSettings,
                           aShapedText->DisableLigatures(),
                           entry->FamilyName(),
                           mergedFeatures))
     {
         // enumerate result and insert into hb_feature array
         mergedFeatures.Enumerate(AddOpenTypeFeature, &features);
--- a/gfx/thebes/gfxHarfBuzzShaper.h
+++ b/gfx/thebes/gfxHarfBuzzShaper.h
@@ -19,16 +19,17 @@ public:
      * For HarfBuzz font callback functions, font_data is a ptr to a
      * FontCallbackData struct
      */
     struct FontCallbackData {
         gfxHarfBuzzShaper *mShaper;
         gfxContext        *mContext;
     };
 
+    bool Initialize();
     virtual bool ShapeText(gfxContext      *aContext,
                            const char16_t *aText,
                            uint32_t         aOffset,
                            uint32_t         aLength,
                            int32_t          aScript,
                            gfxShapedText   *aShapedText);
 
     // get a given font table in harfbuzz blob form
@@ -37,16 +38,21 @@ public:
     // map unicode character to glyph ID
     hb_codepoint_t GetGlyph(hb_codepoint_t unicode,
                             hb_codepoint_t variation_selector) const;
 
     // get harfbuzz glyph advance, in font design units
     hb_position_t GetGlyphHAdvance(gfxContext *aContext,
                                    hb_codepoint_t glyph) const;
 
+    // get harfbuzz horizontal advance in 16.16 fixed point format.
+    static hb_position_t
+    HBGetGlyphHAdvance(hb_font_t *font, void *font_data,
+                       hb_codepoint_t glyph, void *user_data);
+
     hb_position_t GetHKerning(uint16_t aFirstGlyph,
                               uint16_t aSecondGlyph) const;
 
 protected:
     nsresult SetGlyphsFromRun(gfxContext      *aContext,
                               gfxShapedText   *aShapedText,
                               uint32_t         aOffset,
                               uint32_t         aLength,
new file mode 100644
--- /dev/null
+++ b/gfx/thebes/gfxMathTable.cpp
@@ -0,0 +1,459 @@
+/* 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 "gfxMathTable.h"
+
+#include "MathTableStructures.h"
+#include "harfbuzz/hb.h"
+#include <algorithm>
+
+using namespace mozilla;
+
+gfxMathTable::gfxMathTable(hb_blob_t* aMathTable)
+  : mMathTable(aMathTable)
+  , mGlyphConstruction(nullptr)
+  , mGlyphID(-1)
+  , mVertical(false)
+{
+}
+
+gfxMathTable::~gfxMathTable()
+{
+  hb_blob_destroy(mMathTable);
+}
+
+bool
+gfxMathTable::HasValidHeaders()
+{
+  const char* mathData = hb_blob_get_data(mMathTable, nullptr);
+  // Verify the MATH table header.
+  if (!ValidStructure(mathData, sizeof(MATHTableHeader))) {
+    return false;
+  }
+  const MATHTableHeader* header = GetMATHTableHeader();
+  if (uint32_t(header->mVersion) != 0x00010000 ||
+      !ValidOffset(mathData, uint16_t(header->mMathConstants)) ||
+      !ValidOffset(mathData, uint16_t(header->mMathGlyphInfo)) ||
+      !ValidOffset(mathData, uint16_t(header->mMathVariants))) {
+    return false;
+  }
+
+  // Verify the MathConstants header.
+  const MathConstants* mathconstants = GetMathConstants();
+  const char* start = reinterpret_cast<const char*>(mathconstants);
+  if (!ValidStructure(start, sizeof(MathConstants))) {
+    return false;
+  }
+
+  // Verify the MathGlyphInfo header.
+  const MathGlyphInfo* mathglyphinfo = GetMathGlyphInfo();
+  start = reinterpret_cast<const char*>(mathglyphinfo);
+  if (!ValidStructure(start, sizeof(MathGlyphInfo))) {
+    return false;
+  }
+
+  // Verify the MathVariants header.
+  const MathVariants* mathvariants = GetMathVariants();
+  start = reinterpret_cast<const char*>(mathvariants);
+  if (!ValidStructure(start, sizeof(MathVariants)) ||
+      !ValidStructure(start,
+                      sizeof(MathVariants) + sizeof(Offset) *
+                      (uint16_t(mathvariants->mVertGlyphCount) +
+                       uint16_t(mathvariants->mHorizGlyphCount))) ||
+      !ValidOffset(start, uint16_t(mathvariants->mVertGlyphCoverage)) ||
+      !ValidOffset(start, uint16_t(mathvariants->mHorizGlyphCoverage))) {
+    return false;
+  }
+
+  return true;
+}
+
+int32_t
+gfxMathTable::GetMathConstant(gfxFontEntry::MathConstant aConstant)
+{
+  const MathConstants* mathconstants = GetMathConstants();
+
+  if (aConstant <= gfxFontEntry::ScriptScriptPercentScaleDown) {
+    return int16_t(mathconstants->mInt16[aConstant]);
+  }
+
+  if (aConstant <= gfxFontEntry::DisplayOperatorMinHeight) {
+    return
+      uint16_t(mathconstants->
+               mUint16[aConstant - gfxFontEntry::DelimitedSubFormulaMinHeight]);
+  }
+
+  if (aConstant <= gfxFontEntry::RadicalKernAfterDegree) {
+    return int16_t(mathconstants->
+                   mMathValues[aConstant - gfxFontEntry::MathLeading].mValue);
+  }
+
+  return uint16_t(mathconstants->mRadicalDegreeBottomRaisePercent);
+}
+
+bool
+gfxMathTable::GetMathItalicsCorrection(uint32_t aGlyphID,
+                                       int16_t* aItalicCorrection)
+{
+  const MathGlyphInfo* mathglyphinfo = GetMathGlyphInfo();
+
+  // Get the offset of the italic correction and verify whether it is valid.
+  const char* start = reinterpret_cast<const char*>(mathglyphinfo);
+  uint16_t offset = mathglyphinfo->mMathItalicsCorrectionInfo;
+  if (offset == 0 || !ValidOffset(start, offset)) {
+    return false;
+  }
+  start += offset;
+
+  // Verify the validity of the MathItalicsCorrectionInfo and retrieve it.
+  if (!ValidStructure(start, sizeof(MathItalicsCorrectionInfo))) {
+    return false;
+  }
+  const MathItalicsCorrectionInfo* italicsCorrectionInfo =
+    reinterpret_cast<const MathItalicsCorrectionInfo*>(start);
+
+  // Get the coverage index for the glyph.
+  offset = italicsCorrectionInfo->mCoverage;
+  const Coverage* coverage =
+    reinterpret_cast<const Coverage*>(start + offset);
+  int32_t i = GetCoverageIndex(coverage, aGlyphID);
+
+  // Get the ItalicsCorrection.
+  uint16_t count = italicsCorrectionInfo->mItalicsCorrectionCount;
+  if (i < 0 || i >= count) {
+    return false;
+  }
+  start = reinterpret_cast<const char*>(italicsCorrectionInfo + 1);
+  if (!ValidStructure(start, count * sizeof(MathValueRecord))) {
+    return false;
+  }
+  const MathValueRecord* mathValueRecordArray =
+    reinterpret_cast<const MathValueRecord*>(start);
+
+  *aItalicCorrection = int16_t(mathValueRecordArray[i].mValue);
+  return true;
+}
+
+uint32_t
+gfxMathTable::GetMathVariantsSize(uint32_t aGlyphID, bool aVertical,
+                                  uint16_t aSize)
+{
+  // Select the glyph construction.
+  SelectGlyphConstruction(aGlyphID, aVertical);
+  if (!mGlyphConstruction) {
+    return 0;
+  }
+
+  // Verify the validity of the array of the MathGlyphVariantRecord's and
+  // whether there is a variant of the requested size.
+  uint16_t count = mGlyphConstruction->mVariantCount;
+  const char* start = reinterpret_cast<const char*>(mGlyphConstruction + 1);
+  if (aSize >= count ||
+      !ValidStructure(start, count * sizeof(MathGlyphVariantRecord))) {
+    return 0;
+  }
+
+  // Return the glyph index of the requested size variant.
+  const MathGlyphVariantRecord* recordArray =
+    reinterpret_cast<const MathGlyphVariantRecord*>(start);
+  return uint32_t(recordArray[aSize].mVariantGlyph);
+}
+
+bool
+gfxMathTable::GetMathVariantsParts(uint32_t aGlyphID, bool aVertical,
+                                   uint32_t aGlyphs[4])
+{
+  // Get the glyph assembly corresponding to that (aGlyphID, aVertical) pair.
+  const GlyphAssembly* glyphAssembly = GetGlyphAssembly(aGlyphID, aVertical);
+  if (!glyphAssembly) {
+    return false;
+  }
+
+  // Verify the validity of the array of GlyphPartRecord's and retrieve it.
+  uint16_t count = glyphAssembly->mPartCount;
+  const char* start = reinterpret_cast<const char*>(glyphAssembly + 1);
+  if (!ValidStructure(start, count * sizeof(GlyphPartRecord))) {
+    return false;
+  }
+  const GlyphPartRecord* recordArray =
+    reinterpret_cast<const GlyphPartRecord*>(start);
+
+  // XXXfredw The structure of the Open Type Math table is a bit more general
+  // than the one currently used by the nsMathMLChar code, so we try to fallback
+  // in reasonable way. We use the approach of the copyComponents function in
+  // github.com/mathjax/MathJax-dev/blob/master/fonts/OpenTypeMath/fontUtil.py
+  //
+  // The nsMathMLChar code can use at most 3 non extender pieces (aGlyphs[0],
+  // aGlyphs[1] and aGlyphs[2]) and the extenders between these pieces should
+  // all be the same (aGlyphs[4]). Also, the parts of vertical assembly are
+  // stored from bottom to top in the Open Type MATH table while they are
+  // stored from top to bottom in nsMathMLChar.
+
+  // Count the number of non extender pieces
+  uint16_t nonExtenderCount = 0;
+  for (uint16_t i = 0; i < count; i++) {
+    if (!(uint16_t(recordArray[i].mPartFlags) & PART_FLAG_EXTENDER)) {
+      nonExtenderCount++;
+    }
+  }
+  if (nonExtenderCount > 3) {
+    // Not supported: too many pieces
+    return false;
+  }
+
+  // Now browse the list of pieces
+
+  // 0 = look for a left/bottom glyph
+  // 1 = look for an extender between left/bottom and mid
+  // 2 = look for a middle glyph
+  // 3 = look for an extender between middle and right/top
+  // 4 = look for a right/top glyph
+  // 5 = no more piece expected
+  uint8_t state = 0;
+
+  // First extender char found.
+  uint32_t extenderChar = 0;
+
+  // Clear the aGlyphs table.
+  memset(aGlyphs, 0, sizeof(uint32_t) * 4);
+
+  for (uint16_t i = 0; i < count; i++) {
+
+    bool isExtender = uint16_t(recordArray[i].mPartFlags) & PART_FLAG_EXTENDER;
+    uint32_t glyph = recordArray[i].mGlyph;
+
+    if ((state == 1 || state == 2) && nonExtenderCount < 3) {
+      // do not try to find a middle glyph
+      state += 2;
+    }
+
+    if (isExtender) {
+      if (!extenderChar) {
+        extenderChar = glyph;
+        aGlyphs[3] = extenderChar;
+      } else if (extenderChar != glyph)  {
+        // Not supported: different extenders
+        return false;
+      }
+
+      if (state == 0) { // or state == 1
+        // ignore left/bottom piece and multiple successive extenders
+        state = 1;
+      } else if (state == 2) { // or state == 3
+        // ignore middle piece and multiple successive extenders
+        state = 3;
+      } else if (state >= 4) {
+        // Not supported: unexpected extender
+        return false;
+      }
+
+      continue;
+    }
+
+    if (state == 0) {
+      // copy left/bottom part
+      aGlyphs[mVertical ? 2 : 0] = glyph;
+      state = 1;
+      continue;
+    }
+
+    if (state == 1 || state == 2) {
+      // copy middle part
+      aGlyphs[1] = glyph;
+      state = 3;
+      continue;
+    }
+
+    if (state == 3 || state == 4) {
+      // copy right/top part
+      aGlyphs[mVertical ? 0 : 2] = glyph;
+      state = 5;
+    }
+
+  }
+
+  return true;
+}
+
+bool
+gfxMathTable::ValidStructure(const char* aStart, uint16_t aSize)
+{
+  unsigned int mathDataLength;
+  const char* mathData = hb_blob_get_data(mMathTable, &mathDataLength);
+  return (mathData <= aStart &&
+          aStart + aSize <= mathData + mathDataLength);
+}
+
+bool
+gfxMathTable::ValidOffset(const char* aStart, uint16_t aOffset)
+{
+  unsigned int mathDataLength;
+  const char* mathData = hb_blob_get_data(mMathTable, &mathDataLength);
+  return (mathData <= aStart + aOffset &&
+          aStart + aOffset < mathData + mathDataLength);
+}
+
+const MATHTableHeader*
+gfxMathTable::GetMATHTableHeader()
+{
+  const char* mathData = hb_blob_get_data(mMathTable, nullptr);
+  return reinterpret_cast<const MATHTableHeader*>(mathData);
+}
+
+const MathConstants*
+gfxMathTable::GetMathConstants()
+{
+  const char* mathData = hb_blob_get_data(mMathTable, nullptr);
+  return
+    reinterpret_cast<const MathConstants*>(mathData +
+                                           uint16_t(GetMATHTableHeader()->
+                                                    mMathConstants));
+}
+
+const MathGlyphInfo*
+gfxMathTable::GetMathGlyphInfo()
+{
+  const char* mathData = hb_blob_get_data(mMathTable, nullptr);
+  return
+    reinterpret_cast<const MathGlyphInfo*>(mathData +
+                                           uint16_t(GetMATHTableHeader()->
+                                                    mMathGlyphInfo));
+}
+
+const MathVariants*
+gfxMathTable::GetMathVariants()
+{
+  const char* mathData = hb_blob_get_data(mMathTable, nullptr);
+  return
+    reinterpret_cast<const MathVariants*>(mathData +
+                                          uint16_t(GetMATHTableHeader()->
+                                                   mMathVariants));
+}
+
+const GlyphAssembly*
+gfxMathTable::GetGlyphAssembly(uint32_t aGlyphID, bool aVertical)
+{
+  // Select the glyph construction.
+  SelectGlyphConstruction(aGlyphID, aVertical);
+  if (!mGlyphConstruction) {
+    return nullptr;
+  }
+
+  // Get the offset of the glyph assembly and verify whether it is valid.
+  const char* start = reinterpret_cast<const char*>(mGlyphConstruction);
+  uint16_t offset = mGlyphConstruction->mGlyphAssembly;
+  if (offset == 0 || !ValidOffset(start, offset)) {
+    return nullptr;
+  }
+  start += offset;
+
+  // Verify the validity of the GlyphAssembly and return it.
+  if (!ValidStructure(start, sizeof(GlyphAssembly))) {
+    return nullptr;
+  }
+  return reinterpret_cast<const GlyphAssembly*>(start);
+}
+
+int32_t
+gfxMathTable::GetCoverageIndex(const Coverage* aCoverage, uint32_t aGlyph)
+{
+  if (uint16_t(aCoverage->mFormat) == 1) {
+    // Coverage Format 1: list of individual glyph indices in the glyph set.
+    const CoverageFormat1* table =
+      reinterpret_cast<const CoverageFormat1*>(aCoverage);
+    uint16_t count = table->mGlyphCount;
+    const char* start = reinterpret_cast<const char*>(table + 1);
+    if (ValidStructure(start, count * sizeof(GlyphID))) {
+      const GlyphID* glyphArray =
+        reinterpret_cast<const GlyphID*>(start);
+      uint32_t imin = 0, imax = count;
+      while (imin < imax) {
+        uint32_t imid = (imin + imax) >> 1;
+        uint16_t glyphMid = glyphArray[imid];
+        if (glyphMid == aGlyph) {
+          return imid;
+        }
+        if (glyphMid < aGlyph) {
+          imin = imid + 1;
+        } else {
+          imax = imid;
+        }
+      }
+    }
+  } else if (uint16_t(aCoverage->mFormat) == 2) {
+    // Coverage Format 2: ranges of consecutive indices.
+    const CoverageFormat2* table =
+      reinterpret_cast<const CoverageFormat2*>(aCoverage);
+    uint16_t count = table->mRangeCount;
+    const char* start = reinterpret_cast<const char*>(table + 1);
+    if (ValidStructure(start, count * sizeof(RangeRecord))) {
+      const RangeRecord* rangeArray =
+        reinterpret_cast<const RangeRecord*>(start);
+      uint32_t imin = 0, imax = count;
+      while (imin < imax) {
+        uint32_t imid = (imin + imax) >> 1;
+        uint16_t rStart = rangeArray[imid].mStart;
+        uint16_t rEnd = rangeArray[imid].mEnd;
+        if (rEnd < aGlyph) {
+          imin = imid + 1;
+        } else if (aGlyph < rStart) {
+          imax = imid;
+        } else {
+          return (uint16_t(rangeArray[imid].mStartCoverageIndex) +
+                  aGlyph - rStart);
+        }
+      }
+    }
+  }
+  return -1;
+}
+
+void
+gfxMathTable::SelectGlyphConstruction(uint32_t aGlyphID, bool aVertical)
+{
+  if (mGlyphID == aGlyphID && mVertical == aVertical) {
+    // The (glyph, direction) pair is already selected: nothing to do.
+    return;
+  }
+
+  // Update our cached values.
+  mVertical = aVertical;
+  mGlyphID = aGlyphID;
+  mGlyphConstruction = nullptr;
+
+  // Get the coverage index for the new values.
+  const MathVariants* mathvariants = GetMathVariants();
+  const char* start = reinterpret_cast<const char*>(mathvariants);
+  uint16_t offset = (aVertical ?
+                     mathvariants->mVertGlyphCoverage :
+                     mathvariants->mHorizGlyphCoverage);
+  const Coverage* coverage =
+    reinterpret_cast<const Coverage*>(start + offset);
+  int32_t i = GetCoverageIndex(coverage, aGlyphID);
+
+  // Get the offset to the glyph construction.
+  uint16_t count = (aVertical ?
+                    mathvariants->mVertGlyphCount :
+                    mathvariants->mHorizGlyphCount);
+  start = reinterpret_cast<const char*>(mathvariants + 1);
+  if (i < 0 || i >= count) {
+    return;
+  }
+  if (!aVertical) {
+    start += uint16_t(mathvariants->mVertGlyphCount) * sizeof(Offset);
+  }
+  if (!ValidStructure(start, count * sizeof(Offset))) {
+    return;
+  }
+  const Offset* offsetArray = reinterpret_cast<const Offset*>(start);
+  offset = uint16_t(offsetArray[i]);
+
+  // Make mGlyphConstruction point to the desired glyph construction.
+  start = reinterpret_cast<const char*>(mathvariants);
+  if (!ValidStructure(start + offset, sizeof(MathGlyphConstruction))) {
+    return;
+  }
+  mGlyphConstruction =
+    reinterpret_cast<const MathGlyphConstruction*>(start + offset);
+}
new file mode 100644
--- /dev/null
+++ b/gfx/thebes/gfxMathTable.h
@@ -0,0 +1,122 @@
+/* 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 GFX_MATH_TABLE_H
+#define GFX_MATH_TABLE_H
+
+#include "gfxFont.h"
+
+struct Coverage;
+struct GlyphAssembly;
+struct MATHTableHeader;
+struct MathConstants;
+struct MathGlyphConstruction;
+struct MathGlyphInfo;
+struct MathVariants;
+
+/**
+ * Used by |gfxFontEntry| to represent the MATH table of an OpenType font.
+ * Each |gfxFontEntry| owns at most one |gfxMathTable| instance.
+ */
+class gfxMathTable
+{
+public:
+    /**
+     * @param aMathTable The MATH table from the OpenType font
+     *
+     * The gfxMathTable object takes over ownership of the blob references
+     * that are passed in, and will hb_blob_destroy() them when finished;
+     * the caller should -not- destroy this reference.
+     */
+    gfxMathTable(hb_blob_t* aMathTable);
+
+    /**
+     * Releases our reference to the MATH table and cleans up everything else.
+     */
+    ~gfxMathTable();
+
+    /**
+     * Returns the value of the specified constant from the MATH table.
+     */
+    int32_t GetMathConstant(gfxFontEntry::MathConstant aConstant);
+
+    /**
+     *  If the MATH table contains an italic correction for that glyph, this
+     *  function gets the value and returns true. Otherwise it returns false.
+     */
+    bool
+    GetMathItalicsCorrection(uint32_t aGlyphID, int16_t* aItalicCorrection);
+
+    /**
+     * @param aGlyphID  glyph index of the character we want to stretch
+     * @param aVertical direction of the stretching (vertical/horizontal)
+     * @param aSize     the desired size variant
+     *
+     * Returns the glyph index of the desired size variant or 0 if there is not
+     * any such size variant.
+     */
+    uint32_t GetMathVariantsSize(uint32_t aGlyphID, bool aVertical,
+                                 uint16_t aSize);
+
+    /**
+     * @param aGlyphID  glyph index of the character we want to stretch
+     * @param aVertical direction of the stretching (vertical/horizontal)
+     * @param aGlyphs   pre-allocated buffer of 4 elements where the glyph
+     * indexes (or 0 for absent parts) will be stored. The parts are stored in
+     * the order expected by the nsMathMLChar: Top (or Left), Middle, Bottom
+     * (or Right), Glue.
+     *
+     * Tries to fill-in aGlyphs with the relevant glyph indexes and returns
+     * whether the operation was successful. The function returns false if
+     * there is not any assembly for the character we want to stretch or if
+     * the format is not supported by the nsMathMLChar code.
+     *
+     */
+    bool GetMathVariantsParts(uint32_t aGlyphID, bool aVertical,
+                              uint32_t aGlyphs[4]);
+
+protected:
+    friend class gfxFontEntry;
+    // This allows gfxFontEntry to verify the validity of the main headers
+    // before starting to use the MATH table.
+    bool HasValidHeaders();
+
+private:
+    // HarfBuzz blob where the MATH table is stored.
+    hb_blob_t*    mMathTable;
+
+    // Cached values for the latest (mGlyphID, mVertical) pair that has been
+    // accessed and the corresponding glyph construction. These are verified
+    // by SelectGlyphConstruction and updated if necessary.
+    // mGlyphConstruction will be set to nullptr if no construction is defined
+    // for the glyph. If non-null, its mGlyphAssembly and mVariantCount fields
+    // may be safely read, but no further validation will have been done.
+    const MathGlyphConstruction* mGlyphConstruction;
+    uint32_t mGlyphID;
+    bool     mVertical;
+    void     SelectGlyphConstruction(uint32_t aGlyphID, bool aVertical);
+
+    // Access to some structures of the MATH table.
+    // These accessors just return a pointer, but do NOT themselves check the
+    // validity of anything. Until we've checked that HasValidHeaders (which
+    // does validate them) returns true, they might return pointers that cannot
+    // even safely be dereferenced. GetGlyphAssembly may return nullptr if the
+    // given glyph has no assembly defined.
+    const MATHTableHeader* GetMATHTableHeader();
+    const MathConstants*   GetMathConstants();
+    const MathGlyphInfo*   GetMathGlyphInfo();
+    const MathVariants*    GetMathVariants();
+    const GlyphAssembly*   GetGlyphAssembly(uint32_t aGlyphID, bool aVertical);
+
+    // Verify whether a structure or an offset belongs to the math data and can
+    // be read safely.
+    bool ValidStructure(const char* aStructStart, uint16_t aStructSize);
+    bool ValidOffset(const char* aOffsetStart, uint16_t aOffset);
+
+    // Get the coverage index of a glyph index from an Open Type coverage table
+    // or -1 if the glyph index is not found.
+    int32_t GetCoverageIndex(const Coverage* aCoverage, uint32_t aGlyph);
+};
+
+#endif
--- a/gfx/thebes/gfxPangoFonts.cpp
+++ b/gfx/thebes/gfxPangoFonts.cpp
@@ -1619,20 +1619,17 @@ gfxFcFont::ShapeText(gfxContext      *aC
             }
             ok = mGraphiteShaper->ShapeText(aContext, aText, aOffset, aLength,
                                             aScript, aShapedText);
         }
     }
 
     if (!ok) {
         if (!mHarfBuzzShaper) {
-            gfxFT2LockedFace face(this);
             mHarfBuzzShaper = new gfxHarfBuzzShaper(this);
-            // Used by gfxHarfBuzzShaper, currently only for kerning
-            mFUnitsConvFactor = face.XScale();
         }
         ok = mHarfBuzzShaper->ShapeText(aContext, aText, aOffset, aLength,
                                         aScript, aShapedText);
     }
 
     NS_WARN_IF_FALSE(ok, "shaper failed, expect scrambled or missing text");
 
     PostShapingFixup(aContext, aText, aOffset, aLength, aShapedText);
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -799,16 +799,17 @@ gfxWindowsPlatform::GetCommonFallbackFon
             aFontList.AppendElement(kFontMicrosoftUighur);
             break;
         case 0x07:
             aFontList.AppendElement(kFontEstrangeloEdessa);
             aFontList.AppendElement(kFontMVBoli);
             aFontList.AppendElement(kFontEbrima);
             break;
         case 0x09:
+            aFontList.AppendElement(kFontNirmalaUI);
             aFontList.AppendElement(kFontUtsaah);
             aFontList.AppendElement(kFontAparajita);
             break;
         case 0x0e:
             aFontList.AppendElement(kFontLaoUI);
             break;
         case 0x10:
             aFontList.AppendElement(kFontMyanmarText);
--- a/gfx/thebes/moz.build
+++ b/gfx/thebes/moz.build
@@ -21,16 +21,17 @@ EXPORTS += [
     'gfxFontConstants.h',
     'gfxFontFeatures.h',
     'gfxFontInfoLoader.h',
     'gfxFontTest.h',
     'gfxFontUtils.h',
     'gfxGradientCache.h',
     'gfxImageSurface.h',
     'gfxLineSegment.h',
+    'gfxMathTable.h',
     'gfxMatrix.h',
     'gfxPath.h',
     'gfxPattern.h',
     'gfxPlatform.h',
     'gfxPoint.h',
     'gfxPoint3D.h',
     'gfxPointH3D.h',
     'gfxPrefs.h',
@@ -230,16 +231,17 @@ UNIFIED_SOURCES += [
     'gfxFontFeatures.cpp',
     'gfxFontInfoLoader.cpp',
     'gfxFontMissingGlyphs.cpp',
     'gfxFontTest.cpp',
     'gfxGradientCache.cpp',
     'gfxGraphiteShaper.cpp',
     'gfxHarfBuzzShaper.cpp',
     'gfxImageSurface.cpp',
+    'gfxMathTable.cpp',
     'gfxMatrix.cpp',
     'gfxPath.cpp',
     'gfxPattern.cpp',
     'gfxRect.cpp',
     'gfxReusableImageSurfaceWrapper.cpp',
     'gfxReusableSharedImageSurfaceWrapper.cpp',
     'gfxScriptItemizer.cpp',
     'gfxSkipChars.cpp',
--- a/intl/locale/public/nsIPlatformCharset.h
+++ b/intl/locale/public/nsIPlatformCharset.h
@@ -29,16 +29,23 @@ typedef enum {
      kPlatformCharsetSel_Menu = 2,
      kPlatformCharsetSel_4xBookmarkFile = 3,
      kPlatformCharsetSel_KeyboardInput = 4,
      kPlatformCharsetSel_WindowManager = 5,
      kPlatformCharsetSel_4xPrefsJS = 6,
      kPlatformCharsetSel_PlainTextInFile = 7
 } nsPlatformCharsetSel;
 
+/**
+ * DO NOT ADD NEW USES OF THIS INTERFACE!
+ * Removal is https://bugzilla.mozilla.org/show_bug.cgi?id=943272
+ *
+ * Instead, use UTF-16 APIs on Windows and UTF-8 APIs everywhere else.
+ * Assume plain text files are UTF-8.
+ */
 class nsIPlatformCharset : public nsISupports
 {
 public:
  
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_IPLATFORMCHARSET_IID)
 
   NS_IMETHOD GetCharset(nsPlatformCharsetSel selector, nsACString& oResult) = 0;
 
--- a/js/src/gc/Nursery.h
+++ b/js/src/gc/Nursery.h
@@ -134,16 +134,26 @@ class Nursery
     size_t sizeOfHugeSlots(mozilla::MallocSizeOf mallocSizeOf) const {
         size_t total = 0;
         for (HugeSlotsSet::Range r = hugeSlots.all(); !r.empty(); r.popFront())
             total += mallocSizeOf(r.front());
         total += hugeSlots.sizeOfExcludingThis(mallocSizeOf);
         return total;
     }
 
+    MOZ_ALWAYS_INLINE uintptr_t start() const {
+        JS_ASSERT(runtime_);
+        return ((JS::shadow::Runtime *)runtime_)->gcNurseryStart_;
+    }
+
+    MOZ_ALWAYS_INLINE uintptr_t heapEnd() const {
+        JS_ASSERT(runtime_);
+        return ((JS::shadow::Runtime *)runtime_)->gcNurseryEnd_;
+    }
+
   private:
     /*
      * The start and end pointers are stored under the runtime so that we can
      * inline the isInsideNursery check into embedder code. Use the start()
      * and heapEnd() functions to access these values.
      */
     JSRuntime *runtime_;
 
@@ -185,26 +195,16 @@ class Nursery
     static_assert(sizeof(NurseryChunkLayout) == gc::ChunkSize,
                   "Nursery chunk size must match gc::Chunk size.");
     NurseryChunkLayout &chunk(int index) const {
         JS_ASSERT(index < NumNurseryChunks);
         JS_ASSERT(start());
         return reinterpret_cast<NurseryChunkLayout *>(start())[index];
     }
 
-    MOZ_ALWAYS_INLINE uintptr_t start() const {
-        JS_ASSERT(runtime_);
-        return ((JS::shadow::Runtime *)runtime_)->gcNurseryStart_;
-    }
-
-    MOZ_ALWAYS_INLINE uintptr_t heapEnd() const {
-        JS_ASSERT(runtime_);
-        return ((JS::shadow::Runtime *)runtime_)->gcNurseryEnd_;
-    }
-
     MOZ_ALWAYS_INLINE void setCurrentChunk(int chunkno) {
         JS_ASSERT(chunkno < NumNurseryChunks);
         JS_ASSERT(chunkno < numActiveChunks_);
         currentChunk_ = chunkno;
         position_ = chunk(chunkno).start();
         currentEnd_ = chunk(chunkno).end();
         chunk(chunkno).trailer.runtime = runtime();
     }
@@ -309,19 +309,16 @@ class Nursery
         }
     }
 #else
     void enterZealMode() {}
     void leaveZealMode() {}
 #endif
 
     friend class gc::MinorCollectionTracer;
-    friend class jit::CodeGenerator;
     friend class jit::MacroAssembler;
-    friend class jit::ICStubCompiler;
-    friend class jit::BaselineCompiler;
     friend void SetGCZeal(JSRuntime *, uint8_t, uint32_t);
 };
 
 } /* namespace js */
 
 #endif /* JSGC_GENERATIONAL */
 #endif /* gc_Nursery_h */
--- a/js/src/jit/Bailouts.cpp
+++ b/js/src/jit/Bailouts.cpp
@@ -10,34 +10,34 @@
 
 #include "jit/BaselineJIT.h"
 #include "jit/Ion.h"
 #include "jit/IonSpewer.h"
 #include "jit/JitCompartment.h"
 #include "jit/Snapshots.h"
 #include "vm/TraceLogging.h"
 
-#include "jit/IonFrameIterator-inl.h"
+#include "jit/JitFrameIterator-inl.h"
 #include "vm/Stack-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 // These constructor are exactly the same except for the type of the iterator
 // which is given to the SnapshotIterator constructor. Doing so avoid the
 // creation of virtual functions for the IonIterator but may introduce some
-// weirdness as IonInlineIterator is using an IonFrameIterator reference.
+// weirdness as IonInlineIterator is using a JitFrameIterator reference.
 //
 // If a function relies on ionScript() or to use OsiIndex(), due to the
-// lack of virtual, these functions will use the IonFrameIterator reference
+// lack of virtual, these functions will use the JitFrameIterator reference
 // contained in the InlineFrameIterator and thus are not able to recover
 // correctly the data stored in IonBailoutIterator.
 //
 // Currently, such cases should not happen because our only use case of the
-// IonFrameIterator within InlineFrameIterator is to read the frame content, or
+// JitFrameIterator within InlineFrameIterator is to read the frame content, or
 // to clone it to find the parent scripted frame.  Both use cases are fine and
 // should not cause any issue since the only potential issue is to read the
 // bailed out frame.
 
 SnapshotIterator::SnapshotIterator(const IonBailoutIterator &iter)
   : snapshot_(iter.ionScript()->snapshots(),
               iter.snapshotOffset(),
               iter.ionScript()->snapshotsRVATableSize(),
@@ -58,17 +58,17 @@ IonBailoutIterator::dump() const
         InlineFrameIterator frames(GetJSContextFromJitCode(), this);
         for (;;) {
             frames.dump();
             if (!frames.more())
                 break;
             ++frames;
         }
     } else {
-        IonFrameIterator::dump();
+        JitFrameIterator::dump();
     }
 }
 
 uint32_t
 jit::Bailout(BailoutStack *sp, BaselineBailoutInfo **bailoutInfo)
 {
     JSContext *cx = GetJSContextFromJitCode();
     JS_ASSERT(bailoutInfo);
@@ -146,18 +146,18 @@ jit::InvalidationBailout(InvalidationBai
     }
 
     iter.ionScript()->decref(cx->runtime()->defaultFreeOp());
 
     return retval;
 }
 
 IonBailoutIterator::IonBailoutIterator(const JitActivationIterator &activations,
-                                       const IonFrameIterator &frame)
-  : IonFrameIterator(activations),
+                                       const JitFrameIterator &frame)
+  : JitFrameIterator(activations),
     machine_(frame.machineState())
 {
     returnAddressToFp_ = frame.returnAddressToFp();
     topIonScript_ = frame.ionScript();
     const OsiIndex *osiIndex = frame.osiIndex();
 
     current_ = (uint8_t *) frame.fp();
     type_ = JitFrame_IonJS;
--- a/js/src/jit/Bailouts.h
+++ b/js/src/jit/Bailouts.h
@@ -4,18 +4,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jit_Bailouts_h
 #define jit_Bailouts_h
 
 #include "jstypes.h"
 
-#include "jit/IonFrameIterator.h"
 #include "jit/IonFrames.h"
+#include "jit/JitFrameIterator.h"
 #include "vm/Stack.h"
 
 namespace js {
 namespace jit {
 
 // A "bailout" is a condition in which we need to recover an interpreter frame
 // from an IonFrame. Bailouts can happen for the following reasons:
 //   (1) A deoptimization guard, for example, an add overflows or a type check
@@ -106,44 +106,44 @@ class JitCompartment;
 // bailout handler.
 class BailoutStack;
 class InvalidationBailoutStack;
 
 // Must be implemented by each architecture.
 
 // This iterator is constructed at a time where there is no exit frame at the
 // moment. They must be initialized to the first JS frame instead of the exit
-// frame as usually done with IonFrameIterator.
-class IonBailoutIterator : public IonFrameIterator
+// frame as usually done with JitFrameIterator.
+class IonBailoutIterator : public JitFrameIterator
 {
     MachineState machine_;
     uint32_t snapshotOffset_;
     size_t topFrameSize_;
     IonScript *topIonScript_;
 
   public:
     IonBailoutIterator(const JitActivationIterator &activations, BailoutStack *sp);
     IonBailoutIterator(const JitActivationIterator &activations, InvalidationBailoutStack *sp);
-    IonBailoutIterator(const JitActivationIterator &activations, const IonFrameIterator &frame);
+    IonBailoutIterator(const JitActivationIterator &activations, const JitFrameIterator &frame);
 
     SnapshotOffset snapshotOffset() const {
         JS_ASSERT(topIonScript_);
         return snapshotOffset_;
     }
     const MachineState &machineState() const {
         return machine_;
     }
     size_t topFrameSize() const {
         JS_ASSERT(topIonScript_);
         return topFrameSize_;
     }
     IonScript *ionScript() const {
         if (topIonScript_)
             return topIonScript_;
-        return IonFrameIterator::ionScript();
+        return JitFrameIterator::ionScript();
     }
 
     void dump() const;
 };
 
 bool EnsureHasScopeObjects(JSContext *cx, AbstractFramePtr fp);
 
 struct BaselineBailoutInfo;
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -1533,17 +1533,17 @@ jit::FinishBailoutToBaseline(BaselineBai
         return false;
 
     // Create arguments objects for bailed out frames, to maintain the invariant
     // that script->needsArgsObj() implies frame->hasArgsObj().
     RootedScript innerScript(cx, nullptr);
     RootedScript outerScript(cx, nullptr);
 
     JS_ASSERT(cx->currentlyRunningInJit());
-    IonFrameIterator iter(cx);
+    JitFrameIterator iter(cx);
 
     uint32_t frameno = 0;
     while (frameno < numFrames) {
         JS_ASSERT(!iter.isIonJS());
 
         if (iter.isBaselineJS()) {
             BaselineFrame *frame = iter.baselineFrame();
             MOZ_ASSERT(frame->script()->hasBaselineScript());
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -411,17 +411,16 @@ BaselineCompiler::emitEpilogue()
 
     masm.ret();
     return true;
 }
 
 #ifdef JSGC_GENERATIONAL
 // On input:
 //  R2.scratchReg() contains object being written to.
-//  R1.scratchReg() contains slot index being written to.
 //  Otherwise, baseline stack will be synced, so all other registers are usable as scratch.
 // This calls:
 //    void PostWriteBarrier(JSRuntime *rt, JSObject *obj);
 bool
 BaselineCompiler::emitOutOfLinePostBarrierSlot()
 {
     masm.bind(&postBarrierSlot_);
 
@@ -2096,25 +2095,23 @@ BaselineCompiler::emit_JSOP_SETALIASEDVA
     masm.patchableCallPreBarrier(address, MIRType_Value);
     masm.storeValue(R0, address);
     frame.push(R0);
 
 #ifdef JSGC_GENERATIONAL
     // Fully sync the stack if post-barrier is needed.
     // Scope coordinate object is already in R2.scratchReg().
     frame.syncStack(0);
+    Register temp = R1.scratchReg();
 
     Nursery &nursery = cx->runtime()->gcNursery;
     Label skipBarrier;
-    Label isTenured;
     masm.branchTestObject(Assembler::NotEqual, R0, &skipBarrier);
-    masm.branchPtr(Assembler::Below, objReg, ImmWord(nursery.start()), &isTenured);
-    masm.branchPtr(Assembler::Below, objReg, ImmWord(nursery.heapEnd()), &skipBarrier);
-
-    masm.bind(&isTenured);
+    masm.branchPtrInNurseryRange(objReg, temp, &skipBarrier);
+
     masm.call(&postBarrierSlot_);
 
     masm.bind(&skipBarrier);
 #endif
 
     return true;
 }
 
@@ -2414,28 +2411,26 @@ BaselineCompiler::emitFormalArgAccess(ui
         frame.push(R0);
     } else {
         masm.patchableCallPreBarrier(argAddr, MIRType_Value);
         storeValue(frame.peek(-1), argAddr, R0);
 
 #ifdef JSGC_GENERATIONAL
         // Fully sync the stack if post-barrier is needed.
         frame.syncStack(0);
+        Register temp = R1.scratchReg();
 
         // Reload the arguments object
         Register reg = R2.scratchReg();
         masm.loadPtr(Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfArgsObj()), reg);
 
         Nursery &nursery = cx->runtime()->gcNursery;
         Label skipBarrier;
-        Label isTenured;
-        masm.branchPtr(Assembler::Below, reg, ImmWord(nursery.start()), &isTenured);
-        masm.branchPtr(Assembler::Below, reg, ImmWord(nursery.heapEnd()), &skipBarrier);
-
-        masm.bind(&isTenured);
+        masm.branchPtrInNurseryRange(reg, temp, &skipBarrier);
+
         masm.call(&postBarrierSlot_);
 
         masm.bind(&skipBarrier);
 #endif
     }
 
     masm.bind(&done);
     return true;
--- a/js/src/jit/BaselineFrame.cpp
+++ b/js/src/jit/BaselineFrame.cpp
@@ -23,17 +23,17 @@ MarkLocals(BaselineFrame *frame, JSTrace
     if (start < end) {
         // Stack grows down.
         Value *last = frame->valueSlot(end - 1);
         gc::MarkValueRootRange(trc, end - start, last, "baseline-stack");
     }
 }
 
 void
-BaselineFrame::trace(JSTracer *trc, IonFrameIterator &frameIterator)
+BaselineFrame::trace(JSTracer *trc, JitFrameIterator &frameIterator)
 {
     replaceCalleeToken(MarkCalleeToken(trc, calleeToken()));
 
     gc::MarkValueRoot(trc, &thisValue(), "baseline-this");
 
     // Mark actual and formal args.
     if (isNonEvalFunctionFrame()) {
         unsigned numArgs = js::Max(numActualArgs(), numFormalArgs());
@@ -203,17 +203,17 @@ BaselineFrame::initForOsr(InterpreterFra
     if (cx->compartment()->debugMode()) {
         // In debug mode, update any Debugger.Frame objects for the
         // InterpreterFrame to point to the BaselineFrame.
 
         // The caller pushed a fake return address. ScriptFrameIter, used by the
         // debugger, wants a valid return address, but it's okay to just pick one.
         // In debug mode there's always at least 1 ICEntry (since there are always
         // debug prologue/epilogue calls).
-        IonFrameIterator iter(cx);
+        JitFrameIterator iter(cx);
         JS_ASSERT(iter.returnAddress() == nullptr);
         BaselineScript *baseline = fp->script()->baselineScript();
         iter.current()->setReturnAddress(baseline->returnAddressForIC(baseline->icEntry(0)));
 
         if (!Debugger::handleBaselineOsr(cx, fp, this))
             return false;
     }
 
--- a/js/src/jit/BaselineFrame.h
+++ b/js/src/jit/BaselineFrame.h
@@ -291,17 +291,17 @@ class BaselineFrame
     bool overRecursed() const {
         return flags_ & OVER_RECURSED;
     }
 
     void setOverRecursed() {
         flags_ |= OVER_RECURSED;
     }
 
-    void trace(JSTracer *trc, IonFrameIterator &frame);
+    void trace(JSTracer *trc, JitFrameIterator &frame);
 
     bool isFunctionFrame() const {
         return CalleeTokenIsFunction(calleeToken());
     }
     bool isGlobalFrame() const {
         return !CalleeTokenIsFunction(calleeToken());
     }
      bool isEvalFrame() const {
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -733,20 +733,17 @@ inline bool
 ICStubCompiler::emitPostWriteBarrierSlot(MacroAssembler &masm, Register obj, ValueOperand val,
                                          Register scratch, GeneralRegisterSet saveRegs)
 {
     Nursery &nursery = cx->runtime()->gcNursery;
 
     Label skipBarrier;
     masm.branchTestObject(Assembler::NotEqual, val, &skipBarrier);
 
-    Label isTenured;
-    masm.branchPtr(Assembler::Below, obj, ImmWord(nursery.start()), &isTenured);
-    masm.branchPtr(Assembler::Below, obj, ImmWord(nursery.heapEnd()), &skipBarrier);
-    masm.bind(&isTenured);
+    masm.branchPtrInNurseryRange(obj, scratch, &skipBarrier);
 
     Register valReg = masm.extractObject(val, scratch);
     masm.branchPtr(Assembler::Below, valReg, ImmWord(nursery.start()), &skipBarrier);
     masm.branchPtr(Assembler::AboveOrEqual, valReg, ImmWord(nursery.heapEnd()), &skipBarrier);
 
     // void PostWriteBarrier(JSRuntime *rt, JSObject *obj);
 #ifdef JS_CODEGEN_ARM
     saveRegs.add(BaselineTailCallReg);
@@ -768,17 +765,17 @@ ICStubCompiler::emitPostWriteBarrierSlot
 //
 // UseCount_Fallback
 //
 static bool
 IsTopFrameConstructing(JSContext *cx)
 {
     JS_ASSERT(cx->currentlyRunningInJit());
     JitActivationIterator activations(cx->runtime());
-    IonFrameIterator iter(activations);
+    JitFrameIterator iter(activations);
     JS_ASSERT(iter.type() == JitFrame_Exit);
 
     ++iter;
     JS_ASSERT(iter.type() == JitFrame_BaselineStub);
 
     ++iter;
     JS_ASSERT(iter.isBaselineJS());
 
--- a/js/src/jit/BaselineJIT.cpp
+++ b/js/src/jit/BaselineJIT.cpp
@@ -926,17 +926,17 @@ jit::ToggleBaselineSPS(JSRuntime *runtim
             script->baselineScript()->toggleSPS(enable);
         }
     }
 }
 
 static void
 MarkActiveBaselineScripts(JSRuntime *rt, const JitActivationIterator &activation)
 {
-    for (jit::IonFrameIterator iter(activation); !iter.done(); ++iter) {
+    for (jit::JitFrameIterator iter(activation); !iter.done(); ++iter) {
         switch (iter.type()) {
           case JitFrame_BaselineJS:
             iter.script()->baselineScript()->setActive();
             break;
           case JitFrame_IonJS: {
             // Keep the baseline script around, since bailouts from the ion
             // jitcode might need to re-enter into the baseline jitcode.
             iter.script()->baselineScript()->setActive();
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -1007,19 +1007,22 @@ CodeGenerator::visitLambdaArrow(LLambdaA
         return true;
     }
 
     masm.newGCThing(output, tempReg, info.fun, ool->entry(), gc::DefaultHeap);
     masm.initGCThing(output, tempReg, info.fun);
 
     emitLambdaInit(output, scopeChain, info);
 
-    // Store the lexical |this| value.
+    // Initialize extended slots. Lexical |this| is stored in the first one.
     MOZ_ASSERT(info.flags & JSFunction::EXTENDED);
-    masm.storeValue(thisv, Address(output, FunctionExtended::offsetOfArrowThisSlot()));
+    static_assert(FunctionExtended::NUM_EXTENDED_SLOTS == 2, "All slots must be initialized");
+    static_assert(FunctionExtended::ARROW_THIS_SLOT == 0, "|this| must be stored in first slot");
+    masm.storeValue(thisv, Address(output, FunctionExtended::offsetOfExtendedSlot(0)));
+    masm.storeValue(UndefinedValue(), Address(output, FunctionExtended::offsetOfExtendedSlot(1)));
 
     masm.bind(ool->rejoin());
     return true;
 }
 
 void
 CodeGenerator::emitLambdaInit(Register output, Register scopeChain,
                               const LambdaFunctionInfo &info)
@@ -1823,67 +1826,51 @@ CodeGenerator::visitOutOfLineCallPostWri
 bool
 CodeGenerator::visitPostWriteBarrierO(LPostWriteBarrierO *lir)
 {
 #ifdef JSGC_GENERATIONAL
     OutOfLineCallPostWriteBarrier *ool = new(alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object());
     if (!addOutOfLineCode(ool))
         return false;
 
-    const Nursery &nursery = GetIonContext()->runtime->gcNursery();
-    Register temp = ToRegister(lir->temp());
+    Register temp = ToTempRegisterOrInvalid(lir->temp());
 
     if (lir->object()->isConstant()) {
+        const Nursery &nursery = GetIonContext()->runtime->gcNursery();
         JS_ASSERT(!nursery.isInside(&lir->object()->toConstant()->toObject()));
     } else {
-        Register objreg = ToRegister(lir->object());
-        masm.movePtr(ImmWord(-ptrdiff_t(nursery.start())), temp);
-        masm.addPtr(objreg, temp);
-        masm.branchPtr(Assembler::Below, temp, Imm32(Nursery::NurserySize), ool->rejoin());
-    }
-
-    Register valuereg = ToRegister(lir->value());
-    masm.movePtr(ImmWord(-ptrdiff_t(nursery.start())), temp);
-    masm.addPtr(valuereg, temp);
-    masm.branchPtr(Assembler::Below, temp, Imm32(Nursery::NurserySize), ool->entry());
+        masm.branchPtrInNurseryRange(ToRegister(lir->object()), temp, ool->rejoin());
+    }
+
+    masm.branchPtrInNurseryRange(ToRegister(lir->value()), temp, ool->entry());
 
     masm.bind(ool->rejoin());
 #endif
     return true;
 }
 
 bool
 CodeGenerator::visitPostWriteBarrierV(LPostWriteBarrierV *lir)
 {
 #ifdef JSGC_GENERATIONAL
     OutOfLineCallPostWriteBarrier *ool = new(alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object());
     if (!addOutOfLineCode(ool))
         return false;
 
-    ValueOperand value = ToValue(lir, LPostWriteBarrierV::Input);
-    masm.branchTestObject(Assembler::NotEqual, value, ool->rejoin());
-
-    const Nursery &nursery = GetIonContext()->runtime->gcNursery();
+    Register temp = ToTempRegisterOrInvalid(lir->temp());
 
     if (lir->object()->isConstant()) {
+        const Nursery &nursery = GetIonContext()->runtime->gcNursery();
         JS_ASSERT(!nursery.isInside(&lir->object()->toConstant()->toObject()));
     } else {
-        Register temp = ToRegister(lir->temp());
-        Register objreg = ToRegister(lir->object());
-        masm.movePtr(ImmWord(-ptrdiff_t(nursery.start())), temp);
-        masm.addPtr(objreg, temp);
-        masm.branchPtr(Assembler::Below, temp, Imm32(Nursery::NurserySize), ool->rejoin());
-    }
-
-    // This section is a little different because we mustn't trash the temp
-    // register before we use its contents.
-    Register temp = ToRegister(lir->temp());
-    masm.unboxObject(value, temp);
-    masm.addPtr(ImmWord(-ptrdiff_t(nursery.start())), temp);
-    masm.branchPtr(Assembler::Below, temp, Imm32(Nursery::NurserySize), ool->entry());
+        masm.branchPtrInNurseryRange(ToRegister(lir->object()), temp, ool->rejoin());
+    }
+
+    ValueOperand value = ToValue(lir, LPostWriteBarrierV::Input);
+    masm.branchValueIsNurseryObject(value, temp, ool->entry());
 
     masm.bind(ool->rejoin());
 #endif
     return true;
 }
 
 bool
 CodeGenerator::visitCallNative(LCallNative *call)
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -2509,17 +2509,17 @@ jit::FastInvoke(JSContext *cx, HandleFun
 
 static void
 InvalidateActivation(FreeOp *fop, uint8_t *ionTop, bool invalidateAll)
 {
     IonSpew(IonSpew_Invalidate, "BEGIN invalidating activation");
 
     size_t frameno = 1;
 
-    for (IonFrameIterator it(ionTop, SequentialExecution); !it.done(); ++it, ++frameno) {
+    for (JitFrameIterator it(ionTop, SequentialExecution); !it.done(); ++it, ++frameno) {
         JS_ASSERT_IF(frameno == 1, it.type() == JitFrame_Exit);
 
 #ifdef DEBUG
         switch (it.type()) {
           case JitFrame_Exit:
             IonSpew(IonSpew_Invalidate, "#%d exit frame @ %p", frameno, it.fp());
             break;
           case JitFrame_BaselineJS:
@@ -2875,17 +2875,17 @@ jit::ForbidCompilation(JSContext *cx, JS
             mode, script->filename(), script->lineno());
 
     CancelOffThreadIonCompile(cx->compartment(), script);
 
     switch (mode) {
       case SequentialExecution:
         if (script->hasIonScript()) {
             // It is only safe to modify script->ion if the script is not currently
-            // running, because IonFrameIterator needs to tell what ionScript to
+            // running, because JitFrameIterator needs to tell what ionScript to
             // use (either the one on the JSScript, or the one hidden in the
             // breadcrumbs Invalidation() leaves). Therefore, if invalidation
             // fails, we cannot disable the script.
             if (!Invalidate(cx, script, mode, false))
                 return;
         }
 
         script->setIonScript(ION_DISABLED_SCRIPT);
--- a/js/src/jit/IonFrames-inl.h
+++ b/js/src/jit/IonFrames-inl.h
@@ -6,78 +6,78 @@
 
 #ifndef jit_IonFrames_inl_h
 #define jit_IonFrames_inl_h
 
 #ifdef JS_ION
 
 #include "jit/IonFrames.h"
 
-#include "jit/IonFrameIterator.h"
+#include "jit/JitFrameIterator.h"
 #include "jit/LIR.h"
 #include "vm/ForkJoin.h"
 
-#include "jit/IonFrameIterator-inl.h"
+#include "jit/JitFrameIterator-inl.h"
 
 namespace js {
 namespace jit {
 
 inline void
 SafepointIndex::resolve()
 {
     JS_ASSERT(!resolved);
     safepointOffset_ = safepoint_->offset();
 #ifdef DEBUG
     resolved = true;
 #endif
 }
 
 inline uint8_t *
-IonFrameIterator::returnAddress() const
+JitFrameIterator::returnAddress() const
 {
     IonCommonFrameLayout *current = (IonCommonFrameLayout *) current_;
     return current->returnAddress();
 }
 
 inline size_t
-IonFrameIterator::prevFrameLocalSize() const
+JitFrameIterator::prevFrameLocalSize() const
 {
     IonCommonFrameLayout *current = (IonCommonFrameLayout *) current_;
     return current->prevFrameLocalSize();
 }
 
 inline FrameType
-IonFrameIterator::prevType() const
+JitFrameIterator::prevType() const
 {
     IonCommonFrameLayout *current = (IonCommonFrameLayout *) current_;
     return current->prevType();
 }
 
 inline bool
-IonFrameIterator::isFakeExitFrame() const
+JitFrameIterator::isFakeExitFrame() const
 {
     bool res = (prevType() == JitFrame_Unwound_Rectifier ||
                 prevType() == JitFrame_Unwound_IonJS ||
                 prevType() == JitFrame_Unwound_BaselineStub);
     JS_ASSERT_IF(res, type() == JitFrame_Exit || type() == JitFrame_BaselineJS);
     return res;
 }
 
 inline IonExitFrameLayout *
-IonFrameIterator::exitFrame() const
+JitFrameIterator::exitFrame() const
 {
     JS_ASSERT(type() == JitFrame_Exit);
     JS_ASSERT(!isFakeExitFrame());
     return (IonExitFrameLayout *) fp();
 }
 
 inline BaselineFrame *
 GetTopBaselineFrame(JSContext *cx)
 {
-    IonFrameIterator iter(cx);
+    JitFrameIterator iter(cx);
     JS_ASSERT(iter.type() == JitFrame_Exit);
     ++iter;
     if (iter.isBaselineStub())
         ++iter;
     JS_ASSERT(iter.isBaselineJS());
     return iter.baselineFrame();
 }
 
--- a/js/src/jit/IonFrames.cpp
+++ b/js/src/jit/IonFrames.cpp
@@ -22,17 +22,17 @@
 #include "jit/PcScriptCache.h"
 #include "jit/Recover.h"
 #include "jit/Safepoints.h"
 #include "jit/Snapshots.h"
 #include "jit/VMFunctions.h"
 #include "vm/ForkJoin.h"
 #include "vm/Interpreter.h"
 
-#include "jit/IonFrameIterator-inl.h"
+#include "jit/JitFrameIterator-inl.h"
 #include "vm/Probes-inl.h"
 
 namespace js {
 namespace jit {
 
 // Given a slot index, returns the offset, in bytes, of that slot from an
 // IonJSFrameLayout. Slot distances are uniform across architectures, however,
 // the distance does depend on the size of the frame header.
@@ -67,56 +67,56 @@ ReadFrameInt32Slot(IonJSFrameLayout *fp,
 }
 
 static inline bool
 ReadFrameBooleanSlot(IonJSFrameLayout *fp, int32_t slot)
 {
     return *(bool *)((char *)fp + OffsetOfFrameSlot(slot));
 }
 
-IonFrameIterator::IonFrameIterator(JSContext *cx)
+JitFrameIterator::JitFrameIterator(JSContext *cx)
   : current_(cx->mainThread().ionTop),
     type_(JitFrame_Exit),
     returnAddressToFp_(nullptr),
     frameSize_(0),
     cachedSafepointIndex_(nullptr),
     activation_(nullptr),
     mode_(SequentialExecution)
 {
 }
 
-IonFrameIterator::IonFrameIterator(const ActivationIterator &activations)
+JitFrameIterator::JitFrameIterator(const ActivationIterator &activations)
     : current_(activations.jitTop()),
       type_(JitFrame_Exit),
       returnAddressToFp_(nullptr),
       frameSize_(0),
       cachedSafepointIndex_(nullptr),
       activation_(activations.activation()->asJit()),
       mode_(SequentialExecution)
 {
 }
 
-IonFrameIterator::IonFrameIterator(IonJSFrameLayout *fp, ExecutionMode mode)
+JitFrameIterator::JitFrameIterator(IonJSFrameLayout *fp, ExecutionMode mode)
   : current_((uint8_t *)fp),
     type_(JitFrame_IonJS),
     returnAddressToFp_(fp->returnAddress()),
     frameSize_(fp->prevFrameLocalSize()),
     mode_(mode)
 {
 }
 
 bool
-IonFrameIterator::checkInvalidation() const
+JitFrameIterator::checkInvalidation() const
 {
     IonScript *dummy;
     return checkInvalidation(&dummy);
 }
 
 bool
-IonFrameIterator::checkInvalidation(IonScript **ionScriptOut) const
+JitFrameIterator::checkInvalidation(IonScript **ionScriptOut) const
 {
     uint8_t *returnAddr = returnAddressToFp();
     JSScript *script = this->script();
     // N.B. the current IonScript is not the same as the frame's
     // IonScript if the frame has since been invalidated.
     bool invalidated;
     if (mode_ == ParallelExecution) {
         // Parallel execution does not have invalidating bailouts.
@@ -132,96 +132,96 @@ IonFrameIterator::checkInvalidation(IonS
     uint8_t *ionScriptDataOffset = returnAddr + invalidationDataOffset;
     IonScript *ionScript = (IonScript *) Assembler::getPointer(ionScriptDataOffset);
     JS_ASSERT(ionScript->containsReturnAddress(returnAddr));
     *ionScriptOut = ionScript;
     return true;
 }
 
 CalleeToken
-IonFrameIterator::calleeToken() const
+JitFrameIterator::calleeToken() const
 {
     return ((IonJSFrameLayout *) current_)->calleeToken();
 }
 
 JSFunction *
-IonFrameIterator::callee() const
+JitFrameIterator::callee() const
 {
     JS_ASSERT(isScripted());
     JS_ASSERT(isFunctionFrame());
     return CalleeTokenToFunction(calleeToken());
 }
 
 JSFunction *
-IonFrameIterator::maybeCallee() const
+JitFrameIterator::maybeCallee() const
 {
     if (isScripted() && (isFunctionFrame()))
         return callee();
     return nullptr;
 }
 
 bool
-IonFrameIterator::isNative() const
+JitFrameIterator::isNative() const
 {
     if (type_ != JitFrame_Exit || isFakeExitFrame())
         return false;
     return exitFrame()->footer()->jitCode() == nullptr;
 }
 
 bool
-IonFrameIterator::isOOLNative() const
+JitFrameIterator::isOOLNative() const
 {
     if (type_ != JitFrame_Exit)
         return false;
     return exitFrame()->footer()->jitCode() == ION_FRAME_OOL_NATIVE;
 }
 
 bool
-IonFrameIterator::isOOLPropertyOp() const
+JitFrameIterator::isOOLPropertyOp() const
 {
     if (type_ != JitFrame_Exit)
         return false;
     return exitFrame()->footer()->jitCode() == ION_FRAME_OOL_PROPERTY_OP;
 }
 
 bool
-IonFrameIterator::isOOLProxy() const
+JitFrameIterator::isOOLProxy() const
 {
     if (type_ != JitFrame_Exit)
         return false;
     return exitFrame()->footer()->jitCode() == ION_FRAME_OOL_PROXY;
 }
 
 bool
-IonFrameIterator::isDOMExit() const
+JitFrameIterator::isDOMExit() const
 {
     if (type_ != JitFrame_Exit)
         return false;
     return exitFrame()->isDomExit();
 }
 
 bool
-IonFrameIterator::isFunctionFrame() const
+JitFrameIterator::isFunctionFrame() const
 {
     return CalleeTokenIsFunction(calleeToken());
 }
 
 JSScript *
-IonFrameIterator::script() const
+JitFrameIterator::script() const
 {
     JS_ASSERT(isScripted());
     if (isBaselineJS())
         return baselineFrame()->script();
     JSScript *script = ScriptFromCalleeToken(calleeToken());
     JS_ASSERT(script);
     return script;
 }
 
 void
-IonFrameIterator::baselineScriptAndPc(JSScript **scriptRes, jsbytecode **pcRes) const
+JitFrameIterator::baselineScriptAndPc(JSScript **scriptRes, jsbytecode **pcRes) const
 {
     JS_ASSERT(isBaselineJS());
     JSScript *script = this->script();
     if (scriptRes)
         *scriptRes = script;
     uint8_t *retAddr = returnAddressToFp();
     if (pcRes) {
         // If the return address is into the prologue entry address, then assume start
@@ -241,17 +241,17 @@ IonFrameIterator::baselineScriptAndPc(JS
 
         // If not, the return address _must_ be the start address of an op, which can
         // be computed from the pc mapping table.
         *pcRes = script->baselineScript()->pcForReturnAddress(script, retAddr);
     }
 }
 
 Value *
-IonFrameIterator::actualArgs() const
+JitFrameIterator::actualArgs() const
 {
     return jsFrame()->argv() + 1;
 }
 
 static inline size_t
 SizeOfFramePrefix(FrameType type)
 {
     switch (type) {
@@ -270,33 +270,33 @@ SizeOfFramePrefix(FrameType type)
       case JitFrame_Exit:
         return IonExitFrameLayout::Size();
       default:
         MOZ_ASSUME_UNREACHABLE("unknown frame type");
     }
 }
 
 uint8_t *
-IonFrameIterator::prevFp() const
+JitFrameIterator::prevFp() const
 {
     size_t currentSize = SizeOfFramePrefix(type_);
     // This quick fix must be removed as soon as bug 717297 land.  This is
     // needed because the descriptor size of JS-to-JS frame which is just after
     // a Rectifier frame should not change. (cf EnsureExitFrame function)
     if (isFakeExitFrame()) {
         JS_ASSERT(SizeOfFramePrefix(JitFrame_BaselineJS) ==
                   SizeOfFramePrefix(JitFrame_IonJS));
         currentSize = SizeOfFramePrefix(JitFrame_IonJS);
     }
     currentSize += current()->prevFrameLocalSize();
     return current_ + currentSize;
 }
 
-IonFrameIterator &
-IonFrameIterator::operator++()
+JitFrameIterator &
+JitFrameIterator::operator++()
 {
     JS_ASSERT(type_ != JitFrame_Entry);
 
     frameSize_ = prevFrameLocalSize();
     cachedSafepointIndex_ = nullptr;
 
     // If the next frame is the entry frame, just exit. Don't update current_,
     // since the entry and first frames overlap.
@@ -314,27 +314,27 @@ IonFrameIterator::operator++()
     else if (type_ == JitFrame_Unwound_BaselineStub)
         type_ = JitFrame_BaselineStub;
     returnAddressToFp_ = current()->returnAddress();
     current_ = prev;
     return *this;
 }
 
 uintptr_t *
-IonFrameIterator::spillBase() const
+JitFrameIterator::spillBase() const
 {
     // Get the base address to where safepoint registers are spilled.
     // Out-of-line calls do not unwind the extra padding space used to
     // aggregate bailout tables, so we use frameSize instead of frameLocals,
     // which would only account for local stack slots.
     return reinterpret_cast<uintptr_t *>(fp() - ionScript()->frameSize());
 }
 
 MachineState
-IonFrameIterator::machineState() const
+JitFrameIterator::machineState() const
 {
     SafepointReader reader(ionScript(), safepoint());
     uintptr_t *spill = spillBase();
 
     MachineState machine;
     for (GeneralRegisterBackwardIterator iter(reader.allGprSpills()); iter.more(); iter++)
         machine.setRegisterLocation(*iter, --spill);
 
@@ -441,17 +441,17 @@ HandleExceptionIon(JSContext *cx, const 
 
           default:
             MOZ_ASSUME_UNREACHABLE("Unexpected try note");
         }
     }
 }
 
 static void
-HandleExceptionBaseline(JSContext *cx, const IonFrameIterator &frame, ResumeFromException *rfe,
+HandleExceptionBaseline(JSContext *cx, const JitFrameIterator &frame, ResumeFromException *rfe,
                         bool *calledDebugEpilogue)
 {
     JS_ASSERT(frame.isBaselineJS());
     JS_ASSERT(!*calledDebugEpilogue);
 
     RootedScript script(cx);
     jsbytecode *pc;
     frame.baselineScriptAndPc(script.address(), &pc);
@@ -578,17 +578,17 @@ HandleException(ResumeFromException *rfe
 
     // Clear any Ion return override that's been set.
     // This may happen if a callVM function causes an invalidation (setting the
     // override), and then fails, bypassing the bailout handlers that would
     // otherwise clear the return override.
     if (cx->runtime()->hasIonReturnOverride())
         cx->runtime()->takeIonReturnOverride();
 
-    IonFrameIterator iter(cx);
+    JitFrameIterator iter(cx);
     while (!iter.isEntry()) {
         bool overrecursed = false;
         if (iter.isIonJS()) {
             // Search each inlined frame for live iterator objects, and close
             // them.
             InlineFrameIterator frames(cx, &iter);
 
             // Invalidation state will be the same for all inlined scripts in the frame.
@@ -688,17 +688,17 @@ HandleException(ResumeFromException *rfe
 
     rfe->stackPointer = iter.fp();
 }
 
 void
 HandleParallelFailure(ResumeFromException *rfe)
 {
     ForkJoinContext *cx = ForkJoinContext::current();
-    IonFrameIterator iter(cx->perThreadData->ionTop, ParallelExecution);
+    JitFrameIterator iter(cx->perThreadData->ionTop, ParallelExecution);
 
     parallel::Spew(parallel::SpewBailouts, "Bailing from VM reentry");
 
     while (!iter.isEntry()) {
         if (iter.isScripted()) {
             cx->bailoutRecord->updateCause(ParallelBailoutUnsupportedVM,
                                            iter.script(), iter.script(), nullptr);
             break;
@@ -769,49 +769,49 @@ MarkCalleeToken(JSTracer *trc, CalleeTok
       }
       default:
         MOZ_ASSUME_UNREACHABLE("unknown callee token type");
     }
 }
 
 #ifdef JS_NUNBOX32
 static inline uintptr_t
-ReadAllocation(const IonFrameIterator &frame, const LAllocation *a)
+ReadAllocation(const JitFrameIterator &frame, const LAllocation *a)
 {
     if (a->isGeneralReg()) {
         Register reg = a->toGeneralReg()->reg();
         return frame.machineState().read(reg);
     }
     if (a->isStackSlot()) {
         uint32_t slot = a->toStackSlot()->slot();
         return *frame.jsFrame()->slotRef(slot);
     }
     uint32_t index = a->toArgument()->index();
     uint8_t *argv = reinterpret_cast<uint8_t *>(frame.jsFrame()->argv());
     return *reinterpret_cast<uintptr_t *>(argv + index);
 }
 #endif
 
 static void
-MarkActualArguments(JSTracer *trc, const IonFrameIterator &frame)
+MarkActualArguments(JSTracer *trc, const JitFrameIterator &frame)
 {
     IonJSFrameLayout *layout = frame.jsFrame();
     JS_ASSERT(CalleeTokenIsFunction(layout->calleeToken()));
 
     size_t nargs = frame.numActualArgs();
 
     // Trace function arguments. Note + 1 for thisv.
     Value *argv = layout->argv();
     for (size_t i = 0; i < nargs + 1; i++)
         gc::MarkValueRoot(trc, &argv[i], "ion-argv");
 }
 
 #ifdef JS_NUNBOX32
 static inline void
-WriteAllocation(const IonFrameIterator &frame, const LAllocation *a, uintptr_t value)
+WriteAllocation(const JitFrameIterator &frame, const LAllocation *a, uintptr_t value)
 {
     if (a->isGeneralReg()) {
         Register reg = a->toGeneralReg()->reg();
         frame.machineState().write(reg, value);
         return;
     }
     if (a->isStackSlot()) {
         uint32_t slot = a->toStackSlot()->slot();
@@ -820,17 +820,17 @@ WriteAllocation(const IonFrameIterator &
     }
     uint32_t index = a->toArgument()->index();
     uint8_t *argv = reinterpret_cast<uint8_t *>(frame.jsFrame()->argv());
     *reinterpret_cast<uintptr_t *>(argv + index) = value;
 }
 #endif
 
 static void
-MarkIonJSFrame(JSTracer *trc, const IonFrameIterator &frame)
+MarkIonJSFrame(JSTracer *trc, const JitFrameIterator &frame)
 {
     IonJSFrameLayout *layout = (IonJSFrameLayout *)frame.fp();
 
     layout->replaceCalleeToken(MarkCalleeToken(trc, layout->calleeToken()));
 
     IonScript *ionScript = nullptr;
     if (frame.checkInvalidation(&ionScript)) {
         // This frame has been invalidated, meaning that its IonScript is no
@@ -890,17 +890,17 @@ MarkIonJSFrame(JSTracer *trc, const IonF
             WriteAllocation(frame, &payload, layout.s.payload.uintptr);
         }
     }
 #endif
 }
 
 #ifdef JSGC_GENERATIONAL
 static void
-UpdateIonJSFrameForMinorGC(JSTracer *trc, const IonFrameIterator &frame)
+UpdateIonJSFrameForMinorGC(JSTracer *trc, const JitFrameIterator &frame)
 {
     // Minor GCs may move slots/elements allocated in the nursery. Update
     // any slots/elements pointers stored in this frame.
 
     IonJSFrameLayout *layout = (IonJSFrameLayout *)frame.fp();
 
     IonScript *ionScript = nullptr;
     if (frame.checkInvalidation(&ionScript)) {
@@ -936,34 +936,34 @@ UpdateIonJSFrameForMinorGC(JSTracer *trc
     while (safepoint.getSlotsOrElementsSlot(&slot)) {
         HeapSlot **slots = reinterpret_cast<HeapSlot **>(layout->slotRef(slot));
         trc->runtime()->gcNursery.forwardBufferPointer(slots);
     }
 }
 #endif
 
 static void
-MarkBaselineStubFrame(JSTracer *trc, const IonFrameIterator &frame)
+MarkBaselineStubFrame(JSTracer *trc, const JitFrameIterator &frame)
 {
     // Mark the ICStub pointer stored in the stub frame. This is necessary
     // so that we don't destroy the stub code after unlinking the stub.
 
     JS_ASSERT(frame.type() == JitFrame_BaselineStub);
     IonBaselineStubFrameLayout *layout = (IonBaselineStubFrameLayout *)frame.fp();
 
     if (ICStub *stub = layout->maybeStubPtr()) {
         JS_ASSERT(ICStub::CanMakeCalls(stub->kind()));
         stub->trace(trc);
     }
 }
 
 void
 JitActivationIterator::jitStackRange(uintptr_t *&min, uintptr_t *&end)
 {
-    IonFrameIterator frames(jitTop(), SequentialExecution);
+    JitFrameIterator frames(jitTop(), SequentialExecution);
 
     if (frames.isFakeExitFrame()) {
         min = reinterpret_cast<uintptr_t *>(frames.fp());
     } else {
         IonExitFrameLayout *exitFrame = frames.exitFrame();
         IonExitFooterFrame *footer = exitFrame->footer();
         const VMFunction *f = footer->function();
         if (exitFrame->isWrapperExit() && f->outParam == Type_Handle) {
@@ -989,17 +989,17 @@ JitActivationIterator::jitStackRange(uin
 
     while (!frames.done())
         ++frames;
 
     end = reinterpret_cast<uintptr_t *>(frames.prevFp());
 }
 
 static void
-MarkJitExitFrame(JSTracer *trc, const IonFrameIterator &frame)
+MarkJitExitFrame(JSTracer *trc, const JitFrameIterator &frame)
 {
     // Ignore fake exit frames created by EnsureExitFrame.
     if (frame.isFakeExitFrame())
         return;
 
     IonExitFooterFrame *footer = frame.exitFrame()->footer();
 
     // Mark the code of the code handling the exit path.  This is needed because
@@ -1129,17 +1129,17 @@ MarkJitExitFrame(JSTracer *trc, const Io
           case VMFunction::RootCell:
             gc::MarkGCThingRoot(trc, footer->outParam<void *>(), "ion-vm-out");
             break;
         }
     }
 }
 
 static void
-MarkRectifierFrame(JSTracer *trc, const IonFrameIterator &frame)
+MarkRectifierFrame(JSTracer *trc, const JitFrameIterator &frame)
 {
     // Mark thisv.
     //
     // Baseline JIT code generated as part of the ICCall_Fallback stub may use
     // it if we're calling a constructor that returns a primitive value.
     IonRectifierFrameLayout *layout = (IonRectifierFrameLayout *)frame.fp();
     gc::MarkValueRoot(trc, &layout->argv()[0], "ion-thisv");
 }
@@ -1152,17 +1152,17 @@ MarkJitActivation(JSTracer *trc, const J
         // GC can modify spilled registers, breaking our register checks.
         // To handle this, we disable these checks for the current VM call
         // when a GC happens.
         JitActivation *activation = activations.activation()->asJit();
         activation->setCheckRegs(false);
     }
 #endif
 
-    for (IonFrameIterator frames(activations); !frames.done(); ++frames) {
+    for (JitFrameIterator frames(activations); !frames.done(); ++frames) {
         switch (frames.type()) {
           case JitFrame_Exit:
             MarkJitExitFrame(trc, frames);
             break;
           case JitFrame_BaselineJS:
             frames.baselineFrame()->trace(trc, frames);
             break;
           case JitFrame_BaselineStub:
@@ -1192,17 +1192,17 @@ MarkJitActivations(JSRuntime *rt, JSTrac
 }
 
 #ifdef JSGC_GENERATIONAL
 void
 UpdateJitActivationsForMinorGC(JSRuntime *rt, JSTracer *trc)
 {
     JS_ASSERT(trc->runtime()->isHeapMinorCollecting());
     for (JitActivationIterator activations(rt); !activations.done(); ++activations) {
-        for (IonFrameIterator frames(activations); !frames.done(); ++frames) {
+        for (JitFrameIterator frames(activations); !frames.done(); ++frames) {
             if (frames.type() == JitFrame_IonJS)
                 UpdateIonJSFrameForMinorGC(trc, frames);
         }
     }
 }
 #endif
 
 void
@@ -1215,17 +1215,17 @@ AutoTempAllocatorRooter::trace(JSTracer 
 void
 GetPcScript(JSContext *cx, JSScript **scriptRes, jsbytecode **pcRes)
 {
     IonSpew(IonSpew_Snapshots, "Recover PC & Script from the last frame.");
 
     JSRuntime *rt = cx->runtime();
 
     // Recover the return address.
-    IonFrameIterator it(rt->mainThread.ionTop, SequentialExecution);
+    JitFrameIterator it(rt->mainThread.ionTop, SequentialExecution);
 
     // If the previous frame is a rectifier frame (maybe unwound),
     // skip past it.
     if (it.prevType() == JitFrame_Rectifier || it.prevType() == JitFrame_Unwound_Rectifier) {
         ++it;
         JS_ASSERT(it.prevType() == JitFrame_BaselineStub ||
                   it.prevType() == JitFrame_BaselineJS ||
                   it.prevType() == JitFrame_IonJS);
@@ -1301,17 +1301,17 @@ SnapshotIterator::SnapshotIterator(IonSc
              ionScript->recoversSize()),
     fp_(fp),
     machine_(machine),
     ionScript_(ionScript)
 {
     JS_ASSERT(snapshotOffset < ionScript->snapshotsListSize());
 }
 
-SnapshotIterator::SnapshotIterator(const IonFrameIterator &iter)
+SnapshotIterator::SnapshotIterator(const JitFrameIterator &iter)
   : snapshot_(iter.ionScript()->snapshots(),
               iter.osiIndex()->snapshotOffset(),
               iter.ionScript()->snapshotsRVATableSize(),
               iter.ionScript()->snapshotsListSize()),
     recover_(snapshot_,
              iter.ionScript()->recovers(),
              iter.ionScript()->recoversSize()),
     fp_(iter.jsFrame()),
@@ -1531,62 +1531,62 @@ void
 SnapshotIterator::nextFrame()
 {
     nextInstruction();
     while (!instruction()->isResumePoint())
         skipInstruction();
 }
 
 IonScript *
-IonFrameIterator::ionScript() const
+JitFrameIterator::ionScript() const
 {
     JS_ASSERT(type() == JitFrame_IonJS);
 
     IonScript *ionScript = nullptr;
     if (checkInvalidation(&ionScript))
         return ionScript;
     switch (GetCalleeTokenTag(calleeToken())) {
       case CalleeToken_Function:
       case CalleeToken_Script:
         return mode_ == ParallelExecution ? script()->parallelIonScript() : script()->ionScript();
       default:
         MOZ_ASSUME_UNREACHABLE("unknown callee token type");
     }
 }
 
 const SafepointIndex *
-IonFrameIterator::safepoint() const
+JitFrameIterator::safepoint() const
 {
     if (!cachedSafepointIndex_)
         cachedSafepointIndex_ = ionScript()->getSafepointIndex(returnAddressToFp());
     return cachedSafepointIndex_;
 }
 
 const OsiIndex *
-IonFrameIterator::osiIndex() const
+JitFrameIterator::osiIndex() const
 {
     SafepointReader reader(ionScript(), safepoint());
     return ionScript()->getOsiIndex(reader.osiReturnPointOffset());
 }
 
 template <AllowGC allowGC>
 void
-InlineFrameIteratorMaybeGC<allowGC>::resetOn(const IonFrameIterator *iter)
+InlineFrameIteratorMaybeGC<allowGC>::resetOn(const JitFrameIterator *iter)
 {
     frame_ = iter;
     framesRead_ = 0;
     frameCount_ = UINT32_MAX;
 
     if (iter) {
         start_ = SnapshotIterator(*iter);
         findNextFrame();
     }
 }
-template void InlineFrameIteratorMaybeGC<NoGC>::resetOn(const IonFrameIterator *iter);
-template void InlineFrameIteratorMaybeGC<CanGC>::resetOn(const IonFrameIterator *iter);
+template void InlineFrameIteratorMaybeGC<NoGC>::resetOn(const JitFrameIterator *iter);
+template void InlineFrameIteratorMaybeGC<CanGC>::resetOn(const JitFrameIterator *iter);
 
 template <AllowGC allowGC>
 void
 InlineFrameIteratorMaybeGC<allowGC>::findNextFrame()
 {
     JS_ASSERT(more());
 
     si_ = start_;
@@ -1714,19 +1714,19 @@ InlineFrameIteratorMaybeGC<allowGC>::isC
     }
 
     return frame_->isConstructing();
 }
 template bool InlineFrameIteratorMaybeGC<NoGC>::isConstructing() const;
 template bool InlineFrameIteratorMaybeGC<CanGC>::isConstructing() const;
 
 bool
-IonFrameIterator::isConstructing() const
+JitFrameIterator::isConstructing() const
 {
-    IonFrameIterator parent(*this);
+    JitFrameIterator parent(*this);
 
     // Skip the current frame and look at the caller's.
     do {
         ++parent;
     } while (!parent.done() && !parent.isScripted());
 
     if (parent.isIonJS()) {
         // In the case of a JS frame, look up the pc from the snapshot.
@@ -1755,17 +1755,17 @@ IonFrameIterator::isConstructing() const
         return JSOp(*pc) == JSOP_NEW;
     }
 
     JS_ASSERT(parent.done());
     return activation_->firstFrameIsConstructing();
 }
 
 unsigned
-IonFrameIterator::numActualArgs() const
+JitFrameIterator::numActualArgs() const
 {
     if (isScripted())
         return jsFrame()->numActualArgs();
 
     JS_ASSERT(isNative());
     return exitFrame()->nativeExit()->argc();
 }
 
@@ -1786,17 +1786,17 @@ struct DumpOp {
 #else
         fprintf(stderr, "?\n");
 #endif
         i_++;
     }
 };
 
 void
-IonFrameIterator::dumpBaseline() const
+JitFrameIterator::dumpBaseline() const
 {
     JS_ASSERT(isBaselineJS());
 
     fprintf(stderr, " JS Baseline frame\n");
     if (isFunctionFrame()) {
         fprintf(stderr, "  callee fun: ");
 #ifdef DEBUG
         js_DumpObject(callee());
@@ -1893,17 +1893,17 @@ InlineFrameIteratorMaybeGC<allowGC>::dum
     }
 
     fputc('\n', stderr);
 }
 template void InlineFrameIteratorMaybeGC<NoGC>::dump() const;
 template void InlineFrameIteratorMaybeGC<CanGC>::dump() const;
 
 void
-IonFrameIterator::dump() const
+JitFrameIterator::dump() const
 {
     switch (type_) {
       case JitFrame_Entry:
         fprintf(stderr, " Entry frame\n");
         fprintf(stderr, "  Frame size: %u\n", unsigned(current()->prevFrameLocalSize()));
         break;
       case JitFrame_BaselineJS:
         dumpBaseline();
--- a/js/src/jit/IonFrames.h
+++ b/js/src/jit/IonFrames.h
@@ -9,17 +9,17 @@
 
 #ifdef JS_ION
 
 #include <stdint.h>
 
 #include "jscntxt.h"
 #include "jsfun.h"
 
-#include "jit/IonFrameIterator.h"
+#include "jit/JitFrameIterator.h"
 
 namespace js {
 namespace jit {
 
 typedef void * CalleeToken;
 
 enum CalleeTokenTag
 {
@@ -277,17 +277,17 @@ MakeFrameDescriptor(uint32_t frameSize, 
 {
     return (frameSize << FRAMESIZE_SHIFT) | type;
 }
 
 // Returns the JSScript associated with the topmost Ion frame.
 inline JSScript *
 GetTopIonJSScript(uint8_t *ionTop, void **returnAddrOut, ExecutionMode mode)
 {
-    IonFrameIterator iter(ionTop, mode);
+    JitFrameIterator iter(ionTop, mode);
     JS_ASSERT(iter.type() == JitFrame_Exit);
     ++iter;
 
     JS_ASSERT(iter.returnAddressToFp() != nullptr);
     if (returnAddrOut)
         *returnAddrOut = (void *) iter.returnAddressToFp();
 
     if (iter.isBaselineStub()) {
rename from js/src/jit/IonFrameIterator-inl.h
rename to js/src/jit/JitFrameIterator-inl.h
--- a/js/src/jit/IonFrameIterator-inl.h
+++ b/js/src/jit/JitFrameIterator-inl.h
@@ -1,20 +1,20 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef jit_IonFrameIterator_inl_h
-#define jit_IonFrameIterator_inl_h
+#ifndef jit_JitFrameIterator_inl_h
+#define jit_JitFrameIterator_inl_h
 
 #ifdef JS_ION
 
-#include "jit/IonFrameIterator.h"
+#include "jit/JitFrameIterator.h"
 
 #include "jit/Bailouts.h"
 #include "jit/BaselineFrame.h"
 
 namespace js {
 namespace jit {
 
 template <AllowGC allowGC>
@@ -29,20 +29,20 @@ InlineFrameIteratorMaybeGC<allowGC>::Inl
 {
     if (iter) {
         start_ = SnapshotIterator(*iter);
         findNextFrame();
     }
 }
 
 inline BaselineFrame *
-IonFrameIterator::baselineFrame() const
+JitFrameIterator::baselineFrame() const
 {
     JS_ASSERT(isBaselineJS());
     return (BaselineFrame *)(fp() - BaselineFrame::FramePointerOffset - BaselineFrame::Size());
 }
 
 } // namespace jit
 } // namespace js
 
 #endif // JS_ION
 
-#endif /* jit_IonFrameIterator_inl_h */
+#endif /* jit_JitFrameIterator_inl_h */
rename from js/src/jit/IonFrameIterator.h
rename to js/src/jit/JitFrameIterator.h
--- a/js/src/jit/IonFrameIterator.h
+++ b/js/src/jit/JitFrameIterator.h
@@ -1,16 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef jit_IonFrameIterator_h
-#define jit_IonFrameIterator_h
+#ifndef jit_JitFrameIterator_h
+#define jit_JitFrameIterator_h
 
 #ifdef JS_ION
 
 #include "jsfun.h"
 #include "jsscript.h"
 #include "jstypes.h"
 
 #include "jit/IonCode.h"
@@ -76,45 +76,45 @@ enum ReadFrameArgsBehavior {
 class IonCommonFrameLayout;
 class IonJSFrameLayout;
 class IonExitFrameLayout;
 
 class BaselineFrame;
 
 class JitActivation;
 
-class IonFrameIterator
+class JitFrameIterator
 {
   protected:
     uint8_t *current_;
     FrameType type_;
     uint8_t *returnAddressToFp_;
     size_t frameSize_;
 
   private:
     mutable const SafepointIndex *cachedSafepointIndex_;
     const JitActivation *activation_;
     ExecutionMode mode_;
 
     void dumpBaseline() const;
 
   public:
-    explicit IonFrameIterator(uint8_t *top, ExecutionMode mode)
+    explicit JitFrameIterator(uint8_t *top, ExecutionMode mode)
       : current_(top),
         type_(JitFrame_Exit),
         returnAddressToFp_(nullptr),
         frameSize_(0),
         cachedSafepointIndex_(nullptr),
         activation_(nullptr),
         mode_(mode)
     { }
 
-    explicit IonFrameIterator(JSContext *cx);
-    explicit IonFrameIterator(const ActivationIterator &activations);
-    explicit IonFrameIterator(IonJSFrameLayout *fp, ExecutionMode mode);
+    explicit JitFrameIterator(JSContext *cx);
+    explicit JitFrameIterator(const ActivationIterator &activations);
+    explicit JitFrameIterator(IonJSFrameLayout *fp, ExecutionMode mode);
 
     // Current frame information.
     FrameType type() const {
         return type_;
     }
     uint8_t *fp() const {
         return current_;
     }
@@ -190,17 +190,17 @@ class IonFrameIterator
         return frameSize_;
     }
 
     // Functions used to iterate on frames. When prevType is JitFrame_Entry,
     // the current frame is the last frame.
     inline bool done() const {
         return type_ == JitFrame_Entry;
     }
-    IonFrameIterator &operator++();
+    JitFrameIterator &operator++();
 
     // Returns the IonScript associated with this JS frame.
     IonScript *ionScript() const;
 
     // Returns the Safepoint associated with this JS frame. Incurs a lookup
     // overhead.
     const SafepointIndex *safepoint() const;
 
@@ -346,17 +346,17 @@ class SnapshotIterator
     }
 
   public:
     // Connect all informations about the current script in order to recover the
     // content of baseline frames.
 
     SnapshotIterator(IonScript *ionScript, SnapshotOffset snapshotOffset,
                      IonJSFrameLayout *fp, const MachineState &machine);
-    SnapshotIterator(const IonFrameIterator &iter);
+    SnapshotIterator(const JitFrameIterator &iter);
     SnapshotIterator(const IonBailoutIterator &iter);
     SnapshotIterator();
 
     Value read() {
         return allocationValue(readAllocation());
     }
     Value maybeRead(bool silentFailure = false) {
         RValueAllocation a = readAllocation();
@@ -418,17 +418,17 @@ class SnapshotIterator
     }
 };
 
 // Reads frame information in callstack order (that is, innermost frame to
 // outermost frame).
 template <AllowGC allowGC=CanGC>
 class InlineFrameIteratorMaybeGC
 {
-    const IonFrameIterator *frame_;
+    const JitFrameIterator *frame_;
     SnapshotIterator start_;
     SnapshotIterator si_;
     uint32_t framesRead_;
 
     // When the inline-frame-iterator is created, this variable is defined to
     // UINT32_MAX. Then the first iteration of findNextFrame, which settle on
     // the innermost frame, is used to update this counter to the number of
     // frames contained in the recover buffer.
@@ -442,24 +442,24 @@ class InlineFrameIteratorMaybeGC
     struct Nop {
         void operator()(const Value &v) { }
     };
 
   private:
     void findNextFrame();
 
   public:
-    InlineFrameIteratorMaybeGC(JSContext *cx, const IonFrameIterator *iter)
+    InlineFrameIteratorMaybeGC(JSContext *cx, const JitFrameIterator *iter)
       : callee_(cx),
         script_(cx)
     {
         resetOn(iter);
     }
 
-    InlineFrameIteratorMaybeGC(JSRuntime *rt, const IonFrameIterator *iter)
+    InlineFrameIteratorMaybeGC(JSRuntime *rt, const JitFrameIterator *iter)
       : callee_(rt),
         script_(rt)
     {
         resetOn(iter);
     }
 
     InlineFrameIteratorMaybeGC(JSContext *cx, const IonBailoutIterator *iter);
 
@@ -609,19 +609,19 @@ class InlineFrameIteratorMaybeGC
 
     InlineFrameIteratorMaybeGC &operator++() {
         findNextFrame();
         return *this;
     }
 
     void dump() const;
 
-    void resetOn(const IonFrameIterator *iter);
+    void resetOn(const JitFrameIterator *iter);
 
-    const IonFrameIterator &frame() const {
+    const JitFrameIterator &frame() const {
         return *frame_;
     }
 
     // Inline frame number, 0 for the outermost (non-inlined) frame.
     size_t frameNo() const {
         MOZ_ASSERT(frameCount_ != UINT32_MAX);
         return frameCount_ - framesRead_;
     }
@@ -633,9 +633,9 @@ class InlineFrameIteratorMaybeGC
 typedef InlineFrameIteratorMaybeGC<CanGC> InlineFrameIterator;
 typedef InlineFrameIteratorMaybeGC<NoGC> InlineFrameIteratorNoGC;
 
 } // namespace jit
 } // namespace js
 
 #endif // JS_ION
 
-#endif /* jit_IonFrameIterator_h */
+#endif /* jit_JitFrameIterator_h */
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -2326,25 +2326,26 @@ LIRGenerator::visitMonitorTypes(MMonitor
 }
 
 bool
 LIRGenerator::visitPostWriteBarrier(MPostWriteBarrier *ins)
 {
 #ifdef JSGC_GENERATIONAL
     switch (ins->value()->type()) {
       case MIRType_Object: {
+        LDefinition tmp = needTempForPostBarrier() ? temp() : LDefinition::BogusTemp();
         LPostWriteBarrierO *lir =
             new(alloc()) LPostWriteBarrierO(useRegisterOrConstant(ins->object()),
-                                            useRegister(ins->value()),
-                                            temp());
+                                            useRegister(ins->value()), tmp);
         return add(lir, ins) && assignSafepoint(lir, ins);
       }
       case MIRType_Value: {
+        LDefinition tmp = needTempForPostBarrier() ? temp() : LDefinition::BogusTemp();
         LPostWriteBarrierV *lir =
-            new(alloc()) LPostWriteBarrierV(useRegisterOrConstant(ins->object()), temp());
+            new(alloc()) LPostWriteBarrierV(useRegisterOrConstant(ins->object()), tmp);
         if (!useBox(lir, LPostWriteBarrierV::Input, ins->value()))
             return false;
         return add(lir, ins) && assignSafepoint(lir, ins);
       }
       default:
         // Currently, only objects can be in the nursery. Other instruction
         // types cannot hold nursery pointers.
         return true;
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -1013,17 +1013,17 @@ StringReplace(JSContext *cx, HandleStrin
     return rval.toString();
 }
 
 bool
 Recompile(JSContext *cx)
 {
     JS_ASSERT(cx->currentlyRunningInJit());
     JitActivationIterator activations(cx->runtime());
-    IonFrameIterator iter(activations);
+    JitFrameIterator iter(activations);
 
     JS_ASSERT(iter.type() == JitFrame_Exit);
     ++iter;
 
     bool isConstructing = iter.isConstructing();
     RootedScript script(cx, iter.script());
     JS_ASSERT(script->hasIonScript());
 
--- a/js/src/jit/arm/Bailouts-arm.cpp
+++ b/js/src/jit/arm/Bailouts-arm.cpp
@@ -65,17 +65,17 @@ class BailoutStack
 // Make sure the compiler doesn't add extra padding.
 static_assert((sizeof(BailoutStack) % 8) == 0, "BailoutStack should be 8-byte aligned.");
 
 } // namespace jit
 } // namespace js
 
 IonBailoutIterator::IonBailoutIterator(const JitActivationIterator &activations,
                                        BailoutStack *bailout)
-  : IonFrameIterator(activations),
+  : JitFrameIterator(activations),
     machine_(bailout->machine())
 {
     uint8_t *sp = bailout->parentStackPointer();
     uint8_t *fp = sp + bailout->frameSize();
 
     current_ = fp;
     type_ = JitFrame_IonJS;
     topFrameSize_ = current_ - sp;
@@ -100,17 +100,17 @@ IonBailoutIterator::IonBailoutIterator(c
     uint32_t bailoutId = ((tableOffset - tableStart) / BAILOUT_TABLE_ENTRY_SIZE) - 1;
     JS_ASSERT(bailoutId < BAILOUT_TABLE_SIZE);
 
     snapshotOffset_ = topIonScript_->bailoutToSnapshot(bailoutId);
 }
 
 IonBailoutIterator::IonBailoutIterator(const JitActivationIterator &activations,
                                        InvalidationBailoutStack *bailout)
-  : IonFrameIterator(activations),
+  : JitFrameIterator(activations),
     machine_(bailout->machine())
 {
     returnAddressToFp_ = bailout->osiPointReturnAddress();
     topIonScript_ = bailout->ionScript();
     const OsiIndex *osiIndex = topIonScript_->getOsiIndex(returnAddressToFp_);
 
     current_ = (uint8_t*) bailout->fp();
     type_ = JitFrame_IonJS;
--- a/js/src/jit/arm/Lowering-arm.h
+++ b/js/src/jit/arm/Lowering-arm.h
@@ -30,16 +30,18 @@ class LIRGeneratorARM : public LIRGenera
     // stores and loads; on ARM all registers are okay.
     LAllocation useByteOpRegister(MDefinition *mir);
     LAllocation useByteOpRegisterOrNonDoubleConstant(MDefinition *mir);
 
     inline LDefinition tempToUnbox() {
         return LDefinition::BogusTemp();
     }
 
+    bool needTempForPostBarrier() { return false; }
+
     // x64 has a scratch register, so no need for another temp for dispatch
     // ICs.
     LDefinition tempForDispatchCache(MIRType outputType = MIRType_None) {
         return LDefinition::BogusTemp();
     }
 
     void lowerUntypedPhiInput(MPhi *phi, uint32_t inputPosition, LBlock *block, size_t lirIndex);
     bool defineUntypedPhi(MPhi *phi, size_t lirIndex);
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -4329,8 +4329,34 @@ MacroAssemblerARMCompat::jumpWithPatch(R
     ARMBuffer::PoolEntry pe;
     BufferOffset bo = as_BranchPool(0xdeadbeef, label, &pe, cond);
     // Fill in a new CodeOffset with both the load and the
     // pool entry that the instruction loads from.
     CodeOffsetJump ret(bo.getOffset(), pe.encode());
     return ret;
 }
 
+#ifdef JSGC_GENERATIONAL
+
+void
+MacroAssemblerARMCompat::branchPtrInNurseryRange(Register ptr, Register temp, Label *label)
+{
+    JS_ASSERT(ptr != temp);
+    JS_ASSERT(ptr != ScratchRegister);
+
+    const Nursery &nursery = GetIonContext()->runtime->gcNursery();
+    movePtr(ImmWord(-ptrdiff_t(nursery.start())), ScratchRegister);
+    addPtr(ptr, ScratchRegister);
+    branchPtr(Assembler::Below, ScratchRegister, Imm32(Nursery::NurserySize), label);
+}
+
+void
+MacroAssemblerARMCompat::branchValueIsNurseryObject(ValueOperand value, Register temp, Label *label)
+{
+    Label done;
+
+    branchTestObject(Assembler::NotEqual, value, &done);
+    branchPtrInNurseryRange(value.payloadReg(), temp, label);
+
+    bind(&done);
+}
+
+#endif
--- a/js/src/jit/arm/MacroAssembler-arm.h
+++ b/js/src/jit/arm/MacroAssembler-arm.h
@@ -1554,16 +1554,21 @@ class MacroAssemblerARMCompat : public M
     }
     BufferOffset ma_BoundsCheck(Register bounded) {
         return as_cmp(bounded, Imm8(0));
     }
 
     void moveFloat32(FloatRegister src, FloatRegister dest) {
         as_vmov(VFPRegister(src).singleOverlay(), VFPRegister(dest).singleOverlay());
     }
+
+#ifdef JSGC_GENERATIONAL
+    void branchPtrInNurseryRange(Register ptr, Register temp, Label *label);
+    void branchValueIsNurseryObject(ValueOperand value, Register temp, Label *label);
+#endif
 };
 
 typedef MacroAssemblerARMCompat MacroAssemblerSpecific;
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_arm_MacroAssembler_arm_h */
--- a/js/src/jit/mips/MacroAssembler-mips.cpp
+++ b/js/src/jit/mips/MacroAssembler-mips.cpp
@@ -3240,8 +3240,26 @@ MacroAssemblerMIPSCompat::toggledCall(Ji
         as_nop();
     } else {
         as_nop();
         as_nop();
     }
     MOZ_ASSERT(nextOffset().getOffset() - offset.offset() == ToggledCallSize());
     return offset;
 }
+
+void
+MacroAssemblerMIPSCompat::branchPtrInNurseryRange(Register ptr, Register temp, Label *label)
+{
+    JS_ASSERT(temp != InvalidReg);
+    const Nursery &nursery = GetIonContext()->runtime->gcNursery();
+
+    // ptr and temp may be the same register, in which case we mustn't trash it
+    // before we use its contents.
+    if (ptr == temp) {
+        addPtr(ImmWord(-ptrdiff_t(nursery.start())), ptr);
+        branchPtr(Assembler::Below, ptr, Imm32(Nursery::NurserySize), label);
+    } else {
+        movePtr(ImmWord(-ptrdiff_t(nursery.start())), temp);
+        addPtr(ptr, temp);
+        branchPtr(Assembler::Below, temp, Imm32(Nursery::NurserySize), label);
+    }
+}
--- a/js/src/jit/mips/MacroAssembler-mips.h
+++ b/js/src/jit/mips/MacroAssembler-mips.h
@@ -1137,16 +1137,18 @@ public:
         BufferOffset bo = m_buffer.nextOffset();
         ma_liPatchable(bounded, Imm32(0));
         return bo;
     }
 
     void moveFloat32(FloatRegister src, FloatRegister dest) {
         as_movs(dest, src);
     }
+
+    void branchPtrInNurseryRange(Register ptr, Register temp, Label *label);
 };
 
 typedef MacroAssemblerMIPSCompat MacroAssemblerSpecific;
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_mips_MacroAssembler_mips_h */
--- a/js/src/jit/shared/CodeGenerator-x86-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-x86-shared.cpp
@@ -1553,20 +1553,22 @@ CodeGeneratorX86Shared::visitMathF(LMath
 
 bool
 CodeGeneratorX86Shared::visitFloor(LFloor *lir)
 {
     FloatRegister input = ToFloatRegister(lir->input());
     FloatRegister scratch = ScratchFloatReg;
     Register output = ToRegister(lir->output());
 
+    Label bailout;
+
     if (AssemblerX86Shared::HasSSE41()) {
         // Bail on negative-zero.
-        Assembler::Condition bailCond = masm.testNegativeZero(input, output);
-        if (!bailoutIf(bailCond, lir->snapshot()))
+        masm.branchNegativeZero(input, output, &bailout);
+        if (!bailoutFrom(&bailout, lir->snapshot()))
             return false;
 
         // Round toward -Infinity.
         masm.roundsd(input, scratch, JSC::X86Assembler::RoundDown);
 
         masm.cvttsd2si(scratch, output);
         masm.cmp32(output, Imm32(INT_MIN));
         if (!bailoutIf(Assembler::Equal, lir->snapshot()))
@@ -1574,18 +1576,18 @@ CodeGeneratorX86Shared::visitFloor(LFloo
     } else {
         Label negative, end;
 
         // Branch to a slow path for negative inputs. Doesn't catch NaN or -0.
         masm.xorpd(scratch, scratch);
         masm.branchDouble(Assembler::DoubleLessThan, input, scratch, &negative);
 
         // Bail on negative-zero.
-        Assembler::Condition bailCond = masm.testNegativeZero(input, output);
-        if (!bailoutIf(bailCond, lir->snapshot()))
+        masm.branchNegativeZero(input, output, &bailout);
+        if (!bailoutFrom(&bailout, lir->snapshot()))
             return false;
 
         // Input is non-negative, so truncation correctly rounds.
         masm.cvttsd2si(input, output);
         masm.cmp32(output, Imm32(INT_MIN));
         if (!bailoutIf(Assembler::Equal, lir->snapshot()))
             return false;
 
@@ -1620,20 +1622,22 @@ CodeGeneratorX86Shared::visitFloor(LFloo
 
 bool
 CodeGeneratorX86Shared::visitFloorF(LFloorF *lir)
 {
     FloatRegister input = ToFloatRegister(lir->input());
     FloatRegister scratch = ScratchFloatReg;
     Register output = ToRegister(lir->output());
 
+    Label bailout;
+
     if (AssemblerX86Shared::HasSSE41()) {
         // Bail on negative-zero.
-        Assembler::Condition bailCond = masm.testNegativeZeroFloat32(input, output);
-        if (!bailoutIf(bailCond, lir->snapshot()))
+        masm.branchNegativeZeroFloat32(input, output, &bailout);
+        if (!bailoutFrom(&bailout, lir->snapshot()))
             return false;
 
         // Round toward -Infinity.
         masm.roundss(input, scratch, JSC::X86Assembler::RoundDown);
 
         masm.cvttss2si(scratch, output);
         masm.cmp32(output, Imm32(INT_MIN));
         if (!bailoutIf(Assembler::Equal, lir->snapshot()))
@@ -1641,18 +1645,18 @@ CodeGeneratorX86Shared::visitFloorF(LFlo
     } else {
         Label negative, end;
 
         // Branch to a slow path for negative inputs. Doesn't catch NaN or -0.
         masm.xorps(scratch, scratch);
         masm.branchFloat(Assembler::DoubleLessThan, input, scratch, &negative);
 
         // Bail on negative-zero.
-        Assembler::Condition bailCond = masm.testNegativeZeroFloat32(input, output);
-        if (!bailoutIf(bailCond, lir->snapshot()))
+        masm.branchNegativeZeroFloat32(input, output, &bailout);
+        if (!bailoutFrom(&bailout, lir->snapshot()))
             return false;
 
         // Input is non-negative, so truncation correctly rounds.
         masm.cvttss2si(input, output);
         masm.cmp32(output, Imm32(INT_MIN));
         if (!bailoutIf(Assembler::Equal, lir->snapshot()))
             return false;
 
@@ -1688,28 +1692,28 @@ CodeGeneratorX86Shared::visitFloorF(LFlo
 bool
 CodeGeneratorX86Shared::visitRound(LRound *lir)
 {
     FloatRegister input = ToFloatRegister(lir->input());
     FloatRegister temp = ToFloatRegister(lir->temp());
     FloatRegister scratch = ScratchFloatReg;
     Register output = ToRegister(lir->output());
 
-    Label negative, end;
+    Label negative, end, bailout;
 
     // Load 0.5 in the temp register.
     masm.loadConstantDouble(0.5, temp);
 
     // Branch to a slow path for negative inputs. Doesn't catch NaN or -0.
     masm.xorpd(scratch, scratch);
     masm.branchDouble(Assembler::DoubleLessThan, input, scratch, &negative);
 
     // Bail on negative-zero.
-    Assembler::Condition bailCond = masm.testNegativeZero(input, output);
-    if (!bailoutIf(bailCond, lir->snapshot()))
+    masm.branchNegativeZero(input, output, &bailout);
+    if (!bailoutFrom(&bailout, lir->snapshot()))
         return false;
 
     // Input is non-negative. Add 0.5 and truncate, rounding down. Note that we
     // have to add the input to the temp register (which contains 0.5) because
     // we're not allowed to modify the input register.
     masm.addsd(input, temp);
 
     masm.cvttsd2si(temp, output);
@@ -1776,28 +1780,28 @@ CodeGeneratorX86Shared::visitRound(LRoun
 bool
 CodeGeneratorX86Shared::visitRoundF(LRoundF *lir)
 {
     FloatRegister input = ToFloatRegister(lir->input());
     FloatRegister temp = ToFloatRegister(lir->temp());
     FloatRegister scratch = ScratchFloatReg;
     Register output = ToRegister(lir->output());
 
-    Label negative, end;
+    Label negative, end, bailout;
 
     // Load 0.5 in the temp register.
     masm.loadConstantFloat32(0.5f, temp);
 
     // Branch to a slow path for negative inputs. Doesn't catch NaN or -0.
     masm.xorps(scratch, scratch);
     masm.branchFloat(Assembler::DoubleLessThan, input, scratch, &negative);
 
     // Bail on negative-zero.
-    Assembler::Condition bailCond = masm.testNegativeZeroFloat32(input, output);
-    if (!bailoutIf(bailCond, lir->snapshot()))
+    masm.branchNegativeZeroFloat32(input, output, &bailout);
+    if (!bailoutFrom(&bailout, lir->snapshot()))
         return false;
 
     // Input is non-negative. Add 0.5 and truncate, rounding down. Note that we
     // have to add the input to the temp register (which contains 0.5) because
     // we're not allowed to modify the input register.
     masm.addss(input, temp);
 
     masm.cvttss2si(temp, output);
--- a/js/src/jit/shared/MacroAssembler-x86-shared.cpp
+++ b/js/src/jit/shared/MacroAssembler-x86-shared.cpp
@@ -149,8 +149,50 @@ MacroAssemblerX86Shared::callWithExitFra
 bool
 MacroAssemblerX86Shared::buildOOLFakeExitFrame(void *fakeReturnAddr)
 {
     uint32_t descriptor = MakeFrameDescriptor(framePushed(), JitFrame_IonJS);
     Push(Imm32(descriptor));
     Push(ImmPtr(fakeReturnAddr));
     return true;
 }
+
+void
+MacroAssemblerX86Shared::branchNegativeZero(const FloatRegister &reg,
+                                            const Register &scratch,
+                                            Label *label)
+{
+    // Determines whether the low double contained in the XMM register reg
+    // is equal to -0.0.
+
+#if defined(JS_CODEGEN_X86)
+    Label nonZero;
+
+    // Compare to zero. Lets through {0, -0}.
+    xorpd(ScratchFloatReg, ScratchFloatReg);
+
+    // If reg is non-zero, jump to nonZero.
+    branchDouble(DoubleNotEqual, reg, ScratchFloatReg, &nonZero);
+
+    // Input register is either zero or negative zero. Retrieve sign of input.
+    movmskpd(reg, scratch);
+
+    // If reg is 1 or 3, input is negative zero.
+    // If reg is 0 or 2, input is a normal zero.
+    branchTest32(NonZero, scratch, Imm32(1), label);
+
+    bind(&nonZero);
+#elif defined(JS_CODEGEN_X64)
+    movq(reg, scratch);
+    cmpq(scratch, Imm32(1));
+    j(Overflow, label);
+#endif
+}
+
+void
+MacroAssemblerX86Shared::branchNegativeZeroFloat32(const FloatRegister &reg,
+                                                   const Register &scratch,
+                                                   Label *label)
+{
+    movd(reg, scratch);
+    cmpl(scratch, Imm32(1));
+    j(Overflow, label);
+}
--- a/js/src/jit/shared/MacroAssembler-x86-shared.h
+++ b/js/src/jit/shared/MacroAssembler-x86-shared.h
@@ -85,16 +85,19 @@ class MacroAssemblerX86Shared : public A
             j(Parity, label);
             return;
         }
 
         JS_ASSERT(!(cond & DoubleConditionBitSpecial));
         j(ConditionFromDoubleCondition(cond), label);
     }
 
+    void branchNegativeZero(const FloatRegister &reg, const Register &scratch, Label *label);
+    void branchNegativeZeroFloat32(const FloatRegister &reg, const Register &scratch, Label *label);
+
     void move32(const Imm32 &imm, const Register &dest) {
         // Use the ImmWord version of mov to register, which has special
         // optimizations. Casting to uint32_t here ensures that the value
         // is zero-extended.
         mov(ImmWord(uint32_t(imm.value)), dest);
     }
     void move32(const Imm32 &imm, const Operand &dest) {
         movl(imm, dest);
@@ -514,73 +517,43 @@ class MacroAssemblerX86Shared : public A
     }
 
     // Checks whether a double is representable as a 32-bit integer. If so, the
     // integer is written to the output register. Otherwise, a bailout is taken to
     // the given snapshot. This function overwrites the scratch float register.
     void convertDoubleToInt32(FloatRegister src, Register dest, Label *fail,
                               bool negativeZeroCheck = true)
     {
+        // Check for -0.0
+        if (negativeZeroCheck)
+            branchNegativeZero(src, dest, fail);
+
         cvttsd2si(src, dest);
         cvtsi2sd(dest, ScratchFloatReg);
         ucomisd(src, ScratchFloatReg);
         j(Assembler::Parity, fail);
         j(Assembler::NotEqual, fail);
 
-        // Check for -0
-        if (negativeZeroCheck) {
-            Label notZero;
-            testl(dest, dest);
-            j(Assembler::NonZero, &notZero);
-
-            if (Assembler::HasSSE41()) {
-                ptest(src, src);
-                j(Assembler::NonZero, fail);
-            } else {
-                // bit 0 = sign of low double
-                // bit 1 = sign of high double
-                movmskpd(src, dest);
-                andl(Imm32(1), dest);
-                j(Assembler::NonZero, fail);
-            }
-
-            bind(&notZero);
-        }
     }
 
     // Checks whether a float32 is representable as a 32-bit integer. If so, the
     // integer is written to the output register. Otherwise, a bailout is taken to
     // the given snapshot. This function overwrites the scratch float register.
     void convertFloat32ToInt32(FloatRegister src, Register dest, Label *fail,
                                bool negativeZeroCheck = true)
     {
+        // Check for -0.0
+        if (negativeZeroCheck)
+            branchNegativeZeroFloat32(src, dest, fail);
+
         cvttss2si(src, dest);
         convertInt32ToFloat32(dest, ScratchFloatReg);
         ucomiss(src, ScratchFloatReg);
         j(Assembler::Parity, fail);
         j(Assembler::NotEqual, fail);
-
-        // Check for -0
-        if (negativeZeroCheck) {
-            Label notZero;
-            branchTest32(Assembler::NonZero, dest, dest, &notZero);
-
-            if (Assembler::HasSSE41()) {
-                ptest(src, src);
-                j(Assembler::NonZero, fail);
-            } else {
-                // bit 0 = sign of low float
-                // bits 1 to 3 = signs of higher floats
-                movmskps(src, dest);
-                andl(Imm32(1), dest);
-                j(Assembler::NonZero, fail);
-            }
-
-            bind(&notZero);
-        }
     }
 
     void clampIntToUint8(Register reg) {
         Label inRange;
         branchTest32(Assembler::Zero, reg, Imm32(0xffffff00), &inRange);
         {
             sarl(Imm32(31), reg);
             notl(reg);
--- a/js/src/jit/x64/Bailouts-x64.cpp
+++ b/js/src/jit/x64/Bailouts-x64.cpp
@@ -42,32 +42,32 @@ class BailoutStack
 } // namespace js
 
 #if defined(_WIN32)
 # pragma pack(pop)
 #endif
 
 IonBailoutIterator::IonBailoutIterator(const JitActivationIterator &activations,
                                        BailoutStack *bailout)
-  : IonFrameIterator(activations),
+  : JitFrameIterator(activations),
     machine_(bailout->machineState())
 {
     uint8_t *sp = bailout->parentStackPointer();
     uint8_t *fp = sp + bailout->frameSize();
 
     current_ = fp;
     type_ = JitFrame_IonJS;
     topFrameSize_ = current_ - sp;
     topIonScript_ = script()->ionScript();
     snapshotOffset_ = bailout->snapshotOffset();
 }
 
 IonBailoutIterator::IonBailoutIterator(const JitActivationIterator &activations,
                                        InvalidationBailoutStack *bailout)
-  : IonFrameIterator(activations),
+  : JitFrameIterator(activations),
     machine_(bailout->machine())
 {
     returnAddressToFp_ = bailout->osiPointReturnAddress();
     topIonScript_ = bailout->ionScript();
     const OsiIndex *osiIndex = topIonScript_->getOsiIndex(returnAddressToFp_);
 
     current_ = (uint8_t*) bailout->fp();
     type_ = JitFrame_IonJS;
--- a/js/src/jit/x64/Lowering-x64.h
+++ b/js/src/jit/x64/Lowering-x64.h
@@ -30,16 +30,18 @@ class LIRGeneratorX64 : public LIRGenera
 
     // x86 has constraints on what registers can be formatted for 1-byte
     // stores and loads; on x64 all registers are okay.
     LAllocation useByteOpRegister(MDefinition *mir);
     LAllocation useByteOpRegisterOrNonDoubleConstant(MDefinition *mir);
 
     LDefinition tempToUnbox();
 
+    bool needTempForPostBarrier() { return false; }
+
     // x64 has a scratch register, so no need for another temp for dispatch
     // ICs.
     LDefinition tempForDispatchCache(MIRType outputType = MIRType_None) {
         return LDefinition::BogusTemp();
     }
 
   public:
     bool visitBox(MBox *box);
--- a/js/src/jit/x64/MacroAssembler-x64.cpp
+++ b/js/src/jit/x64/MacroAssembler-x64.cpp
@@ -362,23 +362,35 @@ MacroAssemblerX64::handleFailureWithHand
     // If we are bailing out to baseline to handle an exception, jump to
     // the bailout tail stub.
     bind(&bailout);
     loadPtr(Address(esp, offsetof(ResumeFromException, bailoutInfo)), r9);
     mov(ImmWord(BAILOUT_RETURN_OK), rax);
     jmp(Operand(rsp, offsetof(ResumeFromException, target)));
 }
 
-Assembler::Condition
-MacroAssemblerX64::testNegativeZero(const FloatRegister &reg, const Register &scratch)
+#ifdef JSGC_GENERATIONAL
+
+void
+MacroAssemblerX64::branchPtrInNurseryRange(Register ptr, Register temp, Label *label)
 {
-    movq(reg, scratch);
-    cmpq(scratch, Imm32(1));
-    return Overflow;
+    JS_ASSERT(ptr != temp);
+    JS_ASSERT(ptr != ScratchReg);
+
+    const Nursery &nursery = GetIonContext()->runtime->gcNursery();
+    movePtr(ImmWord(-ptrdiff_t(nursery.start())), ScratchReg);
+    addPtr(ptr, ScratchReg);
+    branchPtr(Assembler::Below, ScratchReg, Imm32(Nursery::NurserySize), label);
 }
 
-Assembler::Condition
-MacroAssemblerX64::testNegativeZeroFloat32(const FloatRegister &reg, const Register &scratch)
+void
+MacroAssemblerX64::branchValueIsNurseryObject(ValueOperand value, Register temp, Label *label)
 {
-    movd(reg, scratch);
-    cmpl(scratch, Imm32(1));
-    return Overflow;
+    // 'Value' representing the start of the nursery tagged as a JSObject
+    const Nursery &nursery = GetIonContext()->runtime->gcNursery();
+    Value start = ObjectValue(*reinterpret_cast<JSObject *>(nursery.start()));
+
+    movePtr(ImmWord(-ptrdiff_t(start.asRawBits())), ScratchReg);
+    addPtr(value.valueReg(), ScratchReg);
+    branchPtr(Assembler::Below, ScratchReg, Imm32(Nursery::NurserySize), label);
 }
+
+#endif
--- a/js/src/jit/x64/MacroAssembler-x64.h
+++ b/js/src/jit/x64/MacroAssembler-x64.h
@@ -502,19 +502,16 @@ class MacroAssemblerX64 : public MacroAs
 
     template <typename T1, typename T2>
     void cmpPtrSet(Assembler::Condition cond, T1 lhs, T2 rhs, const Register &dest)
     {
         cmpPtr(lhs, rhs);
         emitSet(cond, dest);
     }
 
-    Condition testNegativeZero(const FloatRegister &reg, const Register &scratch);
-    Condition testNegativeZeroFloat32(const FloatRegister &reg, const Register &scratch);
-
     /////////////////////////////////////////////////////////////////
     // Common interface.
     /////////////////////////////////////////////////////////////////
     void reserveStack(uint32_t amount) {
         if (amount)
             subq(Imm32(amount), StackPointer);
         framePushed_ += amount;
     }
@@ -1320,16 +1317,20 @@ class MacroAssemblerX64 : public MacroAs
         uint8_t *target = globalData + globalDataOffset;
         ((int32_t *)nextInsn)[-1] = target - nextInsn;
     }
     void memIntToValue(Address Source, Address Dest) {
         load32(Source, ScratchReg);
         storeValue(JSVAL_TYPE_INT32, ScratchReg, Dest);
     }
 
+#ifdef JSGC_GENERATIONAL
+    void branchPtrInNurseryRange(Register ptr, Register temp, Label *label);
+    void branchValueIsNurseryObject(ValueOperand value, Register temp, Label *label);
+#endif
 };
 
 typedef MacroAssemblerX64 MacroAssemblerSpecific;
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_x64_MacroAssembler_x64_h */
--- a/js/src/jit/x86/Bailouts-x86.cpp
+++ b/js/src/jit/x86/Bailouts-x86.cpp
@@ -62,17 +62,17 @@ class BailoutStack
 } // namespace js
 
 #if defined(_WIN32)
 # pragma pack(pop)
 #endif
 
 IonBailoutIterator::IonBailoutIterator(const JitActivationIterator &activations,
                                        BailoutStack *bailout)
-  : IonFrameIterator(activations),
+  : JitFrameIterator(activations),
     machine_(bailout->machine())
 {
     uint8_t *sp = bailout->parentStackPointer();
     uint8_t *fp = sp + bailout->frameSize();
 
     current_ = fp;
     type_ = JitFrame_IonJS;
     topFrameSize_ = current_ - sp;
@@ -97,17 +97,17 @@ IonBailoutIterator::IonBailoutIterator(c
     uint32_t bailoutId = ((tableOffset - tableStart) / BAILOUT_TABLE_ENTRY_SIZE) - 1;
     JS_ASSERT(bailoutId < BAILOUT_TABLE_SIZE);
 
     snapshotOffset_ = topIonScript_->bailoutToSnapshot(bailoutId);
 }
 
 IonBailoutIterator::IonBailoutIterator(const JitActivationIterator &activations,
                                        InvalidationBailoutStack *bailout)
-  : IonFrameIterator(activations),
+  : JitFrameIterator(activations),
     machine_(bailout->machine())
 {
     returnAddressToFp_ = bailout->osiPointReturnAddress();
     topIonScript_ = bailout->ionScript();
     const OsiIndex *osiIndex = topIonScript_->getOsiIndex(returnAddressToFp_);
 
     current_ = (uint8_t*) bailout->fp();
     type_ = JitFrame_IonJS;
--- a/js/src/jit/x86/Lowering-x86.h
+++ b/js/src/jit/x86/Lowering-x86.h
@@ -33,16 +33,18 @@ class LIRGeneratorX86 : public LIRGenera
     // give us one of {al,bl,cl,dl}. For now, just useFixed(al).
     LAllocation useByteOpRegister(MDefinition *mir);
     LAllocation useByteOpRegisterOrNonDoubleConstant(MDefinition *mir);
 
     inline LDefinition tempToUnbox() {
         return LDefinition::BogusTemp();
     }
 
+    bool needTempForPostBarrier() { return true; }
+
     LDefinition tempForDispatchCache(MIRType outputType = MIRType_None);
 
     void lowerUntypedPhiInput(MPhi *phi, uint32_t inputPosition, LBlock *block, size_t lirIndex);
     bool defineUntypedPhi(MPhi *phi, size_t lirIndex);
 
   public:
     bool visitBox(MBox *box);
     bool visitUnbox(MUnbox *unbox);
--- a/js/src/jit/x86/MacroAssembler-x86.cpp
+++ b/js/src/jit/x86/MacroAssembler-x86.cpp
@@ -382,48 +382,34 @@ MacroAssemblerX86::branchTestValue(Condi
         JS_ASSERT(cond == NotEqual);
         j(NotEqual, label);
 
         cmpl(value.typeReg(), Imm32(jv.s.tag));
         j(NotEqual, label);
     }
 }
 
-Assembler::Condition
-MacroAssemblerX86::testNegativeZero(const FloatRegister &reg, const Register &scratch)
-{
-    // Determines whether the single double contained in the XMM register reg
-    // is equal to double-precision -0.
-
-    Label nonZero;
-
-    // Compare to zero. Lets through {0, -0}.
-    xorpd(ScratchFloatReg, ScratchFloatReg);
-
-    // If reg is non-zero, jump to nonZero.
-    // Sets ZF=0 and PF=0.
-    branchDouble(DoubleNotEqual, reg, ScratchFloatReg, &nonZero);
+#ifdef JSGC_GENERATIONAL
 
-    // Input register is either zero or negative zero. Retrieve sign of input.
-    movmskpd(reg, scratch);
-
-    // If reg is 1 or 3, input is negative zero.
-    // If reg is 0 or 2, input is a normal zero.
-    // So the following test will set PF=1 for negative zero.
-    orl(Imm32(2), scratch);
+void
+MacroAssemblerX86::branchPtrInNurseryRange(Register ptr, Register temp, Label *label)
+{
+    JS_ASSERT(ptr != temp);
+    JS_ASSERT(temp != InvalidReg);  // A temp register is required for x86.
 
-    bind(&nonZero);
-
-    // Here we need to be able to test if the input is a negative zero.
-    // - branchDouble joins here for non-zero values in which case it sets
-    //   ZF=0 and PF=0. In that case the test should fail.
-    // - orl sets PF=1 on negative zero and PF=0 otherwise
-    // => So testing PF=1 will return if input is negative zero or not.
-    return Parity;
+    const Nursery &nursery = GetIonContext()->runtime->gcNursery();
+    movePtr(ImmWord(-ptrdiff_t(nursery.start())), temp);
+    addPtr(ptr, temp);
+    branchPtr(Assembler::Below, temp, Imm32(Nursery::NurserySize), label);
 }
 
-Assembler::Condition
-MacroAssemblerX86::testNegativeZeroFloat32(const FloatRegister &reg, const Register &scratch)
+void
+MacroAssemblerX86::branchValueIsNurseryObject(ValueOperand value, Register temp, Label *label)
 {
-    movd(reg, scratch);
-    cmpl(scratch, Imm32(1));
-    return Overflow;
+    Label done;
+
+    branchTestObject(Assembler::NotEqual, value, &done);
+    branchPtrInNurseryRange(value.payloadReg(), temp, label);
+
+    bind(&done);
 }
+
+#endif
--- a/js/src/jit/x86/MacroAssembler-x86.h
+++ b/js/src/jit/x86/MacroAssembler-x86.h
@@ -524,19 +524,16 @@ class MacroAssemblerX86 : public MacroAs
 
     template <typename T1, typename T2>
     void cmpPtrSet(Assembler::Condition cond, T1 lhs, T2 rhs, const Register &dest)
     {
         cmpPtr(lhs, rhs);
         emitSet(cond, dest);
     }
 
-    Condition testNegativeZero(const FloatRegister &reg, const Register &scratch);
-    Condition testNegativeZeroFloat32(const FloatRegister &reg, const Register &scratch);
-
     /////////////////////////////////////////////////////////////////
     // Common interface.
     /////////////////////////////////////////////////////////////////
     void reserveStack(uint32_t amount) {
         if (amount)
             subl(Imm32(amount), StackPointer);
         framePushed_ += amount;
     }
@@ -1109,16 +1106,21 @@ class MacroAssemblerX86 : public MacroAs
         call(target);
     }
 
     // Save an exit frame to the thread data of the current thread, given a
     // register that holds a PerThreadData *.
     void linkParallelExitFrame(const Register &pt) {
         movl(StackPointer, Operand(pt, offsetof(PerThreadData, ionTop)));
     }
+
+#ifdef JSGC_GENERATIONAL
+    void branchPtrInNurseryRange(Register ptr, Register temp, Label *label);
+    void branchValueIsNurseryObject(ValueOperand value, Register temp, Label *label);
+#endif
 };
 
 typedef MacroAssemblerX86 MacroAssemblerSpecific;
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_x86_MacroAssembler_x86_h */
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -28,17 +28,17 @@
 
 #include "builtin/Eval.h"
 #include "builtin/Object.h"
 #include "frontend/BytecodeCompiler.h"
 #include "frontend/TokenStream.h"
 #include "gc/Marking.h"
 #ifdef JS_ION
 #include "jit/Ion.h"
-#include "jit/IonFrameIterator.h"
+#include "jit/JitFrameIterator.h"
 #endif
 #include "vm/Interpreter.h"
 #include "vm/Shape.h"
 #include "vm/StringBuffer.h"
 #include "vm/WrapperObject.h"
 #include "vm/Xdr.h"
 
 #include "jsscriptinlines.h"
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -539,18 +539,25 @@ bool IsConstructor(const Value &v);
  * Most functions do not have these extensions, but enough do that efficient
  * storage is required (no malloc'ed reserved slots).
  */
 class FunctionExtended : public JSFunction
 {
   public:
     static const unsigned NUM_EXTENDED_SLOTS = 2;
 
+    /* Arrow functions store their lexical |this| in the first extended slot. */
+    static const unsigned ARROW_THIS_SLOT = 0;
+
+    static inline size_t offsetOfExtendedSlot(unsigned which) {
+        MOZ_ASSERT(which < NUM_EXTENDED_SLOTS);
+        return offsetof(FunctionExtended, extendedSlots) + which * sizeof(HeapValue);
+    }
     static inline size_t offsetOfArrowThisSlot() {
-        return offsetof(FunctionExtended, extendedSlots) + 0 * sizeof(HeapValue);
+        return offsetOfExtendedSlot(ARROW_THIS_SLOT);
     }
 
   private:
     friend class JSFunction;
 
     /* Reserved slots available for storage by particular native functions. */
     HeapValue extendedSlots[NUM_EXTENDED_SLOTS];
 };
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -333,21 +333,21 @@ FrameIter::unaliasedForEachActual(JSCont
       case DONE:
       case ASMJS:
         break;
       case INTERP:
         interpFrame()->unaliasedForEachActual(op);
         return;
       case JIT:
 #ifdef JS_ION
-        if (data_.ionFrames_.isIonJS()) {
+        if (data_.jitFrames_.isIonJS()) {
             ionInlineFrames_.unaliasedForEachActual(cx, op, jit::ReadFrame_Actuals);
         } else {
-            JS_ASSERT(data_.ionFrames_.isBaselineJS());
-            data_.ionFrames_.unaliasedForEachActual(op, jit::ReadFrame_Actuals);
+            JS_ASSERT(data_.jitFrames_.isBaselineJS());
+            data_.jitFrames_.unaliasedForEachActual(op, jit::ReadFrame_Actuals);
         }
         return;
 #else
         break;
 #endif
     }
     MOZ_ASSUME_UNREACHABLE("Unexpected state");
 }
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -13,17 +13,17 @@
 #include "gc/Marking.h"
 #ifdef JS_ION
 #include "jit/AsmJSModule.h"
 #include "jit/BaselineFrame.h"
 #include "jit/JitCompartment.h"
 #endif
 #include "vm/Opcodes.h"
 
-#include "jit/IonFrameIterator-inl.h"
+#include "jit/JitFrameIterator-inl.h"
 #include "vm/Interpreter-inl.h"
 #include "vm/Probes-inl.h"
 #include "vm/ScopeObject-inl.h"
 
 using namespace js;
 
 using mozilla::PodCopy;
 
@@ -563,25 +563,25 @@ FrameIter::settleOnActivation()
                     ++data_.activations_;
                     continue;
                 }
             }
         }
 
 #ifdef JS_ION
         if (activation->isJit()) {
-            data_.ionFrames_ = jit::IonFrameIterator(data_.activations_);
+            data_.jitFrames_ = jit::JitFrameIterator(data_.activations_);
 
             // Stop at the first scripted frame.
-            while (!data_.ionFrames_.isScripted() && !data_.ionFrames_.done())
-                ++data_.ionFrames_;
+            while (!data_.jitFrames_.isScripted() && !data_.jitFrames_.done())
+                ++data_.jitFrames_;
 
             // It's possible to have an JitActivation with no scripted frames,
             // for instance if we hit an over-recursion during bailout.
-            if (data_.ionFrames_.done()) {
+            if (data_.jitFrames_.done()) {
                 ++data_.activations_;
                 continue;
             }
 
             nextJitFrame();
             data_.state_ = JIT;
             return;
         }
@@ -627,102 +627,102 @@ FrameIter::Data::Data(JSContext *cx, Sav
   : cx_(cx),
     savedOption_(savedOption),
     contextOption_(contextOption),
     principals_(principals),
     pc_(nullptr),
     interpFrames_(nullptr),
     activations_(cx->runtime())
 #ifdef JS_ION
-  , ionFrames_((uint8_t *)nullptr, SequentialExecution)
+  , jitFrames_((uint8_t *)nullptr, SequentialExecution)
 #endif
 {
 }
 
 FrameIter::Data::Data(const FrameIter::Data &other)
   : cx_(other.cx_),
     savedOption_(other.savedOption_),
     contextOption_(other.contextOption_),
     principals_(other.principals_),
     state_(other.state_),
     pc_(other.pc_),
     interpFrames_(other.interpFrames_),
     activations_(other.activations_)
 #ifdef JS_ION
-  , ionFrames_(other.ionFrames_)
+  , jitFrames_(other.jitFrames_)
 #endif
 {
 }
 
 FrameIter::FrameIter(JSContext *cx, SavedOption savedOption)
   : data_(cx, savedOption, CURRENT_CONTEXT, nullptr)
 #ifdef JS_ION
-  , ionInlineFrames_(cx, (js::jit::IonFrameIterator*) nullptr)
+  , ionInlineFrames_(cx, (js::jit::JitFrameIterator*) nullptr)
 #endif
 {
     settleOnActivation();
 }
 
 FrameIter::FrameIter(JSContext *cx, ContextOption contextOption,
                      SavedOption savedOption, JSPrincipals *principals)
   : data_(cx, savedOption, contextOption, principals)
 #ifdef JS_ION
-  , ionInlineFrames_(cx, (js::jit::IonFrameIterator*) nullptr)
+  , ionInlineFrames_(cx, (js::jit::JitFrameIterator*) nullptr)
 #endif
 {
     settleOnActivation();
 }
 
 FrameIter::FrameIter(const FrameIter &other)
   : data_(other.data_)
 #ifdef JS_ION
   , ionInlineFrames_(other.data_.cx_,
-                     data_.ionFrames_.isScripted() ? &other.ionInlineFrames_ : nullptr)
+                     data_.jitFrames_.isScripted() ? &other.ionInlineFrames_ : nullptr)
 #endif
 {
 }
 
 FrameIter::FrameIter(const Data &data)
   : data_(data)
 #ifdef JS_ION
-  , ionInlineFrames_(data.cx_, data_.ionFrames_.isIonJS() ? &data_.ionFrames_ : nullptr)
+  , ionInlineFrames_(data.cx_, data_.jitFrames_.isIonJS() ? &data_.jitFrames_ : nullptr)
 #endif
 {
     JS_ASSERT(data.cx_);
 }
 
 #ifdef JS_ION
 void
 FrameIter::nextJitFrame()
 {
-    if (data_.ionFrames_.isIonJS()) {
-        ionInlineFrames_.resetOn(&data_.ionFrames_);
+    if (data_.jitFrames_.isIonJS()) {
+        ionInlineFrames_.resetOn(&data_.jitFrames_);
         data_.pc_ = ionInlineFrames_.pc();
     } else {
-        JS_ASSERT(data_.ionFrames_.isBaselineJS());
-        data_.ionFrames_.baselineScriptAndPc(nullptr, &data_.pc_);
+        JS_ASSERT(data_.jitFrames_.isBaselineJS());
+        data_.jitFrames_.baselineScriptAndPc(nullptr, &data_.pc_);
     }
 }
 
 void
 FrameIter::popJitFrame()
 {
     JS_ASSERT(data_.state_ == JIT);
 
-    if (data_.ionFrames_.isIonJS() && ionInlineFrames_.more()) {
+    if (data_.jitFrames_.isIonJS() && ionInlineFrames_.more()) {
         ++ionInlineFrames_;
         data_.pc_ = ionInlineFrames_.pc();
         return;
     }
 
-    ++data_.ionFrames_;
-    while (!data_.ionFrames_.done() && !data_.ionFrames_.isScripted())
-        ++data_.ionFrames_;
+    ++data_.jitFrames_;
+    while (!data_.jitFrames_.done() && !data_.jitFrames_.isScripted())
+        ++data_.jitFrames_;
 
-    if (!data_.ionFrames_.done()) {
+    if (!data_.jitFrames_.done()) {
         nextJitFrame();
         return;
     }
 
     popActivation();
 }
 #endif
 
@@ -786,17 +786,17 @@ FrameIter::Data *
 FrameIter::copyData() const
 {
 #ifdef JS_ION
     /*
      * This doesn't work for optimized Ion frames since ionInlineFrames_ is
      * not copied.
      */
     JS_ASSERT(data_.state_ != ASMJS);
-    JS_ASSERT(data_.ionFrames_.type() != jit::JitFrame_IonJS);
+    JS_ASSERT(data_.jitFrames_.type() != jit::JitFrame_IonJS);
 #endif
     return data_.cx_->new_<Data>(data_);
 }
 
 AbstractFramePtr
 FrameIter::copyDataAsAbstractFramePtr() const
 {
     AbstractFramePtr frame;
@@ -824,19 +824,19 @@ FrameIter::isFunctionFrame() const
 {
     switch (data_.state_) {
       case DONE:
         break;
       case INTERP:
         return interpFrame()->isFunctionFrame();
       case JIT:
 #ifdef JS_ION
-        JS_ASSERT(data_.ionFrames_.isScripted());
-        if (data_.ionFrames_.isBaselineJS())
-            return data_.ionFrames_.isFunctionFrame();
+        JS_ASSERT(data_.jitFrames_.isScripted());
+        if (data_.jitFrames_.isBaselineJS())
+            return data_.jitFrames_.isFunctionFrame();
         return ionInlineFrames_.isFunctionFrame();
 #else
         break;
 #endif
       case ASMJS:
         return true;
     }
     MOZ_ASSUME_UNREACHABLE("Unexpected state");
@@ -847,18 +847,18 @@ FrameIter::isGlobalFrame() const
 {
     switch (data_.state_) {
       case DONE:
         break;
       case INTERP:
         return interpFrame()->isGlobalFrame();
       case JIT:
 #ifdef JS_ION
-        if (data_.ionFrames_.isBaselineJS())
-            return data_.ionFrames_.baselineFrame()->isGlobalFrame();
+        if (data_.jitFrames_.isBaselineJS())
+            return data_.jitFrames_.baselineFrame()->isGlobalFrame();
         JS_ASSERT(!script()->isForEval());
         return !script()->functionNonDelazifying();
 #else
         break;
 #endif
       case ASMJS:
         return false;
     }
@@ -870,18 +870,18 @@ FrameIter::isEvalFrame() const
 {
     switch (data_.state_) {
       case DONE:
         break;
       case INTERP:
         return interpFrame()->isEvalFrame();
       case JIT:
 #ifdef JS_ION
-        if (data_.ionFrames_.isBaselineJS())
-            return data_.ionFrames_.baselineFrame()->isEvalFrame();
+        if (data_.jitFrames_.isBaselineJS())
+            return data_.jitFrames_.baselineFrame()->isEvalFrame();
         JS_ASSERT(!script()->isForEval());
         return false;
 #else
         break;
 #endif
       case ASMJS:
         return false;
     }
@@ -1035,20 +1035,20 @@ bool
 FrameIter::isConstructing() const
 {
     switch (data_.state_) {
       case DONE:
       case ASMJS:
         break;
       case JIT:
 #ifdef JS_ION
-        if (data_.ionFrames_.isIonJS())
+        if (data_.jitFrames_.isIonJS())
             return ionInlineFrames_.isConstructing();
-        JS_ASSERT(data_.ionFrames_.isBaselineJS());
-        return data_.ionFrames_.isConstructing();
+        JS_ASSERT(data_.jitFrames_.isBaselineJS());
+        return data_.jitFrames_.isConstructing();
 #else
         break;
 #endif
       case INTERP:
         return interpFrame()->isConstructing();
     }
     MOZ_ASSUME_UNREACHABLE("Unexpected state");
 }
@@ -1057,18 +1057,18 @@ AbstractFramePtr
 FrameIter::abstractFramePtr() const
 {
     switch (data_.state_) {
       case DONE:
       case ASMJS:
         break;
       case JIT:
 #ifdef JS_ION
-        if (data_.ionFrames_.isBaselineJS())
-            return data_.ionFrames_.baselineFrame();
+        if (data_.jitFrames_.isBaselineJS())
+            return data_.jitFrames_.baselineFrame();
 #endif
         break;
       case INTERP:
         JS_ASSERT(interpFrame());
         return AbstractFramePtr(interpFrame());
     }
     MOZ_ASSUME_UNREACHABLE("Unexpected state");
 }
@@ -1091,34 +1091,34 @@ FrameIter::updatePcQuadratic()
 
         // Update the pc.
         JS_ASSERT(data_.interpFrames_.frame() == frame);
         data_.pc_ = data_.interpFrames_.pc();
         return;
       }
       case JIT:
 #ifdef JS_ION
-        if (data_.ionFrames_.isBaselineJS()) {
-            jit::BaselineFrame *frame = data_.ionFrames_.baselineFrame();
+        if (data_.jitFrames_.isBaselineJS()) {
+            jit::BaselineFrame *frame = data_.jitFrames_.baselineFrame();
             jit::JitActivation *activation = data_.activations_.activation()->asJit();
 
             // ActivationIterator::ionTop_ may be invalid, so create a new
             // activation iterator.
             data_.activations_ = ActivationIterator(data_.cx_->runtime());
             while (data_.activations_.activation() != activation)
                 ++data_.activations_;
 
             // Look for the current frame.
-            data_.ionFrames_ = jit::IonFrameIterator(data_.activations_);
-            while (!data_.ionFrames_.isBaselineJS() || data_.ionFrames_.baselineFrame() != frame)
-                ++data_.ionFrames_;
+            data_.jitFrames_ = jit::JitFrameIterator(data_.activations_);
+            while (!data_.jitFrames_.isBaselineJS() || data_.jitFrames_.baselineFrame() != frame)
+                ++data_.jitFrames_;
 
             // Update the pc.
-            JS_ASSERT(data_.ionFrames_.baselineFrame() == frame);
-            data_.ionFrames_.baselineScriptAndPc(nullptr, &data_.pc_);
+            JS_ASSERT(data_.jitFrames_.baselineFrame() == frame);
+            data_.jitFrames_.baselineScriptAndPc(nullptr, &data_.pc_);
             return;
         }
 #endif
         break;
     }
     MOZ_ASSUME_UNREACHABLE("Unexpected state");
 }
 
@@ -1129,19 +1129,19 @@ FrameIter::callee() const
       case DONE:
       case ASMJS:
         break;
       case INTERP:
         JS_ASSERT(isFunctionFrame());
         return &interpFrame()->callee();
       case JIT:
 #ifdef JS_ION
-        if (data_.ionFrames_.isBaselineJS())
-            return data_.ionFrames_.callee();
-        JS_ASSERT(data_.ionFrames_.isIonJS());
+        if (data_.jitFrames_.isBaselineJS())
+            return data_.jitFrames_.callee();
+        JS_ASSERT(data_.jitFrames_.isIonJS());
         return ionInlineFrames_.callee();
 #else
         break;
 #endif
     }
     MOZ_ASSUME_UNREACHABLE("Unexpected state");
 }
 
@@ -1172,21 +1172,21 @@ FrameIter::numActualArgs() const
       case DONE:
       case ASMJS:
         break;
       case INTERP:
         JS_ASSERT(isFunctionFrame());
         return interpFrame()->numActualArgs();
       case JIT:
 #ifdef JS_ION
-        if (data_.ionFrames_.isIonJS())
+        if (data_.jitFrames_.isIonJS())
             return ionInlineFrames_.numActualArgs();
 
-        JS_ASSERT(data_.ionFrames_.isBaselineJS());
-        return data_.ionFrames_.numActualArgs();
+        JS_ASSERT(data_.jitFrames_.isBaselineJS());
+        return data_.jitFrames_.numActualArgs();
 #else
         break;
 #endif
     }
     MOZ_ASSUME_UNREACHABLE("Unexpected state");
 }
 
 unsigned
@@ -1201,37 +1201,37 @@ FrameIter::unaliasedActual(unsigned i, M
     switch (data_.state_) {
       case DONE:
       case ASMJS:
         break;
       case INTERP:
         return interpFrame()->unaliasedActual(i, checkAliasing);
       case JIT:
 #ifdef JS_ION
-        JS_ASSERT(data_.ionFrames_.isBaselineJS());
-        return data_.ionFrames_.baselineFrame()->unaliasedActual(i, checkAliasing);
+        JS_ASSERT(data_.jitFrames_.isBaselineJS());
+        return data_.jitFrames_.baselineFrame()->unaliasedActual(i, checkAliasing);
 #else
         break;
 #endif
     }
     MOZ_ASSUME_UNREACHABLE("Unexpected state");
 }
 
 JSObject *
 FrameIter::scopeChain() const
 {
     switch (data_.state_) {
       case DONE:
       case ASMJS:
         break;
       case JIT:
 #ifdef JS_ION
-        if (data_.ionFrames_.isIonJS())
+        if (data_.jitFrames_.isIonJS())
             return ionInlineFrames_.scopeChain();
-        return data_.ionFrames_.baselineFrame()->scopeChain();
+        return data_.jitFrames_.baselineFrame()->scopeChain();
 #else
         break;
 #endif
       case INTERP:
         return interpFrame()->scopeChain();
     }
     MOZ_ASSUME_UNREACHABLE("Unexpected state");
 }
@@ -1253,18 +1253,18 @@ FrameIter::hasArgsObj() const
     switch (data_.state_) {
       case DONE:
       case ASMJS:
         break;
       case INTERP:
         return interpFrame()->hasArgsObj();
       case JIT:
 #ifdef JS_ION
-        JS_ASSERT(data_.ionFrames_.isBaselineJS());
-        return data_.ionFrames_.baselineFrame()->hasArgsObj();
+        JS_ASSERT(data_.jitFrames_.isBaselineJS());
+        return data_.jitFrames_.baselineFrame()->hasArgsObj();
 #else
         break;
 #endif
     }
     MOZ_ASSUME_UNREACHABLE("Unexpected state");
 }
 
 ArgumentsObject &
@@ -1273,18 +1273,18 @@ FrameIter::argsObj() const
     JS_ASSERT(hasArgsObj());
 
     switch (data_.state_) {
       case DONE:
       case ASMJS:
         break;
       case JIT:
 #ifdef JS_ION
-        JS_ASSERT(data_.ionFrames_.isBaselineJS());
-        return data_.ionFrames_.baselineFrame()->argsObj();
+        JS_ASSERT(data_.jitFrames_.isBaselineJS());
+        return data_.jitFrames_.baselineFrame()->argsObj();
 #else
         break;
 #endif
       case INTERP:
         return interpFrame()->argsObj();
     }
     MOZ_ASSUME_UNREACHABLE("Unexpected state");
 }
@@ -1304,19 +1304,19 @@ Value
 FrameIter::thisv() const
 {
     switch (data_.state_) {
       case DONE:
       case ASMJS:
         break;
       case JIT:
 #ifdef JS_ION
-        if (data_.ionFrames_.isIonJS())
+        if (data_.jitFrames_.isIonJS())
             return ObjectValue(*ionInlineFrames_.thisObject());
-        return data_.ionFrames_.baselineFrame()->thisValue();
+        return data_.jitFrames_.baselineFrame()->thisValue();
 #else
         break;
 #endif
       case INTERP:
         return interpFrame()->thisValue();
     }
     MOZ_ASSUME_UNREACHABLE("Unexpected state");
 }
@@ -1325,18 +1325,18 @@ Value
 FrameIter::returnValue() const
 {
     switch (data_.state_) {
       case DONE:
       case ASMJS:
         break;
       case JIT:
 #ifdef JS_ION
-        if (data_.ionFrames_.isBaselineJS())
-            return data_.ionFrames_.baselineFrame()->returnValue();
+        if (data_.jitFrames_.isBaselineJS())
+            return data_.jitFrames_.baselineFrame()->returnValue();
 #endif
         break;
       case INTERP:
         return interpFrame()->returnValue();
     }
     MOZ_ASSUME_UNREACHABLE("Unexpected state");
 }
 
@@ -1344,18 +1344,18 @@ void
 FrameIter::setReturnValue(const Value &v)
 {
     switch (data_.state_) {
       case DONE:
       case ASMJS:
         break;
       case JIT:
 #ifdef JS_ION
-        if (data_.ionFrames_.isBaselineJS()) {
-            data_.ionFrames_.baselineFrame()->setReturnValue(v);
+        if (data_.jitFrames_.isBaselineJS()) {
+            data_.jitFrames_.baselineFrame()->setReturnValue(v);
             return;
         }
 #endif
         break;
       case INTERP:
         interpFrame()->setReturnValue(v);
         return;
     }
@@ -1366,22 +1366,22 @@ size_t
 FrameIter::numFrameSlots() const
 {
     switch (data_.state_) {
       case DONE:
       case ASMJS:
         break;
       case JIT: {
 #ifdef JS_ION
-        if (data_.ionFrames_.isIonJS()) {
+        if (data_.jitFrames_.isIonJS()) {
             return ionInlineFrames_.snapshotIterator().numAllocations() -
                 ionInlineFrames_.script()->nfixed();
         }
-        jit::BaselineFrame *frame = data_.ionFrames_.baselineFrame();
-        return frame->numValueSlots() - data_.ionFrames_.script()->nfixed();
+        jit::BaselineFrame *frame = data_.jitFrames_.baselineFrame();
+        return frame->numValueSlots() - data_.jitFrames_.script()->nfixed();
 #else
         break;
 #endif
       }
       case INTERP:
         JS_ASSERT(data_.interpFrames_.sp() >= interpFrame()->base());
         return data_.interpFrames_.sp() - interpFrame()->base();
     }
@@ -1392,24 +1392,24 @@ Value
 FrameIter::frameSlotValue(size_t index) const
 {
     switch (data_.state_) {
       case DONE:
       case ASMJS:
         break;
       case JIT:
 #ifdef JS_ION
-        if (data_.ionFrames_.isIonJS()) {
+        if (data_.jitFrames_.isIonJS()) {
             jit::SnapshotIterator si(ionInlineFrames_.snapshotIterator());
             index += ionInlineFrames_.script()->nfixed();
             return si.maybeReadAllocByIndex(index);
         }
 
-        index += data_.ionFrames_.script()->nfixed();
-        return *data_.ionFrames_.baselineFrame()->valueSlot(index);
+        index += data_.jitFrames_.script()->nfixed();
+        return *data_.jitFrames_.baselineFrame()->valueSlot(index);
 #else
         break;
 #endif
       case INTERP:
           return interpFrame()->base()[index];
     }
     MOZ_ASSUME_UNREACHABLE("Unexpected state");
 }
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -7,17 +7,17 @@
 #ifndef vm_Stack_h
 #define vm_Stack_h
 
 #include "mozilla/MemoryReporting.h"
 
 #include "jsfun.h"
 #include "jsscript.h"
 
-#include "jit/IonFrameIterator.h"
+#include "jit/JitFrameIterator.h"
 #ifdef CHECK_OSIPOINT_REGISTERS
 #include "jit/Registers.h" // for RegisterDump
 #endif
 #include "js/OldDebugAPI.h"
 
 struct JSCompartment;
 struct JSGenerator;
 
@@ -1510,17 +1510,17 @@ class FrameIter
         State           state_;
 
         jsbytecode *    pc_;
 
         InterpreterFrameIterator interpFrames_;
         ActivationIterator activations_;
 
 #ifdef JS_ION
-        jit::IonFrameIterator ionFrames_;
+        jit::JitFrameIterator jitFrames_;
 #endif
 
         Data(JSContext *cx, SavedOption savedOption, ContextOption contextOption,
              JSPrincipals *principals);
         Data(const Data &other);
     };
 
     FrameIter(JSContext *cx, SavedOption = STOP_AT_SAVED);
@@ -1753,39 +1753,39 @@ class AllFramesIter : public ScriptFrame
 inline JSScript *
 FrameIter::script() const
 {
     JS_ASSERT(!done());
     if (data_.state_ == INTERP)
         return interpFrame()->script();
 #ifdef JS_ION
     JS_ASSERT(data_.state_ == JIT);
-    if (data_.ionFrames_.isIonJS())
+    if (data_.jitFrames_.isIonJS())
         return ionInlineFrames_.script();
-    return data_.ionFrames_.script();
+    return data_.jitFrames_.script();
 #else
     return nullptr;
 #endif
 }
 
 inline bool
 FrameIter::isIon() const
 {
 #ifdef JS_ION
-    return isJit() && data_.ionFrames_.isIonJS();
+    return isJit() && data_.jitFrames_.isIonJS();
 #else
     return false;
 #endif
 }
 
 inline bool
 FrameIter::isBaseline() const
 {
 #ifdef JS_ION
-    return isJit() && data_.ionFrames_.isBaselineJS();
+    return isJit() && data_.jitFrames_.isBaselineJS();
 #else
     return false;
 #endif
 }
 
 inline InterpreterFrame *
 FrameIter::interpFrame() const
 {
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -5093,16 +5093,46 @@ nsRect nsDisplayTransform::TransformRect
 
   float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
   return nsLayoutUtils::MatrixTransformRectOut
     (aUntransformedBounds,
      GetResultingTransformMatrix(aFrame, aOrigin, factor, aBoundsOverride),
      factor);
 }
 
+bool nsDisplayTransform::UntransformRect(const nsRect &aTransformedBounds,
+                                         const nsRect &aChildBounds,
+                                         const nsIFrame* aFrame,
+                                         const nsPoint &aOrigin,
+                                         nsRect *aOutRect)
+{
+  NS_PRECONDITION(aFrame, "Can't take the transform based on a null frame!");
+
+  float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
+
+  gfx3DMatrix transform = GetResultingTransformMatrix(aFrame, aOrigin, factor, nullptr);
+  if (transform.IsSingular()) {
+    return false;
+  }
+
+  gfxRect result(NSAppUnitsToFloatPixels(aTransformedBounds.x, factor),
+                 NSAppUnitsToFloatPixels(aTransformedBounds.y, factor),
+                 NSAppUnitsToFloatPixels(aTransformedBounds.width, factor),
+                 NSAppUnitsToFloatPixels(aTransformedBounds.height, factor));
+
+  gfxRect childGfxBounds(NSAppUnitsToFloatPixels(aChildBounds.x, factor),
+                         NSAppUnitsToFloatPixels(aChildBounds.y, factor),
+                         NSAppUnitsToFloatPixels(aChildBounds.width, factor),
+                         NSAppUnitsToFloatPixels(aChildBounds.height, factor));
+
+  result = transform.UntransformBounds(result, childGfxBounds);
+  *aOutRect = nsLayoutUtils::RoundGfxRectToAppRect(result, factor);
+  return true;
+}
+
 bool nsDisplayTransform::UntransformVisibleRect(nsDisplayListBuilder* aBuilder,
                                                 nsRect *aOutRect)
 {
   const gfx3DMatrix& matrix = GetTransform();
   if (matrix.IsSingular())
     return false;
 
   // GetTransform always operates in dev pixels.
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -3253,16 +3253,22 @@ public:
   static nsRect TransformRectOut(const nsRect &aUntransformedBounds, 
                                  const nsIFrame* aFrame,
                                  const nsPoint &aOrigin,
                                  const nsRect* aBoundsOverride = nullptr);
 
   /* UntransformRect is like TransformRect, except that it inverts the
    * transform.
    */
+  static bool UntransformRect(const nsRect &aTransformedBounds,
+                              const nsRect &aChildBounds,
+                              const nsIFrame* aFrame,
+                              const nsPoint &aOrigin,
+                              nsRect *aOutRect);
+
   bool UntransformVisibleRect(nsDisplayListBuilder* aBuilder,
                               nsRect* aOutRect);
 
   static gfxPoint3D GetDeltaToTransformOrigin(const nsIFrame* aFrame,
                                               float aAppUnitsPerPixel,
                                               const nsRect* aBoundsOverride);
 
   static gfxPoint3D GetDeltaToPerspectiveOrigin(const nsIFrame* aFrame,
--- a/layout/generic/StickyScrollContainer.cpp
+++ b/layout/generic/StickyScrollContainer.cpp
@@ -176,21 +176,16 @@ StickyScrollContainer::ComputeStickyLimi
     aFrame->Properties().Get(nsIFrame::ComputedOffsetProperty()));
   if (!computedOffsets) {
     // We haven't reflowed the scroll frame yet, so offsets haven't been
     // computed. Bail.
     return;
   }
 
   nsIFrame* scrolledFrame = mScrollFrame->GetScrolledFrame();
-  // FIXME (Bug 920688):  cbFrame isn't quite right if we're dealing
-  // with a block-in-inline split whose first part is a block.  We
-  // probably want the first in flow of the containing block of the
-  // first inline part.  (Or maybe those block-in-inline split pieces
-  // are never a containing block, and we're ok?)
   nsIFrame* cbFrame = aFrame->GetContainingBlock();
   NS_ASSERTION(cbFrame == scrolledFrame ||
     nsLayoutUtils::IsProperAncestorFrame(scrolledFrame, cbFrame),
     "Scroll frame should be an ancestor of the containing block");
 
   nsRect rect =
     nsLayoutUtils::GetAllInFlowRectsUnion(aFrame, aFrame->GetParent());
 
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -1924,32 +1924,26 @@ nsIFrame::BuildDisplayListForStackingCon
     const nsRect overflow = GetVisualOverflowRectRelativeToSelf();
     if (aBuilder->IsForPainting() &&
         nsDisplayTransform::ShouldPrerenderTransformedContent(aBuilder, this)) {
       dirtyRect = overflow;
     } else {
       if (overflow.IsEmpty() && !Preserves3DChildren()) {
         return;
       }
-      // Trying to back-transform arbitrary rects gives us really weird results. I believe 
-      // this is from points that lie beyond the vanishing point. As a workaround we transform
-      // the overflow rect into screen space and compare in that coordinate system.
-
-      // Transform the overflow rect into screen space.
+
       nsPoint offset = aBuilder->ToReferenceFrame(this);
-      nsRect trans = nsDisplayTransform::TransformRect(overflow + offset, this, offset);
       dirtyRect += offset;
-      if (dirtyRect.Intersects(trans)) {
-        // If they intersect, we take our whole overflow rect. We could instead take the intersection
-        // and then reverse transform it but I doubt this extra work is worthwhile.
-        dirtyRect = overflow;
+
+      nsRect untransformedDirtyRect;
+      if (nsDisplayTransform::UntransformRect(dirtyRect, overflow, this, offset, &untransformedDirtyRect)) {
+        dirtyRect = untransformedDirtyRect;
       } else {
-        if (!Preserves3DChildren()) {
-          return;
-        }
+        NS_WARNING("Unable to untransform dirty rect!");
+        // This should only happen if the transform is singular, in which case nothing is visible anyway
         dirtyRect.SetEmpty();
       }
     }
     inTransform = true;
   }
 
   bool useOpacity = HasVisualOpacity() && !nsSVGUtils::CanOptimizeOpacity(this);
   bool useBlendMode = disp->mMixBlendMode != NS_STYLE_BLEND_NORMAL;
--- a/layout/mathml/mathfont.properties
+++ b/layout/mathml/mathfont.properties
@@ -6,19 +6,19 @@
 #  Do not translate anything in this file
 
 # List of fonts that have corresponding properties files containing special
 # glyph tables for stretching MathML characters.  See the documentation at the
 # end of this file for details on the setup of the property file associated to
 # each font.  Do not include the Unicode table in this list.
 
 %ifdef XP_WIN
-font.mathfont-glyph-tables = MathJax_Main, STIXNonUnicode, STIXSizeOneSym, STIXSize1, Asana Math, Standard Symbols L, Symbol
+font.mathfont-glyph-tables = MathJax_Main, STIXNonUnicode, STIXSizeOneSym, Standard Symbols L, Symbol
 %else
-font.mathfont-glyph-tables = MathJax_Main, STIXNonUnicode, STIXSizeOneSym, STIXSize1, Asana Math, Standard Symbols L
+font.mathfont-glyph-tables = MathJax_Main, STIXNonUnicode, STIXSizeOneSym, Standard Symbols L
 %endif
 
 # The ordered list of fonts with which to attempt to stretch MathML
 # characters is controlled by setting pref("font.mathfont-family",
 # "CMSY10, CMEX10, ...") for example, or by setting the font-family list in
 # :-moz-math-stretchy in mathml.css.
 #
 # Preferred fonts for particular stretchy characters may be specified in
deleted file mode 100644
--- a/layout/mathml/mathfontAsanaMath.properties
+++ /dev/null
@@ -1,145 +0,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/.
-
-#  LOCALIZATION NOTE: FILE
-#  Do not translate anything in this file
-
-# This file contains the list of some stretchy MathML chars that
-# can be rendered with Asana Math font.
-
-#        [ T/L |  M  | B/R |  G  | size0 ... size{N-1} ]
-#        (*) not in the MathML operator dictionary
-
-\u0028 = \u239B\uFFFD\u239D\u239C\u0028\uDBFF\uDFF4\uDBFF\uDFF5\uDBFF\uDFF6 # (
-\u0029 = \u239E\uFFFD\u23A0\u239F\u0029\uDBFF\uDFF7\uDBFF\uDFF8\uDBFF\uDFF9 # )
-\u005B = \u23A1\uFFFD\u23A3\u23A2\u005B\uDBFF\uDFEE\uDBFF\uDFEF\uDBFF\uDFF0 # [
-\u005D = \u23A4\uFFFD\u23A6\u23A5\u005D\uDBFF\uDFF1\uDBFF\uDFF2\uDBFF\uDFF3 # ]
-\u007B = \u23A7\u23A8\u23A9\u23AA\u007B\uDBFF\uDFFA\uDBFF\uDFFB\uDBFF\uDFFC # {
-\u007C = \uFFFD\uFFFD\uFFFD\u007C\u007C\uDBFF\uDFD6\uDBFF\uDFD7\uDBFF\uDFD8\uDBFF\uDFD9 # |
-\u007D = \u23AB\u23AC\u23AD\u23AA\u007D\uDBFF\uDFFD\uDBFF\uDFFE\uDBFF\uDFFF # }
-\u2016 = \uFFFD\uFFFD\uFFFD\uDBFF\uDFD1\u2016\uDBFF\uDFCE\uDBFF\uDFCF\uDBFF\uDFD0\uDBFF\uDFD1 # DOUBLE VERTICAL LINE
-
-\u2044 = \uFFFD\uFFFD\uFFFD\uFFFD\u2044\uDBFF\uDFD2\uDBFF\uDFD3\uDBFF\uDFD4\uDBFF\uDFD5 # FRACTION SLASH
-# \u2045 = \uDBFF\uDFB6\uDBFF\uDF53\uDBFF\uDFB7\uDBFF\uDFBA\u2045\uDBFF\uDFBB\uDBFF\uDFBC\uDBFF\uDFBD # LEFT SQUARE BRACKET WITH QUILL (*)
-# \u2046 = \uDBFF\uDFB8\uDBFF\uDF54\uDBFF\uDFB9\uDBFF\uDF52\u2046\uDBFF\uDFBE\uDBFF\uDFBF\uDBFF\uDFC0 # RIGHT SQUARE BRACKET WITH QUILL (*)
-
-\u2191 = \u2191\uFFFD\uFFFD\uDBFF\uDEC6\u2191 # UPWARDS ARROW
-\u2193 = \uFFFD\uFFFD\u2193\uDBFF\uDEC6\u2193 # DOWNWARDS ARROW
-\u21D1 = \u21D1\uFFFD\uFFFD\uDBFF\uDEC7\u21D1 # UPWARDS DOUBLE ARROW
-\u21D3 = \uFFFD\uFFFD\u21D3\uDBFF\uDEC7\u21D3 # DOWNWARDS DOUBLE ARROW
-
-\u220F = \uFFFD\uFFFD\uFFFD\uFFFD\u220F\uDBFF\uDF9F\uDBFF\uDFA0\uDBFF\uDFA1 # N-ARY PRODUCT
-\u2210 = \uFFFD\uFFFD\uFFFD\uFFFD\u2210\uDBFF\uDFA2\uDBFF\uDFA3\uDBFF\uDFA4 # N-ARY COPRODUCT
-\u2211 = \uFFFD\uFFFD\uFFFD\uFFFD\u2211\uDBFF\uDF9C\uDBFF\uDF9D\uDBFF\uDF9E # summation N-ARY SUMMATION
-\u221A = \uDBFF\uDF6D\uFFFD\u23B7\u20D3\u221A\uDBFF\uDF6E\uDBFF\uDF6F\uDBFF\uDF70\uDBFF\uDF71 # SQUARE ROOT
-\u2223 = \uFFFD\uFFFD\uFFFD\u2223\u2223 # DIVIDES
-\u2225 = \uFFFD\uFFFD\uFFFD\u2225\u2225 # PARALLEL TO
-\u222B = \u2320\uFFFD\u2321\u23AE\u222B\uDBFF\uDF99\uDBFF\uDF9A\uDBFF\uDF9B # INTEGRAL
-\u222C = \uFFFD\uFFFD\uFFFD\uFFFD\u222C\uDBFF\uDF6A\uDBFF\uDF6B\uDBFF\uDF6C # DOUBLE INTEGRAL
-\u222D = \uFFFD\uFFFD\uFFFD\uFFFD\u222D\uDBFF\uDF67\uDBFF\uDF68\uDBFF\uDF69 # TRIPLE INTEGRAL
-\u222E = \uFFFD\uFFFD\uFFFD\uFFFD\u222E\uDBFF\uDF64\uDBFF\uDF65\uDBFF\uDF66 # CONTOUR INTEGRAL
-\u222F = \uFFFD\uFFFD\uFFFD\uFFFD\u222F\uDBFF\uDF61\uDBFF\uDF62\uDBFF\uDF63 # SURFACE INTEGRAL
-\u2230 = \uFFFD\uFFFD\uFFFD\uFFFD\u2230\uDBFF\uDF5E\uDBFF\uDF5F\uDBFF\uDF60 # VOLUME INTEGRAL
-\u2231 = \uFFFD\uFFFD\uFFFD\uFFFD\u2231\uDBFF\uDF5B\uDBFF\uDF5C\uDBFF\uDF5D # CLOCKWISE INTEGRAL
-\u2232 = \uFFFD\uFFFD\uFFFD\uFFFD\u2232\uDBFF\uDF58\uDBFF\uDF59\uDBFF\uDF5A # CLOCKWISE CONTOUR INTEGRAL
-\u2233 = \uFFFD\uFFFD\uFFFD\uFFFD\u2233\uDBFF\uDF55\uDBFF\uDF56\uDBFF\uDF57 # ANTICLOCKWISE CONTOUR INTEGRAL
-
-\u22C0 = \uFFFD\uFFFD\uFFFD\uFFFD\u22C0\uDBFF\uDF92\uDBFF\uDF93 # N-ARY LOGICAL AND
-\u22C1 = \uFFFD\uFFFD\uFFFD\uFFFD\u22C1\uDBFF\uDF94\uDBFF\uDF95 # N-ARY LOGICAL OR
-\u22C2 = \uFFFD\uFFFD\uFFFD\uFFFD\u22C2\uDBFF\uDF8E\uDBFF\uDF8F # N-ARY INTERSECTION
-\u22C3 = \uFFFD\uFFFD\uFFFD\uFFFD\u22C3\uDBFF\uDF8C\uDBFF\uDF8D # N-ARY UNION
-\u2308 = \u23A1\uFFFD\uFFFD\u23A2\u2308\uDBFF\uDFE2\uDBFF\uDFE3\uDBFF\uDFE4 # LEFT CEILING
-\u2309 = \u23A4\uFFFD\uFFFD\u23A5\u2309\uDBFF\uDFE5\uDBF\uDFE6\uDBFF\uDFE7 # RIGHT CEILING
-\u230A = \uFFFD\uFFFD\u23A3\u23A2\u230A\uDBFF\uDFE8\uDBFF\uDFE9\uDBFF\uDFEA # LEFT FLOOR
-\u230B = \uFFFD\uFFFD\u23A6\u23A5\u230B\u230B\uDBFF\uDFEB\uDBFF\uDFEC\uDBFF\uDFED # RIGHT FLOOR
-
-# \u27C5 = \uFFFD\uFFFD\uFFFD\uFFFD\u27C5\uDBFF\uDDF3\uDBFF\uDDF5\uDBFF\uDDF7\uDBFF\uDDF9\uDBFF\uDDFB # LEFT S-SHAPED BAG DELIMITER (*)
-# \u27C6 = \uFFFD\uFFFD\uFFFD\uFFFD\uDBFF\uDDF4\uDBFF\uDDF6\uDBFF\uDDF8\uDBFF\uDDFA\uDBFF\uDDFC # RIGHT S-SHAPED BAG DELIMITER (*)
-\u27E6 = \uFFFD\uFFFD\uFFFD\uFFFD\u27E6\uDBFF\uDFDA\uDBFF\uDFDB\uDBFF\uDFDC\uDBFF\uDFDD # MATHEMATICAL LEFT WHITE SQUARE BRACKET
-\u27E7 = \uFFFD\uFFFD\uFFFD\uFFFD\u27E7\uDBFF\uDFDE\uDBFF\uDFDF\uDBFF\uDFE0\uDBFF\uDFE1 # MATHEMATICAL RIGHT WHITE SQUARE BRACKET
-\u27E8 = \uFFFD\uFFFD\uFFFD\uFFFD\u27E8\uDBFF\uDF89\uDBFF\uDF8A\uDBFF\uD8B # MATHEMATICAL LEFT ANGLE BRACKET
-\u27E9 = \uFFFD\uFFFD\uFFFD\uFFFD\u27E9\uDBFF\uDF7C\uDBFF\uDF7D\uDBFF\uDF7E # MATHEMATICAL RIGHT ANGLE BRACKET
-\u27EA = \uFFFD\uFFFD\uFFFD\uFFFD\u27EA\uDBFF\uDF76\uDBFF\uDF77\uDBFF\uDF78 # MATHEMATICAL LEFT DOUBLE ANGLE BRACKET
-\u27EB = \uFFFD\uFFFD\uFFFD\uFFFD\u27EB\uDBFF\uDF79\uDBFF\uDF7A\uDBFF\uDF7B # MATHEMATICAL RIGHT DOUBLE ANGLE BRACKET
-
-\u29FC = \uFFFD\uFFFD\uFFFD\uFFFD\u29FC\uDBFF\uDEC8\uDBFF\uDEC9\uDBFF\uDECA # LEFT-POINTING CURVED ANGLE BRACKET
-\u29FD = \uFFFD\uFFFD\uFFFD\uFFFD\u29FD\uDBFF\uDECB\uDBFF\uDECC\uDBFF\uDECD # RIGHT-POINTING CURVED ANGLE BRACKET
-
-\u2A00 = \uFFFD\uFFFD\uFFFD\uFFFD\u2A00\uDBFF\uDF96\uDBFF\uDF97 # N-ARY CIRCLED DOT OPERATOR
-\u2A01 = \uFFFD\uFFFD\uFFFD\uFFFD\u2A01\uDBFF\uDF98\uDBFF\uDFA5 # N-ARY CIRCLED PLUS OPERATOR
-\u2A02 = \uFFFD\uFFFD\uFFFD\uFFFD\u2A02\uDBFF\uDF7F\uDBFF\uDF80 # N-ARY CIRCLED TIMES OPERATOR
-\u2A03 = \uFFFD\uFFFD\uFFFD\uFFFD\u2A03\uDBFF\uDF81\uDBFF\uDF82 # N-ARY UNION OPERATOR WITH DOT
-\u2A04 = \uFFFD\uFFFD\uFFFD\uFFFD\u2A04\uDBFF\uDF90\uDBFF\uDF91 # N-ARY UNION OPERATOR WITH PLUS
-\u2A05 = \uFFFD\uFFFD\uFFFD\uFFFD\u2A05\uDBFF\uDF83\uDBFF\uDF84 # N-ARY SQUARE INTERSECTION OPERATOR
-\u2A06 = \uFFFD\uFFFD\uFFFD\uFFFD\u2A06\uDBFF\uDF85\uDBFF\uDF86 # N-ARY SQUARE UNION OPERATOR
-\u2A07 = \uFFFD\uFFFD\uFFFD\uFFFD\u2A07\uDBFF\uDF72\uDBFF\uDF73 # TWO LOGICAL AND OPERATOR
-\u2A08 = \uFFFD\uFFFD\uFFFD\uFFFD\u2A08\uDBFF\uDF74\uDBFF\uDF75 # TWO LOGICAL OR OPERATOR
-\u2A09 = \uFFFD\uFFFD\uFFFD\uFFFD\u2A09\uDBFF\uDF87\uDBFF\uDF88 # N-ARY TIMES OPERATOR
-\u2A0C = \uFFFD\uFFFD\uFFFD\uFFFD\u2A0C\uDBFF\uDF1F\uDBFF\uDF20\uDBFF\uDF21 # QUADRUPLE INTEGRAL OPERATOR
-\u2A0D = \uFFFD\uFFFD\uFFFD\uFFFD\u2A0D\uDBFF\uDF22\uDBFF\uDF23\uDBFF\uDF24 # FINITE PART INTEGRAL
-\u2A0E = \uFFFD\uFFFD\uFFFD\uFFFD\u2A0E\uDBFF\uDF25\uDBFF\uDF26\uDBFF\uDF27 # INTEGRAL WITH DOUBLE STROKE
-\u2A0F = \uFFFD\uFFFD\uFFFD\uFFFD\u2A0F\uDBFF\uDF28\uDBFF\uDF29\uDBFF\uDF2A # INTEGRAL AVERAGE WITH SLASH
-\u2A10 = \uFFFD\uFFFD\uFFFD\uFFFD\u2A10\uDBFF\uDF2B\uDBFF\uDF2C\uDBFF\uDF2D # CIRCULATION FUNCTION
-\u2A11 = \uFFFD\uFFFD\uFFFD\uFFFD\u2A11\uDBFF\uDF2E\uDBFF\uDF2F\uDBFF\uDF30 # ANTICLOCKWISE INTEGRATION
-\u2A12 = \uFFFD\uFFFD\uFFFD\uFFFD\u2A12\uDBFF\uDF31\uDBFF\uDF32\uDBFF\uDF33 # LINE INTEGRATION WITH RECTANGULAR PATH AROUND POLE
-\u2A13 = \uFFFD\uFFFD\uFFFD\uFFFD\u2A13\uDBFF\uDF34\uDBFF\uDF35\uDBFF\uDF36 # LINE INTEGRATION WITH SEMICIRCULAR PATH AROUND POLE
-\u2A14 = \uFFFD\uFFFD\uFFFD\uFFFD\u2A14\uDBFF\uDF37\uDBFF\uDF38\uDBFF\uDF39 # LINE INTEGRATION NOT INCLUDING THE POLE
-\u2A15 = \uFFFD\uFFFD\uFFFD\uFFFD\u2A15\uDBFF\uDF3A\uDBFF\uDF3B\uDBFF\uDF3C # INTEGRAL AROUND A POINT OPERATOR
-\u2A16 = \uFFFD\uFFFD\uFFFD\uFFFD\u2A16\uDBFF\uDF3D\uDBFF\uDF3E\uDBFF\uDF3F # QUATERNION INTEGRAL OPERATOR
-\u2A17 = \uFFFD\uFFFD\uFFFD\uFFFD\u2A17\uDBFF\uDF40\uDBFF\uDF41\uDBFF\uDF42 # INTEGRAL WITH LEFTWARDS ARROW WITH HOOK
-\u2A18 = \uFFFD\uFFFD\uFFFD\uFFFD\u2A18\uDBFF\uDF43\uDBFF\uDF44\uDBFF\uDF45 # INTEGRAL WITH TIMES SIGN
-\u2A19 = \uFFFD\uFFFD\uFFFD\uFFFD\u2A19\uDBFF\uDF46\uDBFF\uDF47\uDBFF\uDF48 # INTEGRAL WITH INTERSECTION
-\u2A1A = \uFFFD\uFFFD\uFFFD\uFFFD\u2A1A\uDBFF\uDF49\uDBFF\uDF4A\uDBFF\uDF4B # INTEGRAL WITH UNION
-\u2A1B = \uFFFD\uFFFD\uFFFD\uFFFD\u2A1B\uDBFF\uDF4C\uDBFF\uDF4D\uDBFF\uDF4E # INTEGRAL WITH OVERBAR
-\u2A1C = \uFFFD\uFFFD\uFFFD\uFFFD\u2A1C\uDBFF\uDF4F\uDBFF\uDF50\uDBFF\uDF51 # INTEGRAL WITH UNDERBAR
-
-\u005E = \uFFFD\uFFFD\uFFFD\uFFFD\u005E\uDBFF\uDFA6\uDBFF\uDFA7\uDBFF\uDFA8 # CIRCUMFLEX ACCENT
-\u0302 = \uFFFD\uFFFD\uFFFD\uFFFD\u005E\uDBFF\uDFA6\uDBFF\uDFA7\uDBFF\uDFA8 # COMBINING CIRCUMFLEX ACCENT
-\u007E = \uFFFD\uFFFD\uFFFD\uFFFD\u007E\uDBFF\uDFAA\uDBFF\uDFAB\uDBFF\uDFAC\uDBFF\uDFAD # TILDE
-\u02DC = \uFFFD\uFFFD\uFFFD\uFFFD\u007E\uDBFF\uDFAA\uDBFF\uDFAB\uDBFF\uDFAC\uDBFF\uDFAD # SMALL TILDE
-# \u0303 = \uFFFD\uFFFD\uFFFD\uFFFD\u007E\uDBFF\uDFAA\uDBFF\uDFAB\uDBFF\uDFAC\uDBFF\uDFAD # COMBINING TILDE (*)
-# \u0305 = \uFFFD\uFFFD\uFFFD\uDBFF\uDF1E\u0305 COMBINING OVERLINE (*)
-# \u0306 = \uFFFD\uFFFD\uFFFD\uFFFD\u02D8\uDBFF\uDFB2\uDBFF\uDFB3\uDBFF\uDFB4\uDBFF\uDFB5 # COMBINING BREVE (*)
-# \u02D8 = \uFFFD\uFFFD\uFFFD\uFFFD\u02D8\uDBFF\uDFB2\uDBFF\uDFB3\uDBFF\uDFB4\uDBFF\uDFB5 # BREVE (not stretchy)
-\u02C7 = \uFFFD\uFFFD\uFFFD\uFFFD\u02C7\uDBFF\uDFAE\uDBFF\uDFAF\uDBFF\uDFB0\uDBFF\uDFB1 # CARON
-# \u030C = \uFFFD\uFFFD\uFFFD\uFFFD\u02C7\uDBFF\uDFAE\uDBFF\uDFAF\uDBFF\uDFB0\uDBFF\uDFB1 # COMBINING CARON (*)
-# \u0332 = \uFFFD\uFFFD\uFFFD\uDBFF\uDF1D\u0332\uDBFF\uDF1D\uDBFF\uDF18\uDBFF\uDF14 # COMBINING LOW LINE (*)
-# \u0333 = \uFFFD\uFFFD\uFFFD\uDBFF\uDF1C\u0333\uDBFF\uDF1C\uDBFF\uDF17\uDBFF\uDF13 # COMBINING DOUBLE LOW LINE (*)
-# \u033F = \uFFFD\uFFFD\uFFFD\uDBFF\uDF1B\u033F\uDBFF\uDF1B\uDBFF\uDF16\uDBFF\uDF12 # COMBINING DOUBLE OVERLINE (*)
-# \u20D0 = \u20D0\uFFFD\uFFFD\uDBFF\uDF1A\u20D0 # COMBINING LEFT HARPOON ABOVE (*)
-# \u20D1 = \uFFFD\uFFFD\u20D1\uDBFF\uDF1A\u20D1 # COMBINING RIGHT HARPOON ABOVE (*)
-# \u20D6 = \u20D6\uFFFD\uFFFD\uDBFF\uDF1A\u20D6\uDBFF\uDE4A\uDBFF\uDE4B\uDBFF\uDE4C\uDBFF\uDE4D # COMBINING LEFT ARROW ABOVE (*)
-# \u20D7 = \uFFFD\uFFFD\u20D7\uDBFF\uDF1A\u20D7\uDBFF\uDE4E\uDBFF\uDE4F\uDBFF\uDE50\uDBFF\uDE51 # COMBINING RIGHT ARROW ABOVE (*)
-# \u20E1 = \u20D6\uFFFD\u20D7\uDBFF\uDF1A\u20E1 # COMBINING LEFT RIGHT ARROW ABOVE (*)
-# \u20E9 = \uDBFF\uDEEC\uFFFD\uDBFF\uDEED\uDBFF\uDEEB\u20E9 # COMBINING WIDE BRIDGE ABOVE (*)
-
-\u2190 = \uDBFF\uDF11\uFFFD\uDBFF\uDF10\u23AF\u2190 # LEFTWARDS ARROW 
-\u2192 = \uDBFF\uDF0E\uFFFD\uDBFF\uDF0F\u23AF\u2192 # RIGHTWARDS ARROW
-\u2194 = \uDBFF\uDF11\uFFFD\uDBFF\uDF0F\u23AF\u2194 # LEFT RIGHT ARROW
-\u21A4 = \uDBFF\uDF11\uFFFD\uDBFF\uDF08\u23AF\u21A4 # LEFTWARDS ARROW FROM BAR
-\u21A6 = \uDBFF\uDF07\uFFFD\uDBFF\uDF0F\u23AF\u21A6 # RIGHTWARDS ARROW FROM BAR
-\u21A9 = \uDBFF\uDF11\uFFFD\uDBFF\uDF06\u23AF\u21A9 # LEFTWARDS ARROW WITH HOOK
-\u21AA = \uDBFF\uDF05\uFFFD\uDBFF\uDF0F\u23AF\u21AA # RIGHTWARDS ARROW WITH HOOK
-
-\u21D0 = \uDBFF\uDF0D\uFFFD\uDBFF\uDF0C\uDBFF\uDF09\u21D0 # LEFTWARDS DOUBLE ARROW
-\u21D2 = \uDBFF\uDF0A\uFFFD\uDBFF\uDF0B\uDBFF\uDF09\u21D2 # RIGHTWARDS DOUBLE ARROW
-\u21D4 = \uDBFF\uDF0D\uFFFD\uDBFF\uDF0B\uDBFF\uDF09\u21D4 # LEFT RIGHT DOUBLE ARROW
-
-\u23B4 = \uDBFF\uDEEC\uFFFD\uDBFF\uDEED\uDBFF\uDEEB\u23B4\uDBFF\uDEFD\uDBFF\uDEFE\uDBFF\uDEFF # TOP SQUARE BRACKET
-\u23B5 = \uDBFF\uDEEE\uFFFD\uDBFF\uDEEF\uDBFF\uDEEA\u23B5\uDBFF\uDF00\uDBFF\uDF01\uDBFF\uDF02 # BOTTOM SQUARE BRACKET
-
-\u23DC = \uDBFF\uDFC7\uFFFD\uDBFF\uDFC9\uDBFF\uDFCA\u23DC\uDBFF\uDEF7\uDBFF\uDEF8\uDBFF\uDEF9 # TOP PARENTHESIS
-\uFE35 = \uDBFF\uDFC7\uFFFD\uDBFF\uDFC9\uDBFF\uDFCA\u23DC\uDBFF\uDEF7\uDBFF\uDEF8\uDBFF\uDEF9 # &OverParenthesis; (MathML 2.0)
-\u23DD = \uDBFF\uDFCB\uFFFD\uDBFF\uDFCD\uDBFF\uDEF0\u23DD\uDBFF\uDEFA\uDBFF\uDEFB\uDBFF\uDEFC # BOTTOM PARENTHESIS
-\uFE36 = \uDBFF\uDFCB\uFFFD\uDBFF\uDFCD\uDBFF\uDEF0\u23DD\uDBFF\uDEFA\uDBFF\uDEFB\uDBFF\uDEFC # &UnderParenthesis; (MathML 2.0)
-
-\u23DE = \uDBFF\uDFC7\uDBFF\uDFC8\uDBFF\uDFC9\uDBFF\uDFCA\u23DE\uDBFF\uDFC1\uDBFF\uDFC2\uDBFF\uDFC3 # TOP CURLY BRACKET
-\uFE37 = \uDBFF\uDFC7\uDBFF\uDFC8\uDBFF\uDFC9\uDBFF\uDFCA\u23DE\uDBFF\uDFC1\uDBFF\uDFC2\uDBFF\uDFC3 # &OverBrace; (MathML 2.0)
-\u23DF = \uDBFF\uDFCB\uDBFF\uDFCC\uDBFF\uDFCD\uDBFF\uDEF0\u23DF\uDBFF\uDFC4\uDBFF\uDFC5\uDBFF\uDFC6 # BOTTOM CURLY BRACKET
-\uFE38 = \uDBFF\uDFCB\uDBFF\uDFCC\uDBFF\uDFCD\uDBFF\uDEF0\u23DF\uDBFF\uDFC4\uDBFF\uDFC5\uDBFF\uDFC6 # &UnderBrace; (MathML 2.0)
-\u23E0 = \uFFFD\uFFFD\uFFFD\uFFFD\u23E0\uDBFF\uDEF1\uDBFF\uDEF2\uDBFF\uDEF3 # TOP TORTOISE SHELL BRACKET
-\u23E1 = \uFFFD\uFFFD\uFFFD\uFFFD\u23E1\uDBFF\uDEF4\uDBFF\uDEF5\uDBFF\uDEF6 # BOTTOM TORTOISE SHELL BRACKET
-
-\u2906 = \uDBFF\uDF0D\uFFFD\uDBFF\uDF04\uDBFF\uDF09\u2906 # LEFTWARDS DOUBLE ARROW FROM BAR
-\u2907 = \uDBFF\uDF03\uFFFD\uDBFF\uDF0B\uDBFF\uDF09\u2907 # RIGHTWARDS DOUBLE ARROW FROM BAR
deleted file mode 100644
--- a/layout/mathml/mathfontSTIXSize1.properties
+++ /dev/null
@@ -1,70 +0,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/.
-
-#  LOCALIZATION NOTE: FILE
-#  Do not translate anything in this file
-
-# This file contains the list of some stretchy MathML chars that
-# can be rendered with STIXSize* set of fonts,
-# with some help from STIXNonUnicode and STIXGeneral.
-
-external.1 = STIXNonUnicode
-external.2 = STIXSize2
-external.3 = STIXSize3
-external.4 = STIXSize4
-external.5 = STIXSize5
-external.6 = STIXGeneral
-
-#        [ T/L |  M  | B/R |  G  | size0 ... size{N-1} ]
-\u0028 = \u239B\uFFFD\u239D\u239C\uFFFD((@2(@3(@4 # (
-\u0029 = \u239E\uFFFD\u23A0\u239F\uFFFD))@2)@3)@4 # )
-\u005B = \u23A1\uFFFD\u23A3\u23A2\u005B[[@2[@3[@4 # [
-\u005D = \u23A4\uFFFD\u23A6\u23A5\u005D]]@2]@3]@4 # ]
-\u007B = \u23A7\u23A8\u23A9\u23AA\u007B{{@2{@3{@4 # {
-\u007D = \u23AB\u23AC\u23AD\u23AA\u007D}}@2}@3}@4 # }
-
-# N-ARY operators
-\u2140 = \uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u2140 # DOUBLE-STRUCK N-ARY SUMMATION
-\u220F = \uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u220F # N-ARY PRODUCT
-\u2210 = \uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u2210 # N-ARY COPRODUCT
-\u2211 = \uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u2211 # N-ARY SUMMATION
-\u22C0 = \uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u22C0 # N-ARY LOGICAL AND
-\u22C1 = \uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u22C1 # N-ARY LOGICAL OR
-\u22C2 = \uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u22C2 # N-ARY INTERSECTION
-\u22C3 = \uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u22C3 # N-ARY UNION
-\u2A00 = \uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u2A00 # N-ARY CIRCLED DOT OPERATOR
-\u2A01 = \uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u2A01 # N-ARY CIRCLED PLUS OPERATOR
-\u2A02 = \uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u2A02 # N-ARY CIRCLED TIMES OPERATOR
-\u2A03 = \uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u2A03 # N-ARY UNION OPERATOR WITH DOT
-\u2A04 = \uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u2A04 # N-ARY UNION OPERATOR WITH PLUS
-\u2A05 = \uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u2A05 # N-ARY SQUARE INTERSECTION OPERATOR
-\u2A06 = \uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u2A06 # N-ARY SQUARE UNION OPERATOR
-\u2A09 = \uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u2A09 # N-ARY TIMES OPERATOR
-\u2AFF = \uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u2AFF # N-ARY WHITE VERTICAL BAR
-
-# E000 stix-radical symbol vertical extender
-# E001 stix-radical symbol top corner
-\u221A = \uE001@1\uFFFD\u221A@4\uE000@1\uFFFD\u221A\u221A@2\u221A@3 # Sqrt, radic
-
-\u222B = \u2320\uFFFD\u2321\u23AE\uFFFD\u222B@6 # Integral, int
-
-\u27E8 = \uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u27E8\u27E8@2\u27E8@3\u27E8@4 # LeftAngleBracket
-\u27E9 = \uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u27E9\u27E9@2\u27E9@3\u27E9@4 # RightAngleBracket
-
-\u23DE = \uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u23DE\u23DE@2\u23DE@3\u23DE@4\u23DE@5 # &OverBrace; (Unicode)
-\uFE37 = \uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u23DE\u23DE@2\u23DE@3\u23DE@4\u23DE@5 # &OverBrace; (MathML 2.0)
-\u23B4 = \uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u23B4\u23B4@2\u23B4@3\u23B4@4\u23B4@5 # &OverBracket;
-\u23DC = \uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u23DC\u23DC@2\u23DC@3\u23DC@4\u23DC@5 # &OverParenthesis; (Unicode)
-\uFE35 = \uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u23DC\u23DC@2\u23DC@3\u23DC@4\u23DC@5 # &OverParenthesis; (MathML 2.0)
-\u23DF = \uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u23DF\u23DF@2\u23DF@3\u23DF@4\u23DF@5 # &UnderBrace; (Unicode)
-\uFE38 = \uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u23DF\u23DF@2\u23DF@3\u23DF@4\u23DF@5 # &UnderBrace; (MathML 2.0)
-\u23B5 = \uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u23B5\u23B5@2\u23B5@3\u23B5@4\u23B5@5 # &UnderBracket;
-\u23DD = \uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u23DD\u23DD@2\u23DD@3\u23DD@4\u23DD@5 # &UnderParenthesis; (Unicode)
-\uFE36 = \uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u23DD\u23DD@2\u23DD@3\u23DD@4\u23DD@5 # &UnderParenthesis; (MathML 2.0)
-
-\u005E = \uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u0302\u0302@2\u0302@3\u0302@4\u0302@5 # circumflex accent, COMBINING CIRCUMFLEX ACCENT
-\u02C6 = \uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u0302\u0302@2\u0302@3\u0302@4\u0302@5 # modifier letter circumflex accent, COMBINING CIRCUMFLEX ACCENT
-\u007E = \uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u0303\u0303@2\u0303@3\u0303@4\u0303@5 # ~ tilde, COMBINING TILDE
-\u02DC = \uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u0303\u0303@2\u0303@3\u0303@4\u0303@5 # small tilde, COMBINING TILDE
-\u02C7 = \uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u030C\u030C@2\u030C@3\u030C@4\u030C@5 # caron, COMBINING CARON
--- a/layout/mathml/moz.build
+++ b/layout/mathml/moz.build
@@ -50,21 +50,19 @@ if CONFIG['ENABLE_TESTS']:
     PARALLEL_DIRS += [
         'tests',
     ]
 
 JAR_MANIFESTS += ['jar.mn']
 
 RESOURCE_FILES.fonts += [
     'mathfont.properties',
-    'mathfontAsanaMath.properties',
     'mathfontMathJax_Main.properties',
     'mathfontStandardSymbolsL.properties',
     'mathfontSTIXNonUnicode.properties',
-    'mathfontSTIXSize1.properties',
     'mathfontSTIXSizeOneSym.properties',
     'mathfontUnicode.properties',
 ]
 
 RESOURCE_FILES.fonts['mathfont.properties'].preprocess = True
 
 if CONFIG['TARGET_MD_ARCH'] == 'win32':
     RESOURCE_FILES.fonts += [
--- a/layout/mathml/nsMathMLChar.cpp
+++ b/layout/mathml/nsMathMLChar.cpp
@@ -23,58 +23,103 @@
 #include "nsCSSRendering.h"
 #include "prprf.h"         // For PR_snprintf()
 
 #include "nsDisplayList.h"
 
 #include "nsMathMLOperators.h"
 #include <algorithm>
 
+#include "gfxMathTable.h"
+
 using namespace mozilla;
 
 //#define NOISY_SEARCH 1
 
+static const float kLargeOpFactor = float(M_SQRT2);
+static const float kIntegralFactor = 2.0;
+
 // -----------------------------------------------------------------------------
 static const nsGlyphCode kNullGlyph = {{{0, 0}}, 0};
 typedef enum {eExtension_base, eExtension_variants, eExtension_parts}
   nsMathfontPrefExtension;
 
 // -----------------------------------------------------------------------------
 // nsGlyphTable is a class that provides an interface for accessing glyphs
 // of stretchy chars. It acts like a table that stores the variants of bigger
 // sizes (if any) and the partial glyphs needed to build extensible symbols.
-// An instance of nsGlyphTable is associated to one primary font. Extra glyphs
-// can be taken in other additional fonts when stretching certain characters.
-// These supplementary fonts are referred to as "external" fonts to the table.
 //
-// Bigger sizes (if any) of the char can then be retrieved with
-// BigOf(aSize). Partial glyphs can be retrieved with ElementAt()
+// Bigger sizes (if any) of the char can then be retrieved with BigOf(...).
+// Partial glyphs can be retrieved with ElementAt(...).
 //
 // A table consists of "nsGlyphCode"s which are viewed either as Unicode
-// points or as direct glyph indices, depending on the type of the table.
-// XXX The latter is not yet supported.
+// points (for nsPropertiesTable) or as direct glyph indices (for
+// nsOpenTypeTable)
+// -----------------------------------------------------------------------------
+
+class nsGlyphTable {
+public:
+  virtual ~nsGlyphTable() {}
+
+  virtual const nsAString&
+  FontNameFor(const nsGlyphCode& aGlyphCode) const = 0;
+
+  // Getters for the parts
+  virtual nsGlyphCode ElementAt(gfxContext*   aThebesContext,
+                                int32_t       aAppUnitsPerDevPixel,
+                                gfxFontGroup* aFontGroup,
+                                char16_t      aChar,
+                                bool          aVertical,
+                                uint32_t      aPosition) = 0;
+  virtual nsGlyphCode BigOf(gfxContext*   aThebesContext,
+                            int32_t       aAppUnitsPerDevPixel,
+                            gfxFontGroup* aFontGroup,
+                            char16_t      aChar,
+                            bool          aVertical,
+                            uint32_t      aSize) = 0;
+
+  // True if this table contains parts to render this char
+  virtual bool HasPartsOf(gfxContext*   aThebesContext,
+                          int32_t       aAppUnitsPerDevPixel,
+                          gfxFontGroup* aFontGroup,
+                          char16_t      aChar,
+                          bool          aVertical) = 0;
+
+  virtual gfxTextRun* MakeTextRun(gfxContext*        aThebesContext,
+                                  int32_t            aAppUnitsPerDevPixel,
+                                  gfxFontGroup*      aFontGroup,
+                                  const nsGlyphCode& aGlyph) = 0;
+protected:
+  nsGlyphTable() : mCharCache(0) {}
+  // For speedy re-use, we always cache the last data used in the table.
+  // mCharCache is the Unicode point of the last char that was queried in this
+  // table.
+  char16_t mCharCache;
+};
+
+// An instance of nsPropertiesTable is associated with one primary font. Extra
+// glyphs can be taken in other additional fonts when stretching certain
+// characters.
+// These supplementary fonts are referred to as "external" fonts to the table.
 
 // General format of MathFont Property Files from which glyph data are
 // retrieved:
 // -----------------------------------------------------------------------------
 // Each font should have its set of glyph data. For example, the glyph data for
 // the "Symbol" font and the "MT Extra" font are in "mathfontSymbol.properties"
 // and "mathfontMTExtra.properties", respectively. The mathfont property file
 // is a set of all the stretchy MathML characters that can be rendered with that
 // font using larger and/or partial glyphs. The entry of each stretchy character
 // in the mathfont property file gives, in that order, the 4 partial glyphs:
 // Top (or Left), Middle, Bottom (or Right), Glue; and the variants of bigger
 // sizes (if any).
 // A position that is not relevant to a particular character is indicated there
 // with the UNICODE REPLACEMENT CHARACTER 0xFFFD.
 // -----------------------------------------------------------------------------
 
-#define NS_TABLE_TYPE_UNICODE       0
-#define NS_TABLE_TYPE_GLYPH_INDEX   1
-
 #define NS_TABLE_STATE_ERROR       -1
 #define NS_TABLE_STATE_EMPTY        0
 #define NS_TABLE_STATE_READY        1
 
 // helper to trim off comments from data in a MathFont Property File
 static void
 Clean(nsString& aValue)
 {
@@ -93,101 +138,124 @@ LoadProperties(const nsString& aName,
   uriStr.AssignLiteral("resource://gre/res/fonts/mathfont");
   uriStr.Append(aName);
   uriStr.StripWhitespace(); // that may come from aName
   uriStr.AppendLiteral(".properties");
   return NS_LoadPersistentPropertiesFromURISpec(getter_AddRefs(aProperties), 
                                                 NS_ConvertUTF16toUTF8(uriStr));
 }
 
-// -----------------------------------------------------------------------------
-
-class nsGlyphTable {
+class nsPropertiesTable MOZ_FINAL : public nsGlyphTable {
 public:
-  explicit nsGlyphTable(const nsString& aPrimaryFontName)
-    : mFontName(1), // ensure space for primary font name.
-      mState(NS_TABLE_STATE_EMPTY),
-      mCharCache(0)
+  explicit nsPropertiesTable(const nsString& aPrimaryFontName)
+    : mFontName(1) // ensure space for primary font name.
+    , mState(NS_TABLE_STATE_EMPTY)
   {
-    MOZ_COUNT_CTOR(nsGlyphTable);
+    MOZ_COUNT_CTOR(nsPropertiesTable);
     mFontName.AppendElement(aPrimaryFontName);
   }
 
-  // not a virtual destructor: this class is not intended to be subclassed
-  ~nsGlyphTable()
+  ~nsPropertiesTable()
   {
-    MOZ_COUNT_DTOR(nsGlyphTable);
+    MOZ_COUNT_DTOR(nsPropertiesTable);
   }
 
   const nsAString& PrimaryFontName() const
   {
     return mFontName[0];
   }
 
-  const nsAString& FontNameFor(const nsGlyphCode& aGlyphCode) const
+  const nsAString&
+  FontNameFor(const nsGlyphCode& aGlyphCode) const MOZ_OVERRIDE
   {
     NS_ASSERTION(!aGlyphCode.IsGlyphID(),
-                 "nsGlyphTable can only access glyphs by Unicode code point");
+                 "nsPropertiesTable can only access glyphs by code point");
     return mFontName[aGlyphCode.font];
   }
 
-  // Getters for the parts
-  nsGlyphCode ElementAt(char16_t aChar, uint32_t aPosition);
-  nsGlyphCode BigOf(char16_t aChar, int32_t aSize) {
-    return ElementAt(aChar, 4 + aSize);
+  virtual nsGlyphCode ElementAt(gfxContext*   aThebesContext,
+                                int32_t       aAppUnitsPerDevPixel,
+                                gfxFontGroup* aFontGroup,
+                                char16_t      aChar,
+                                bool          aVertical,
+                                uint32_t      aPosition) MOZ_OVERRIDE;
+
+  virtual nsGlyphCode BigOf(gfxContext*   aThebesContext,
+                            int32_t       aAppUnitsPerDevPixel,
+                            gfxFontGroup* aFontGroup,
+                            char16_t      aChar,
+                            bool          aVertical,
+                            uint32_t      aSize) MOZ_OVERRIDE
+  {
+    return ElementAt(aThebesContext, aAppUnitsPerDevPixel, aFontGroup,
+                     aChar, aVertical, 4 + aSize);
   }
 
-  // True if this table contains parts to render this char
-  bool HasPartsOf(char16_t aChar) {
-    return (ElementAt(aChar, 0).Exists() || ElementAt(aChar, 1).Exists() ||
-            ElementAt(aChar, 2).Exists() || ElementAt(aChar, 3).Exists());
+  virtual bool HasPartsOf(gfxContext*   aThebesContext,
+                          int32_t       aAppUnitsPerDevPixel,
+                          gfxFontGroup* aFontGroup,
+                          char16_t      aChar,
+                          bool          aVertical) MOZ_OVERRIDE
+  {
+    return (ElementAt(aThebesContext, aAppUnitsPerDevPixel, aFontGroup,
+                      aChar, aVertical, 0).Exists() ||
+            ElementAt(aThebesContext, aAppUnitsPerDevPixel, aFontGroup,
+                      aChar, aVertical, 1).Exists() ||
+            ElementAt(aThebesContext, aAppUnitsPerDevPixel, aFontGroup,
+                      aChar, aVertical, 2).Exists() ||
+            ElementAt(aThebesContext, aAppUnitsPerDevPixel, aFontGroup,
+                      aChar, aVertical, 3).Exists());
   }
 
-  gfxTextRun* MakeTextRun(gfxContext*        aThebesContext,
-                          int32_t            aAppUnitsPerDevPixel,
-                          gfxFontGroup*      aFontGroup,
-                          const nsGlyphCode& aGlyph);
+  virtual gfxTextRun* MakeTextRun(gfxContext*        aThebesContext,
+                                  int32_t            aAppUnitsPerDevPixel,
+                                  gfxFontGroup*      aFontGroup,
+                                  const nsGlyphCode& aGlyph) MOZ_OVERRIDE;
 private:
 
   // mFontName[0] is the primary font associated to this table. The others 
   // are possible "external" fonts for glyphs not in the primary font
   // but which are needed to stretch certain characters in the table
   nsTArray<nsString> mFontName;
 
   // Tri-state variable for error/empty/ready
   int32_t mState;
 
   // The set of glyph data in this table, as provided by the MathFont Property
   // File
   nsCOMPtr<nsIPersistentProperties> mGlyphProperties;
 
-  // For speedy re-use, we always cache the last data used in the table.
-  // mCharCache is the Unicode point of the last char that was queried in this
-  // table. mGlyphCache is a buffer containing the glyph data associated to
-  // that char. For a property line 'key = value' in the MathFont Property File,
+  // mGlyphCache is a buffer containing the glyph data associated with
+  // mCharCache.
+  // For a property line 'key = value' in the MathFont Property File,
   // mCharCache will retain the 'key' -- which is a Unicode point, while
   // mGlyphCache will retain the 'value', which is a consecutive list of
   // nsGlyphCodes, i.e., the pairs of 'code@font' needed by the char -- in
   // which 'code@0' can be specified
   // without the optional '@0'. However, to ease subsequent processing,
   // mGlyphCache excludes the '@' symbol and explicitly inserts all optional '0'
   // that indicates the primary font identifier. Specifically therefore, the
   // k-th glyph is characterized by :
   // 1) mGlyphCache[3*k],mGlyphCache[3*k+1] : its Unicode point
   // 2) mGlyphCache[3*k+2] : the numeric identifier of the font where it comes
   // from.
   // A font identifier of '0' means the default primary font associated to this
   // table. Other digits map to the "external" fonts that may have been
   // specified in the MathFont Property File.
   nsString  mGlyphCache;
-  char16_t mCharCache;
 };
 
+/* virtual */
 nsGlyphCode
-nsGlyphTable::ElementAt(char16_t aChar, uint32_t aPosition)
+nsPropertiesTable::ElementAt(gfxContext*   /* aThebesContext */,
+                             int32_t       /* aAppUnitsPerDevPixel */,
+                             gfxFontGroup* /* aFontGroup */,
+                             char16_t      aChar,
+                             bool          /* aVertical */,
+                             uint32_t      aPosition)
 {
   if (mState == NS_TABLE_STATE_ERROR) return kNullGlyph;
   // Load glyph properties if this is the first time we have been here
   if (mState == NS_TABLE_STATE_EMPTY) {
     nsresult rv = LoadProperties(mFontName[0], mGlyphProperties);
 #ifdef DEBUG
     nsAutoCString uriStr;
     uriStr.AssignLiteral("resource://gre/res/fonts/mathfont");
@@ -281,42 +349,230 @@ nsGlyphTable::ElementAt(char16_t aChar, 
   if (index+2 >= mGlyphCache.Length()) return kNullGlyph;
   nsGlyphCode ch;
   ch.code[0] = mGlyphCache.CharAt(index);
   ch.code[1] = mGlyphCache.CharAt(index + 1);
   ch.font = mGlyphCache.CharAt(index + 2);
   return ch.code[0] == char16_t(0xFFFD) ? kNullGlyph : ch;
 }
 
+/* virtual */
 gfxTextRun*
-nsGlyphTable::MakeTextRun(gfxContext*        aThebesContext,
-                          int32_t            aAppUnitsPerDevPixel,
-                          gfxFontGroup*      aFontGroup,
-                          const nsGlyphCode& aGlyph)
+nsPropertiesTable::MakeTextRun(gfxContext*        aThebesContext,
+                               int32_t            aAppUnitsPerDevPixel,
+                               gfxFontGroup*      aFontGroup,
+                               const nsGlyphCode& aGlyph)
 {
-  NS_ASSERTION(!aGlyph.IsGlyphID(), "not yet implemented");
+  NS_ASSERTION(!aGlyph.IsGlyphID(),
+               "nsPropertiesTable can only access glyphs by code point");
   return aFontGroup->
     MakeTextRun(aGlyph.code, aGlyph.Length(), aThebesContext,
                 aAppUnitsPerDevPixel, 0);
 }
 
+// An instance of nsOpenTypeTable is associated with one gfxFontEntry that
+// corresponds to an Open Type font with a MATH table. All the glyphs come from
+// the same font and the calls to access size variants and parts are directly
+// forwarded to the gfx code.
+class nsOpenTypeTable MOZ_FINAL : public nsGlyphTable {
+public:
+  ~nsOpenTypeTable()
+  {
+    MOZ_COUNT_DTOR(nsOpenTypeTable);
+  }
+
+  virtual nsGlyphCode ElementAt(gfxContext*   aThebesContext,
+                                int32_t       aAppUnitsPerDevPixel,
+                                gfxFontGroup* aFontGroup,
+                                char16_t      aChar,
+                                bool          aVertical,
+                                uint32_t      aPosition) MOZ_OVERRIDE;
+  virtual nsGlyphCode BigOf(gfxContext*   aThebesContext,
+                            int32_t       aAppUnitsPerDevPixel,
+                            gfxFontGroup* aFontGroup,
+                            char16_t      aChar,
+                            bool          aVertical,
+                            uint32_t      aSize) MOZ_OVERRIDE;
+  virtual bool HasPartsOf(gfxContext*   aThebesContext,
+                          int32_t       aAppUnitsPerDevPixel,
+                          gfxFontGroup* aFontGroup,
+                          char16_t      aChar,
+                          bool          aVertical) MOZ_OVERRIDE;
+
+  const nsAString&
+  FontNameFor(const nsGlyphCode& aGlyphCode) const MOZ_OVERRIDE {
+    NS_ASSERTION(aGlyphCode.IsGlyphID(),
+                 "nsOpenTypeTable can only access glyphs by id");
+    return mFontEntry->FamilyName();
+  }
+
+  virtual gfxTextRun* MakeTextRun(gfxContext*        aThebesContext,
+                                  int32_t            aAppUnitsPerDevPixel,
+                                  gfxFontGroup*      aFontGroup,
+                                  const nsGlyphCode& aGlyph) MOZ_OVERRIDE;
+
+  // This returns a new OpenTypeTable instance to give access to OpenType MATH
+  // table or nullptr if the font does not have such table. Ownership is passed
+  // to the caller.
+  static nsOpenTypeTable* Create(gfxFont* aFont)
+  {
+    if (!aFont->GetFontEntry()->TryGetMathTable(aFont)) {
+      return nullptr;
+    }
+    return new nsOpenTypeTable(aFont->GetFontEntry());
+  }
+
+private:
+  nsRefPtr<gfxFontEntry> mFontEntry;
+  uint32_t mGlyphID;
+
+  explicit nsOpenTypeTable(gfxFontEntry* aFontEntry)
+    : mFontEntry(aFontEntry) {
+    MOZ_COUNT_CTOR(nsOpenTypeTable);
+  }
+
+  void UpdateCache(gfxContext*   aThebesContext,
+                   int32_t       aAppUnitsPerDevPixel,
+                   gfxFontGroup* aFontGroup,
+                   char16_t      aChar);
+};
+
+void
+nsOpenTypeTable::UpdateCache(gfxContext*   aThebesContext,
+                             int32_t       aAppUnitsPerDevPixel,
+                             gfxFontGroup* aFontGroup,
+                             char16_t      aChar)
+{
+  if (mCharCache != aChar) {
+    nsAutoPtr<gfxTextRun> textRun;
+    textRun = aFontGroup->
+      MakeTextRun(&aChar, 1, aThebesContext, aAppUnitsPerDevPixel, 0);
+    const gfxTextRun::CompressedGlyph& data = textRun->GetCharacterGlyphs()[0];
+    if (data.IsSimpleGlyph()) {
+      mGlyphID = data.GetSimpleGlyph();
+    } else if (data.GetGlyphCount() == 1) {
+      mGlyphID = textRun->GetDetailedGlyphs(0)->mGlyphID;
+    } else {
+      mGlyphID = 0;
+    }
+    mCharCache = aChar;
+  }
+}
+
+/* virtual */
+nsGlyphCode
+nsOpenTypeTable::ElementAt(gfxContext*   aThebesContext,
+                           int32_t       aAppUnitsPerDevPixel,
+                           gfxFontGroup* aFontGroup,
+                           char16_t      aChar,
+                           bool          aVertical,
+                           uint32_t      aPosition)
+{
+  UpdateCache(aThebesContext, aAppUnitsPerDevPixel, aFontGroup, aChar);
+
+  uint32_t parts[4];
+  if (!mFontEntry->GetMathVariantsParts(mGlyphID, aVertical, parts)) {
+    return kNullGlyph;
+  }
+
+  uint32_t glyphID = parts[aPosition];
+  if (!glyphID) {
+    return kNullGlyph;
+  }
+  nsGlyphCode glyph;
+  glyph.glyphID = glyphID;
+  glyph.font = -1;
+  return glyph;
+}
+
+/* virtual */
+nsGlyphCode
+nsOpenTypeTable::BigOf(gfxContext*   aThebesContext,
+                       int32_t       aAppUnitsPerDevPixel,
+                       gfxFontGroup* aFontGroup,
+                       char16_t      aChar,
+                       bool          aVertical,
+                       uint32_t      aSize)
+{
+  UpdateCache(aThebesContext, aAppUnitsPerDevPixel, aFontGroup, aChar);
+
+  uint32_t glyphID =
+    mFontEntry->GetMathVariantsSize(mGlyphID, aVertical, aSize);
+  if (!glyphID) {
+    return kNullGlyph;
+  }
+
+  nsGlyphCode glyph;
+  glyph.glyphID = glyphID;
+  glyph.font = -1;
+  return glyph;
+}
+
+/* virtual */
+bool
+nsOpenTypeTable::HasPartsOf(gfxContext*   aThebesContext,
+                            int32_t       aAppUnitsPerDevPixel,
+                            gfxFontGroup* aFontGroup,
+                            char16_t      aChar,
+                            bool          aVertical)
+{
+  UpdateCache(aThebesContext, aAppUnitsPerDevPixel, aFontGroup, aChar);
+
+  uint32_t parts[4];
+  if (!mFontEntry->GetMathVariantsParts(mGlyphID, aVertical, parts)) {
+    return false;
+  }
+
+  return parts[0] || parts[1] || parts[2] || parts[3];
+}
+
+/* virtual */
+gfxTextRun*
+nsOpenTypeTable::MakeTextRun(gfxContext*        aThebesContext,
+                             int32_t            aAppUnitsPerDevPixel,
+                             gfxFontGroup*      aFontGroup,
+                             const nsGlyphCode& aGlyph)
+{
+  NS_ASSERTION(aGlyph.IsGlyphID(),
+               "nsOpenTypeTable can only access glyphs by id");
+
+  gfxTextRunFactory::Parameters params = {
+    aThebesContext, nullptr, nullptr, nullptr, 0, aAppUnitsPerDevPixel
+  };
+  gfxTextRun* textRun = gfxTextRun::Create(&params, 1, aFontGroup, 0);
+  textRun->AddGlyphRun(aFontGroup->GetFontAt(0), gfxTextRange::kFontGroup, 0,
+                       false);
+  gfxTextRun::DetailedGlyph detailedGlyph;
+  detailedGlyph.mGlyphID = aGlyph.glyphID;
+  detailedGlyph.mAdvance =
+    NSToCoordRound(aAppUnitsPerDevPixel *
+                   aFontGroup->GetFontAt(0)->
+                   GetGlyphHAdvance(aThebesContext, aGlyph.glyphID));
+  detailedGlyph.mXOffset = detailedGlyph.mYOffset = 0;
+  gfxShapedText::CompressedGlyph g;
+  g.SetComplex(true, true, 1);
+  textRun->SetGlyphs(0, g, &detailedGlyph);
+
+  return textRun;
+}
+
 // -----------------------------------------------------------------------------
 // This is the list of all the applicable glyph tables.
 // We will maintain a single global instance that will only reveal those
 // glyph tables that are associated to fonts currently installed on the
 // user' system. The class is an XPCOM shutdown observer to allow us to
 // free its allocated data at shutdown
 
 class nsGlyphTableList : public nsIObserver
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
 
-  nsGlyphTable mUnicodeTable;
+  nsPropertiesTable mUnicodeTable;
 
   nsGlyphTableList()
     : mUnicodeTable(NS_LITERAL_STRING("Unicode"))
   {
     MOZ_COUNT_CTOR(nsGlyphTableList);
   }
 
   virtual ~nsGlyphTableList()
@@ -331,25 +587,24 @@ public:
   nsGlyphTable*
   AddGlyphTable(const nsString& aPrimaryFontName);
 
   // Find the glyph table in the list corresponding to the given font family.
   nsGlyphTable*
   GetGlyphTableFor(const nsAString& aFamily);
 
 private:
-  nsGlyphTable* TableAt(int32_t aIndex) {
-    return &mTableList.ElementAt(aIndex);
+  nsPropertiesTable* PropertiesTableAt(int32_t aIndex) {
+    return &mPropertiesTableList.ElementAt(aIndex);
   }
-  int32_t Count() {
-    return mTableList.Length();
+  int32_t PropertiesTableCount() {
+    return mPropertiesTableList.Length();
   }
-
   // List of glyph tables;
-  nsTArray<nsGlyphTable> mTableList;
+  nsTArray<nsPropertiesTable> mPropertiesTableList;
 };
 
 NS_IMPL_ISUPPORTS1(nsGlyphTableList, nsIObserver)
 
 // -----------------------------------------------------------------------------
 // Here is the global list of applicable glyph tables that we will be using
 static nsGlyphTableList* gGlyphTableList = nullptr;
 
@@ -400,25 +655,25 @@ nsGlyphTable*
 nsGlyphTableList::AddGlyphTable(const nsString& aPrimaryFontName)
 {
   // See if there is already a special table for this family.
   nsGlyphTable* glyphTable = GetGlyphTableFor(aPrimaryFontName);
   if (glyphTable != &mUnicodeTable)
     return glyphTable;
 
   // allocate a table
-  glyphTable = mTableList.AppendElement(aPrimaryFontName);
+  glyphTable = mPropertiesTableList.AppendElement(aPrimaryFontName);
   return glyphTable;
 }
 
 nsGlyphTable*
 nsGlyphTableList::GetGlyphTableFor(const nsAString& aFamily)
 {
-  for (int32_t i = 0; i < Count(); i++) {
-    nsGlyphTable* glyphTable = TableAt(i);
+  for (int32_t i = 0; i < PropertiesTableCount(); i++) {
+    nsPropertiesTable* glyphTable = PropertiesTableAt(i);
     const nsAString& fontName = glyphTable->PrimaryFontName();
     // TODO: would be nice to consider StripWhitespace and other aliasing
     if (fontName.Equals(aFamily, nsCaseInsensitiveStringComparator())) {
       return glyphTable;
     }
   }
   // Fall back to default Unicode table
   return &mUnicodeTable;
@@ -925,18 +1180,22 @@ public:
       mTryVariants(true),
       mTryParts(true),
       mGlyphFound(aGlyphFound) {}
 
   static bool
   EnumCallback(const nsString& aFamily, bool aGeneric, void *aData);
 
 private:
-  bool TryVariants(nsGlyphTable* aGlyphTable, const nsAString& aFamily);
-  bool TryParts(nsGlyphTable* aGlyphTable, const nsAString& aFamily);
+  bool TryVariants(nsGlyphTable* aGlyphTable,
+                   nsRefPtr<gfxFontGroup>* aFontGroup,
+                   const nsAString& aFamily);
+  bool TryParts(nsGlyphTable* aGlyphTable,
+                nsRefPtr<gfxFontGroup>* aFontGroup,
+                const nsAString& aFamily);
 
   nsMathMLChar* mChar;
   nsPresContext* mPresContext;
   gfxContext* mThebesContext;
   const nsStretchDirection mDirection;
   const nscoord mTargetSize;
   const uint32_t mStretchHint;
   nsBoundingMetrics& mBoundingMetrics;
@@ -952,60 +1211,111 @@ private:
   bool&       mGlyphFound;
 };
 
 
 // 2. See if there are any glyphs of the appropriate size.
 // Returns true if the size is OK, false to keep searching.
 // Always updates the char if a better match is found.
 bool
-nsMathMLChar::StretchEnumContext::TryVariants(nsGlyphTable*    aGlyphTable,
-                                              const nsAString& aFamily)
+nsMathMLChar::
+StretchEnumContext::TryVariants(nsGlyphTable* aGlyphTable,
+                                nsRefPtr<gfxFontGroup>* aFontGroup,
+                                const nsAString& aFamily)
 {
   // Use our stretchy style context now that stretching is in progress
   nsStyleContext *sc = mChar->mStyleContext;
   nsFont font = sc->StyleFont()->mFont;
-  // Ensure SetFontFamily will set the font
-  font.name.Truncate();
 
   bool isVertical = (mDirection == NS_STRETCH_DIRECTION_VERTICAL);
+  nscoord oneDevPixel = mPresContext->AppUnitsPerDevPixel();
+  char16_t uchar = mChar->mData[0];
   bool largeop = (NS_STRETCH_LARGEOP & mStretchHint) != 0;
   bool largeopOnly =
     largeop && (NS_STRETCH_VARIABLE_MASK & mStretchHint) == 0;
   bool maxWidth = (NS_STRETCH_MAXWIDTH & mStretchHint) != 0;
 
   nscoord bestSize =
     isVertical ? mBoundingMetrics.ascent + mBoundingMetrics.descent
                : mBoundingMetrics.rightBearing - mBoundingMetrics.leftBearing;
   bool haveBetter = false;
 
   // start at size = 1 (size = 0 is the char at its normal size)
   int32_t size = 1;
+  nsGlyphCode ch;
+  nscoord displayOperatorMinHeight = 0;
+  if (largeopOnly) {
+    NS_ASSERTION(isVertical, "Stretching should be in the vertical direction");
+    ch = aGlyphTable->BigOf(mThebesContext, oneDevPixel, *aFontGroup, uchar,
+                            isVertical, 0);
+    if (ch.IsGlyphID()) {
+      gfxFont* mathFont = aFontGroup->get()->GetFontAt(0);
+      // For OpenType MATH fonts, we will rely on the DisplayOperatorMinHeight
+      // to select the right size variant. Note that the value is sometimes too
+      // small so we use kLargeOpFactor/kIntegralFactor as a minimum value.
+      displayOperatorMinHeight =
+        NSToCoordRound(mathFont->GetFontEntry()->
+                       GetMathConstant(gfxFontEntry::DisplayOperatorMinHeight) *
+                       mathFont->GetAdjustedSize() * oneDevPixel);
+      nsAutoPtr<gfxTextRun> textRun;
+      textRun = aGlyphTable->MakeTextRun(mThebesContext, oneDevPixel,
+                                         *aFontGroup, ch);
+      nsBoundingMetrics bm = MeasureTextRun(mThebesContext, textRun);
+      float largeopFactor = kLargeOpFactor;
+      if (NS_STRETCH_INTEGRAL & mStretchHint) {
+        // integrals are drawn taller
+        largeopFactor = kIntegralFactor;
+      }
+      nscoord minHeight = largeopFactor * (bm.ascent + bm.descent);
+      if (displayOperatorMinHeight < minHeight) {
+        displayOperatorMinHeight = minHeight;
+      }
+    }
+  }
 #ifdef NOISY_SEARCH
   printf("  searching in %s ...\n",
            NS_LossyConvertUTF16toASCII(aFamily).get());
 #endif
-
-  nsGlyphCode ch;
-  nsRefPtr<gfxFontGroup> fontGroup;
-  while ((ch = aGlyphTable->BigOf(mChar->mData[0], size)).Exists()) {
+  while ((ch = aGlyphTable->BigOf(mThebesContext, oneDevPixel, *aFontGroup,
+                                  uchar, isVertical, size)).Exists()) {
 
     if (!mChar->SetFontFamily(mPresContext, aGlyphTable, ch, aFamily, font,
-                              &fontGroup)) {
+                              aFontGroup)) {
       // if largeopOnly is set, break now
       if (largeopOnly) break;
       ++size;
       continue;
     }
 
     nsAutoPtr<gfxTextRun> textRun;
-    textRun = aGlyphTable->MakeTextRun(mThebesContext,
-                                       mPresContext->AppUnitsPerDevPixel(),
-                                       fontGroup, ch);
+    textRun = aGlyphTable->MakeTextRun(mThebesContext, oneDevPixel,
+                                       *aFontGroup, ch);
     nsBoundingMetrics bm = MeasureTextRun(mThebesContext, textRun);
+    if (ch.IsGlyphID()) {
+      gfxFont* mathFont = aFontGroup->get()->GetFontAt(0);
+      if (mathFont->GetFontEntry()->TryGetMathTable(mathFont)) {
+        // MeasureTextRun should have set the advance width to the right
+        // bearing for OpenType MATH fonts. We now subtract the italic
+        // correction, so that nsMathMLmmultiscripts will place the scripts
+        // correctly.
+        // Note that STIX-Word does not provide italic corrections but its
+        // advance widths do not match right bearings.
+        // (http://sourceforge.net/p/stixfonts/tracking/50/)
+        gfxFloat italicCorrection;
+        if (mathFont->GetFontEntry()->
+            GetMathItalicsCorrection(ch.glyphID, &italicCorrection)) {
+          bm.width -=
+            NSToCoordRound(italicCorrection *
+                           mathFont->GetAdjustedSize() * oneDevPixel);
+          if (bm.width < 0) {
+            bm.width = 0;
+          }
+        }
+      }
+    }
 
     nscoord charSize =
       isVertical ? bm.ascent + bm.descent
       : bm.rightBearing - bm.leftBearing;
 
     if (largeopOnly ||
         IsSizeBetter(charSize, bestSize, mTargetSize, mStretchHint)) {
       mGlyphFound = true;
@@ -1035,64 +1345,64 @@ nsMathMLChar::StretchEnumContext::TryVar
     else {
 #ifdef NOISY_SEARCH
       printf("    size:%d Rejected!\n", size);
 #endif
       if (haveBetter)
         break; // Not making an futher progress, stop searching
     }
 
-    // if largeopOnly is set, break now
-    if (largeopOnly) break;
+    // If this a largeop only operator, we stop if the glyph is large enough.
+    if (largeopOnly && (bm.ascent + bm.descent) >= displayOperatorMinHeight) {
+      break;
+    }
     ++size;
   }
 
   return haveBetter &&
     (largeopOnly ||
      IsSizeOK(mPresContext, bestSize, mTargetSize, mStretchHint));
 }
 
 // 3. Build by parts.
 // Returns true if the size is OK, false to keep searching.
 // Always updates the char if a better match is found.
 bool
-nsMathMLChar::StretchEnumContext::TryParts(nsGlyphTable*    aGlyphTable,
+nsMathMLChar::StretchEnumContext::TryParts(nsGlyphTable* aGlyphTable,
+                                           nsRefPtr<gfxFontGroup>* aFontGroup,
                                            const nsAString& aFamily)
 {
-  if (!aGlyphTable->HasPartsOf(mChar->mData[0]))
-    return false; // to next table
-
-  // See if the parts of this table fit in the desired space //////////////////
-
   // Use our stretchy style context now that stretching is in progress
   nsFont font = mChar->mStyleContext->StyleFont()->mFont;
-  // Ensure SetFontFamily will set the font
-  font.name.Truncate();
 
   // Compute the bounding metrics of all partial glyphs
   nsAutoPtr<gfxTextRun> textRun[4];
   nsGlyphCode chdata[4];
   nsBoundingMetrics bmdata[4];
   nscoord sizedata[4];
 
   bool isVertical = (mDirection == NS_STRETCH_DIRECTION_VERTICAL);
+  nscoord oneDevPixel = mPresContext->AppUnitsPerDevPixel();
+  char16_t uchar = mChar->mData[0];
   bool maxWidth = (NS_STRETCH_MAXWIDTH & mStretchHint) != 0;
-  nsRefPtr<gfxFontGroup> fontGroup;
+  if (!aGlyphTable->HasPartsOf(mThebesContext, oneDevPixel, *aFontGroup,
+                               uchar, isVertical))
+    return false; // to next table
 
   for (int32_t i = 0; i < 4; i++) {
-    nsGlyphCode ch = aGlyphTable->ElementAt(mChar->mData[0], i);
+    nsGlyphCode ch = aGlyphTable->ElementAt(mThebesContext, oneDevPixel,
+                                            *aFontGroup, uchar, isVertical, i);
     chdata[i] = ch;
     if (ch.Exists()) {
       if (!mChar->SetFontFamily(mPresContext, aGlyphTable, ch, aFamily, font,
-                                &fontGroup))
+                                aFontGroup))
         return false;
 
-      textRun[i] = aGlyphTable->MakeTextRun(mThebesContext,
-                                            mPresContext->AppUnitsPerDevPixel(),
-                                            fontGroup, ch);
+      textRun[i] = aGlyphTable->MakeTextRun(mThebesContext, oneDevPixel,
+                                            *aFontGroup, ch);
       nsBoundingMetrics bm = MeasureTextRun(mThebesContext, textRun[i]);
 
       // TODO: For the generic Unicode table, ideally we should check that the
       // glyphs are actually found and that they each come from the same
       // font.
       bmdata[i] = bm;
       sizedata[i] = isVertical ? bm.ascent + bm.descent
                                : bm.rightBearing - bm.leftBearing;
@@ -1202,48 +1512,61 @@ nsMathMLChar::StretchEnumContext::TryPar
 
 // This is called for each family, whether it exists or not
 bool
 nsMathMLChar::StretchEnumContext::EnumCallback(const nsString& aFamily,
                                                bool aGeneric, void *aData)
 {
   StretchEnumContext* context = static_cast<StretchEnumContext*>(aData);
 
-  // See if there is a special table for the family, but always use the
-  // Unicode table for generic fonts.
-  nsGlyphTable* glyphTable = aGeneric ?
-    &gGlyphTableList->mUnicodeTable :
-    gGlyphTableList->GetGlyphTableFor(aFamily);
-
-  if (context->mTablesTried.Contains(glyphTable))
-    return true; // already tried this one
-
   // Check font family if it is not a generic one
   // We test with the kNullGlyph
   nsStyleContext *sc = context->mChar->mStyleContext;
   nsFont font = sc->StyleFont()->mFont;
   nsRefPtr<gfxFontGroup> fontGroup;
   if (!aGeneric && !context->mChar->SetFontFamily(context->mPresContext,
                                                   nullptr, kNullGlyph, aFamily,
                                                   font, &fontGroup))
      return true; // Could not set the family
 
-  // Now see if the table has a glyph that matches the container
+  // Determine the glyph table to use for this font.
+  nsAutoPtr<nsOpenTypeTable> openTypeTable;
+  nsGlyphTable* glyphTable;
+  if (aGeneric) {
+    // This is a generic font, use the Unicode table.
+    glyphTable = &gGlyphTableList->mUnicodeTable;
+  } else {
+    // If the font contains an Open Type MATH table, use it.
+    openTypeTable = nsOpenTypeTable::Create(fontGroup->GetFontAt(0));
+    if (openTypeTable) {
+      glyphTable = openTypeTable;
+    } else {
+      // Otherwise try to find a .properties file corresponding to that font
+      // family or fallback to the Unicode table.
+      glyphTable = gGlyphTableList->GetGlyphTableFor(aFamily);
+    }
+  }
 
-  // Only try this table once.
-  context->mTablesTried.AppendElement(glyphTable);
+  if (!openTypeTable) {
+    if (context->mTablesTried.Contains(glyphTable))
+      return true; // already tried this one
+    
+    // Only try this table once.
+    context->mTablesTried.AppendElement(glyphTable);
+  }
 
   // If the unicode table is being used, then search all font families.  If a
   // special table is being used then the font in this family should have the
   // specified glyphs.
   const nsAString& family = glyphTable == &gGlyphTableList->mUnicodeTable ?
     context->mFamilies : aFamily;
 
-  if((context->mTryVariants && context->TryVariants(glyphTable, family)) ||
-     (context->mTryParts && context->TryParts(glyphTable, family)))
+  if((context->mTryVariants &&
+      context->TryVariants(glyphTable, &fontGroup, family)) ||
+     (context->mTryParts && context->TryParts(glyphTable, &fontGroup, family)))
     return false; // no need to continue
 
   return true; // true means continue
 }
 
 nsresult
 nsMathMLChar::StretchInternal(nsPresContext*           aPresContext,
                               gfxContext*              aThebesContext,
@@ -1469,17 +1792,17 @@ nsMathMLChar::StretchInternal(nsPresCont
       }
     }
   }
 
   // We do not have a char variant for this largeop in display mode, so we
   // apply a scale transform to the base char.
   if (!glyphFound && largeop) {
     float scale;
-    float largeopFactor = float(M_SQRT2);
+    float largeopFactor = kLargeOpFactor;
 
     // increase the width if it is not largeopFactor times larger
     // than the initial one.
     if ((aDesiredStretchSize.rightBearing - aDesiredStretchSize.leftBearing) <
         largeopFactor * (initialSize.rightBearing - initialSize.leftBearing)) {
       scale = (largeopFactor *
                (initialSize.rightBearing - initialSize.leftBearing)) /
         (aDesiredStretchSize.rightBearing - aDesiredStretchSize.leftBearing);
@@ -1490,17 +1813,17 @@ nsMathMLChar::StretchInternal(nsPresCont
       aDesiredStretchSize.rightBearing *= scale;
       aDesiredStretchSize.width *= scale;
     }
 
     // increase the height if it is not largeopFactor times larger
     // than the initial one.
     if (NS_STRETCH_INTEGRAL & aStretchHint) {
       // integrals are drawn taller
-      largeopFactor = 2.0;
+      largeopFactor = kIntegralFactor;
     }
     if ((aDesiredStretchSize.ascent + aDesiredStretchSize.descent) <
         largeopFactor * (initialSize.ascent + initialSize.descent)) {
       scale = (largeopFactor *
                (initialSize.ascent + initialSize.descent)) /
         (aDesiredStretchSize.ascent + aDesiredStretchSize.descent);
       if (!maxWidth) {
         mScaleY *= scale;
--- a/layout/mathml/nsMathMLChar.h
+++ b/layout/mathml/nsMathMLChar.h
@@ -194,16 +194,18 @@ public:
   // the Get/Set AdditionalStyleContext() APIs. Owners of MathMLChars
   // should honor these APIs.
   nsStyleContext* GetStyleContext() const;
 
   void SetStyleContext(nsStyleContext* aStyleContext);
 
 protected:
   friend class nsGlyphTable;
+  friend class nsPropertiesTable;
+  friend class nsOpenTypeTable;
   nsString           mData;
 
 private:
   nsRect             mRect;
   nsStretchDirection mDirection;
   nsBoundingMetrics  mBoundingMetrics;
   nsStyleContext*    mStyleContext;
   // mGlyphs/mBmData are arrays describing the glyphs used to draw the operator.
--- a/layout/reftests/first-letter/reftest.list
+++ b/layout/reftests/first-letter/reftest.list
@@ -30,17 +30,17 @@ random-if(d2d) == dynamic-2.html dynamic
 == 23605-2.html 23605-2-ref.html
 == 23605-3.html 23605-3-ref.html
 == 23605-4.html 23605-4-ref.html
 == 23605-5.html 23605-5-ref.html
 == 23605-6.html 23605-6-ref.html
 != 229764-1.html 229764-ref.html
 == 229764-2.html 229764-ref.html
 == 329069-1.html 329069-1-ref.html
-== 329069-2.html 329069-2-ref.html
+fails-if(Android&&AndroidVersion==10) == 329069-2.html 329069-2-ref.html # Bug 999139
 == 329069-3.html 329069-3-ref.html
 == 329069-4.html 329069-4-ref.html
 fails-if(!cocoaWidget) == 329069-5.html 329069-5-ref.html # bug 603710
 == 342120-1.xhtml 342120-1-ref.xhtml
 == 379799-1.html 379799-1-ref.html
 == 399941-1.html 399941-1-ref.html
 == 399941-2.html 399941-2-ref.html
 == 399941-3.html 399941-3-ref.html
--- a/layout/reftests/font-face/reftest.list
+++ b/layout/reftests/font-face/reftest.list
@@ -143,18 +143,18 @@ HTTP(..) != font-familiy-whitespace-1.ht
 skip-if(B2G) HTTP(..) == ivs-1.html ivs-1-ref.html # bug 773482
 skip-if(B2G) HTTP(..) == cjkcisvs-1.html cjkcisvs-1-ref.html
 
 skip-if(B2G) HTTP(..) == missing-names.html missing-names-ref.html # bug 773482
 
 # Tests for bug 670900 - handling of 404 (not found) error in @font-face URL
 # (using Chunkfive font data returned from a .sjs file)
 HTTP(..) == font-error-404-1.html font-error-404-1-ref.html # HTTP status 404, don't load
-skip-if(B2G) fails-if(Android&&AndroidVersion==17) HTTP(..) == font-error-404-2.html font-error-404-2-ref.html # HTTP status 200, load # bug 773482
-fails-if(Android&&AndroidVersion==17) HTTP(..) != font-error-404-1.html font-error-404-2.html # sanity-check that the results differ
+skip-if(B2G) fails-if(Android&&(AndroidVersion==10||AndroidVersion==17)) HTTP(..) == font-error-404-2.html font-error-404-2-ref.html # HTTP status 200, load # bug 773482
+fails-if(Android&&(AndroidVersion==10||AndroidVersion==17)) HTTP(..) != font-error-404-1.html font-error-404-2.html # sanity-check that the results differ
 
 # Font load redirection
 HTTP(..) == font-redirect.html order-1-ref.html
 
 # Tests for potential regressions from bug 879963
 == dynamic-duplicate-rule-1a.html dynamic-duplicate-rule-1-ref.html
 == dynamic-duplicate-rule-1b.html dynamic-duplicate-rule-1-ref.html
 == dynamic-duplicate-rule-1c.html dynamic-duplicate-rule-1-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/fonts/math/README
@@ -0,0 +1,6 @@
+The fonts in this directory are autogenerated with FontForge using the Python
+script generate.py. See the comments in that file for more information on how
+to run the script.
+
+These fonts are intended to test the The MATH table and OpenType Features used
+in MathML. See layout/reftests/mathml/
new file mode 100644
--- /dev/null
+++ b/layout/reftests/fonts/math/generate.py
@@ -0,0 +1,231 @@
+#!/usr/bin/python
+# vim: set shiftwidth=4 tabstop=8 autoindent expandtab:
+# 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/.
+
+# For general fontforge documentation, see:
+#   http://fontforge.sourceforge.net/
+# For fontforge scripting documentation, see:
+#   http://fontforge.sourceforge.net/scripting-tutorial.html
+#   http://fontforge.sourceforge.net/scripting.html
+# and most importantly:
+#   http://fontforge.sourceforge.net/python.html
+
+# To install what you need, on Ubuntu,
+#   sudo apt-get install python-fontforge
+
+from __future__ import print_function
+import fontforge
+
+em = 1000
+
+def newMathFont(aName):
+    print("Generating %s.otf..." % aName, end="")
+    mathFont = fontforge.font()
+    mathFont.fontname = aName
+    mathFont.familyname = aName
+    mathFont.fullname = aName
+    mathFont.copyright = "Copyright (c) 2014 Mozilla Corporation"
+    mathFont.encoding = "UnicodeFull"
+
+    # Create a space character. Also force the creation of some MATH subtables
+    # so that OTS will not reject the MATH table.
+    g = mathFont.createChar(ord(" "), "space")
+    g.width = em
+    g.italicCorrection = 0
+    g.topaccent = 0
+    g.mathKern.bottomLeft = tuple([(0,0)])
+    g.mathKern.bottomRight = tuple([(0,0)])
+    g.mathKern.topLeft = tuple([(0,0)])
+    g.mathKern.topRight = tuple([(0,0)])
+    mathFont[ord(" ")].horizontalVariants = "space"
+    mathFont[ord(" ")].verticalVariants = "space"
+    return mathFont
+
+def saveMathFont(aFont):
+    aFont.em = em
+    aFont.ascent = aFont.descent = em/2
+    aFont.hhea_ascent = aFont.os2_typoascent = aFont.os2_winascent = em/2
+    aFont.descent = aFont.hhea_descent = em/2
+    aFont.os2_typodescent = aFont.os2_windescent = em/2
+    aFont.hhea_ascent_add = aFont.hhea_descent_add = 0
+    aFont.os2_typoascent_add = aFont.os2_typodescent_add = 0
+    aFont.os2_winascent_add = aFont.os2_windescent_add = 0
+    aFont.os2_use_typo_metrics = True
+    aFont.generate(aFont.fontname + ".otf")
+    print(" done.")
+
+################################################################################
+# Glyph variants and constructions
+f = newMathFont("stretchy")
+nvariants = 3
+
+# Draw boxes for the size variants and glues
+for i in range(0, nvariants):
+    s = em * (i + 1)
+
+    g = f.createChar(-1, "h%d" % i)
+    p = g.glyphPen()
+    p.moveTo(0, -em)
+    p.lineTo(0, em)
+    p.lineTo(s, em)
+    p.lineTo(s, -em)
+    p.closePath()
+    g.width = s
+
+    g = f.createChar(-1, "v%d" % i)
+    p = g.glyphPen()
+    p.moveTo(0, 0)
+    p.lineTo(0, s)
+    p.lineTo(2 * em, s)
+    p.lineTo(2 * em, 0)
+    p.closePath();
+    g.width = 2 * em
+
+# Draw some pieces for stretchy operators
+s = em * nvariants
+
+g = f.createChar(-1, "left")
+p = g.glyphPen()
+p.moveTo(0, -2 * em)
+p.lineTo(0, 2 * em)
+p.lineTo(s, em)
+p.lineTo(s, -em)
+p.closePath();
+g.width = s
+
+g = f.createChar(-1, "right")
+p = g.glyphPen()
+p.moveTo(0, -em)
+p.lineTo(0, em)
+p.lineTo(s, 2 * em)
+p.lineTo(s, -2 * em)
+p.closePath();
+g.width = s
+
+g = f.createChar(-1, "hmid")
+p = g.glyphPen()
+p.moveTo(0, -em)
+p.lineTo(0, em)
+p.lineTo(s, 2 * em)
+p.lineTo(2 * s, em)
+p.lineTo(2 * s, -em)
+p.lineTo(s, -2 * em)
+p.closePath();
+g.width = 2 * s
+
+g = f.createChar(-1, "bottom")
+p = g.glyphPen()
+p.moveTo(0, 0)
+p.lineTo(0, s)
+p.lineTo(2 * em, s)
+p.lineTo(4 * em, 0)
+p.closePath();
+g.width = 4 * em
+
+g = f.createChar(-1, "top")
+p = g.glyphPen()
+p.moveTo(0, 0)
+p.lineTo(4 * em, 0)
+p.lineTo(2 * em, -s)
+p.lineTo(0, -s)
+p.closePath();
+g.width = 4 * em
+
+g = f.createChar(-1, "vmid")
+p = g.glyphPen()
+p.moveTo(0, s)
+p.lineTo(2 * em, s)
+p.lineTo(4 * em, 0)
+p.lineTo(2 * em, -s)
+p.lineTo(0, -s)
+p.closePath();
+g.width = 3 * em
+
+# Create small rectangle of various size for some exotic arrows that are
+# unlikely to be stretchable with standard fonts.
+hstretchy = [
+    0x219C, # leftwards wave arrow
+    0x219D, # rightwards wave arrow
+    0x219E, # leftwards two headed arrow
+    0x21A0, # rightwards two headed arrow
+    0x21A2  # leftwards arrow with tail
+]
+vstretchy = [
+    0x219F, # upwards two headed arrow
+    0x21A1, # downwards two headed arrow
+    0x21A5, # upwards arrow from bar
+    0x21A7, # downwards arrow from bar
+    0x21A8  # up down arrow with base
+]
+for i in range(0, 1 + nvariants + 1):
+    s = (i + 1) * em/10
+
+    g = f.createChar(hstretchy[i])
+    p = g.glyphPen()
+    p.moveTo(0, -em/10)
+    p.lineTo(0, em/10)
+    p.lineTo(s, em/10)
+    p.lineTo(s, -em/10)
+    p.closePath()
+    g.width = s
+
+    g = f.createChar(vstretchy[i])
+    p = g.glyphPen()
+    p.moveTo(0, 0)
+    p.lineTo(0, s)
+    p.lineTo(2 * em/10, s)
+    p.lineTo(2 * em/10, 0)
+    p.closePath();
+    g.width = 2 * em/10
+
+# hstretchy[0] and vstretchy[0] have all the variants and the components. The others only have one of them.
+s = em * nvariants
+
+f[hstretchy[0]].horizontalVariants = "uni219C h0 h1 h2"
+f[hstretchy[0]].horizontalComponents = (("left", False, 0, 0, s), \
+("h2", True, 0, 0, s), ("hmid", False, 0, 0, 2 * s), ("h2", True, 0, 0, s), \
+("right", False, 0, 0, s))
+
+f[hstretchy[1]].horizontalVariants = "uni219D h0"
+f[hstretchy[2]].horizontalVariants = "uni219E h1"
+f[hstretchy[3]].horizontalVariants = "uni21A0 h2"
+f[hstretchy[4]].horizontalVariants = "uni21A2 h2"
+f[hstretchy[4]].horizontalComponents = f[hstretchy[0]].horizontalComponents
+
+f[vstretchy[0]].verticalVariants = "uni219F v0 v1 v2"
+f[vstretchy[0]].verticalComponents = (("bottom", False, 0, 0, s), \
+("v2", True, 0, 0, s), ("vmid", False, 0, 0, 2 * s), ("v2", True, 0, 0, s), \
+("top", False, 0, 0, s))
+
+f[vstretchy[1]].verticalVariants = "uni21A1 v0"
+f[vstretchy[2]].verticalVariants = "uni21A5 v1"
+f[vstretchy[3]].verticalVariants = "uni21A7 v2"
+f[vstretchy[4]].verticalVariants = "uni21A8"
+f[vstretchy[4]].verticalComponents = f[vstretchy[0]].verticalComponents
+
+################################################################################
+# Testing DisplayOperatorMinHeight
+f.math.DisplayOperatorMinHeight =  8 * em
+largeop = [0x2A1B, 0x2A1C] # integral with overbar/underbar
+
+# Draw boxes of size 1, 2, 7, 8, 9em.
+for i in [1, 2, 7, 8, 9]:
+    s = em * i
+    if i == 1 or i == 2:
+        g = f.createChar(largeop[i-1])
+    else:
+        g = f.createChar(-1, "L%d" % i)
+    p = g.glyphPen()
+    p.moveTo(0, 0)
+    p.lineTo(0, s)
+    p.lineTo(s, s)
+    p.lineTo(s, 0)
+    p.closePath();
+    g.width = s
+
+f[largeop[0]].verticalVariants = "uni2A1B L7 L8 L9"
+f[largeop[1]].verticalVariants = "uni2A1C L8"
+
+saveMathFont(f)
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..f192de346316ee35c7da99356a83361b28ed7b61
GIT binary patch
literal 3336
zc%0o=Yiv_x7=GXOw7Yb49k7e;jGf7V0dBMnGA<(-Oc#D^4ulXS4bp9ePPeso-MFL}
zq6Fp+%2m7}P7-uKh$e;zB+NAWLnJ1QA0!$HfoKGOn8i3~DX{r`=bUyIB4YUG+4;Wr
zeV^}n-}gJ;t=}5ftZ5({?IbffYieti^4mwuL>4EJw6wN%P5monr~8O9f<z?OFI%(X
zx5MpAVDBX|r#Gyg=g#@2Y9<jYXy4e><_*?#HC#d4NnnkBpSQWh-Bymab0`b^0A_40
zc>($mN~ymsyp;!_QPyEp;WqEqAQi2I$_1#b?cO$@<H(_3VDBU{%?$=R!XvxMjs7{%
zt`G|<QQUDb`cih)EwY(!if9)*ds;{3(2D70)5~C{lZo^PK0L%jv|ix%3Q#kRe~O4!
zYBfn3(}MgxCh-+}b;;Mr#fWDjt2yN*qHG)&CrZj3S<j`_G$xZ`e1V-lPoXpEc|&LP
zcaNbX2^}+ZGnLYyp-Ys`GY#EB4qj#GR^VYUS;~M_r+EpT$w8flZlWyOZ|G)PK_?7d
zLY%3FZXt!|8M+mCBh^rVg0zi7)IyudPhosR88y*tNH;mjMf0K6gS8!uR{VKk3$GCT
zMD2x73$%8knm}+{sAaQ1tdup)R@_e4e5F3Hy`{C)tJDNS!9d6xZV7<b0hbW@P&Xmv
zHUQy}FWlsZRu8xt@j9suhOfD$6S7tdAJ$|D-Slalt3dY@T8uI=o{qYxlFHHj^3;b=
zZJ<3|8whRoDeelVvRFyxh3cv-pI<Jz{Lt>L1snoF|6m1iMdS)iir&y#U#J7$s5qUj
z3a8VlJT#g%k9v`fnf8d;(QJvT#YWOrxkO6I@sblIeRJfI5wX%rj>7tEPT|PN$n~q6
zt~Ot7y8P$FHP2RhU>i(){;mCg@EE&!<PCAoJoqq45;1c|YFcm#Fjd5}DZYT?m8SSY
zb39*C<)Ibw^fJ5_nQO9TB8SY89GRYyCEH}0X43X|Hs#@rDW$o{wFvW9N2_T)=5zy2
zkw&cRP4qGrP6t*&1Sj<Cw2OArNjgR6I@?>^u0=JvcvctB>7rH>%bdD!>B6mxXLM2N
z4TS<*I)lyajU7G#Epsi`MUCIt<@CF{Tz+?#yVbWTtZh$!TT63eARG>~g#*DZ7<H9(
z3+om=+V)M3)7WLqyobB_P3~cp_wrl(Ht*y8e1H$~AwJCS@DYBO-{bfB1OAYY@<)7(
zKjvQkgo?zl&A@@P7(?ZyS1}}qad4fc^Yjz_LH9YE6?XG-UdQdc3jxmZCBA7gn~F@8
zrd4ucpV}YW(a-(;v1q@!KQCSyyMC|K+JE<p+}Ms-^zM!{`*4n3j*rZWc~~7Z&yogX
zo>^AAX2v|QW1g7juE%PZ?|i3&Q2|B)My+;rP-CfJ4QiysTtS*8A&&=n?5Yt*9gUON
zXHdF9=@yjeC?zT=y<I6FUOExz7Qk?hnust8utX#g8ID-(w^cc9fJFsR$<#UK;Rp!8
zbcS$}U;&Cd0Sre{J|MzrVvxiQoq|@`b_N<2Is8P2bdG?~AfhF1KnPkQqUy{<#G*wI
z;4r}KUVy^@Wrr<^j^`)a)0c=85<FA{)ibCXu5J-Hwp!Xinx0(bfRN;y`$s|D3m#Mc
zQsu&REGp66!gb>PwGR`K|FbF=SF@-@OL4V$uVGf@!f#b<V-}TY(KfYw<wvVM=9wy|
z`Eu2)%tRz}aB%R>%B-wJ#HMSLu+^uCgCa|G0WSu{mOMi6{IK-+MIp!CRYXfBUE>N|
zKB_ENimU(A$#ve4vmb$b1l`#12t2pW)nrm16Js3i9#h8nb1u>S%h0QE;^je>LMHDx
ztet~ntx}XMnoI>W?H_xkGpF)bd;#88+?pzAIc`qtwG;C->Y;=5DSb&_(}fhf55I~Q
z{H|u3f2W+ZOEkrHmc-2}jlS0~p=WTp)+Y2!KA>^>w2TsEf%9bHi&R*=jO6LFHO?e(
zg`LcC3Oo~X*uiz6ZL*QWj2w0&hlCssBZn0^1m_g=biQTq#a%962-_J{Qw)vi38Cc&
z$6*C2-sAaAP1%!EoV=9ZD3<%!e)@b{Fzy*x>o&~wB-mCIF;0?pcf~zfyU90`wyODQ
mL+&@^kS>!&aMK1xWwMBTNhylxQ+BUjgk89fNbE83U%=n(=YQ`2
new file mode 100644
--- /dev/null
+++ b/layout/reftests/invalidation/filter-userspace-offset.svg
@@ -0,0 +1,156 @@
+<svg
+  width="500px" height="500px" viewBox="0 0 500 500"
+  xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
+  >
+  <title>Filters and offsets, user space origins, invalidation</title>
+  <defs>
+    <filter id="flood-boundingBox"
+            filterUnits="objectBoundingBox"
+            x="0%" y="0%" width="100%" height="100%"
+            color-interpolation-filters="sRGB">
+      <feFlood flood-color="lime"/>
+    </filter>
+    <filter id="matrix-boundingBox"
+            filterUnits="objectBoundingBox"
+            x="0%" y="0%" width="100%" height="100%"
+            color-interpolation-filters="sRGB">
+      <feColorMatrix type="matrix" values="0 1 0 0 0
+                                           1 0 0 0 0
+                                           0 0 1 0 0
+                                           0 0 0 1 0"/>
+    </filter>
+    <filter id="matrix-fillPaint-boundingBox"
+            filterUnits="objectBoundingBox"
+            x="0%" y="0%" width="100%" height="100%"
+            color-interpolation-filters="sRGB">
+      <feColorMatrix type="matrix" values="0 1 0 0 0
+                                           1 0 0 0 0
+                                           0 0 1 0 0
+                                           0 0 0 1 0" in="FillPaint"/>
+    </filter>
+    <filter id="flood-userSpace-atZero"
+            filterUnits="userSpaceOnUse"
+            x="0" y="0" width="100" height="100"
+            color-interpolation-filters="sRGB">
+      <feFlood flood-color="lime"/>
+    </filter>
+    <filter id="matrix-userSpace-atZero"
+            filterUnits="userSpaceOnUse"
+            x="0" y="0" width="100" height="100"
+            color-interpolation-filters="sRGB">
+      <feColorMatrix type="matrix" values="0 1 0 0 0
+                                           1 0 0 0 0
+                                           0 0 1 0 0
+                                           0 0 0 1 0"/>
+    </filter>
+    <filter id="flood-userSpace-at100"
+            filterUnits="userSpaceOnUse"
+            x="100" y="100" width="100" height="100"
+            color-interpolation-filters="sRGB">
+      <feFlood flood-color="lime"/>
+    </filter>
+    <filter id="matrix-userSpace-at100"
+            filterUnits="userSpaceOnUse"
+            x="100" y="100" width="100" height="100"
+            color-interpolation-filters="sRGB">
+      <feColorMatrix type="matrix" values="0 1 0 0 0
+                                           1 0 0 0 0
+                                           0 0 1 0 0
+                                           0 0 0 1 0"/>
+    </filter>
+    <filter id="matrix-fillPaint-userSpace-at100"
+            filterUnits="userSpaceOnUse"
+            x="100" y="100" width="100" height="100"
+            color-interpolation-filters="sRGB">
+      <feColorMatrix type="matrix" values="0 1 0 0 0
+                                           1 0 0 0 0
+                                           0 0 1 0 0
+                                           0 0 0 1 0" in="FillPaint"/>
+    </filter>
+    <mask id="boundingBox" maskContentUnits="objectBoundingBox">
+      <rect x="0" y="0" width="1" height="1" fill="white"/>
+    </mask>
+    <mask id="userSpace-atZero" maskContentUnits="userSpaceOnUse">
+      <rect x="0" y="0" width="100" height="100" fill="white"/>
+    </mask>
+    <mask id="userSpace-at100" maskContentUnits="userSpaceOnUse">
+      <rect x="100" y="100" width="100" height="100" fill="white"/>
+    </mask>
+    <g id="usedRect">
+      <rect class="fillColor" width="100" height="100"/>
+    </g>
+  </defs>
+  <g transform="translate(40,40)">
+    <rect stroke-width="1" stroke="black" fill="none" x="99.5" y="99.5" width="101" height="101"/>
+
+    <rect x="0" y="100" width="100" height="100" class="fillColor offsetContainer" id="rect"/>
+    <use xlink:href="#usedRect" x="0" y="100" class="offsetContainer" id="use"/>
+    <svg x="0" y="100" width="100" height="100" class="offsetContainer" id="innerSVG">
+      <rect class="fillColor" width="100" height="100"/>
+    </svg>
+    <foreignObject x="0" y="100" width="100" height="100" class="offsetContainer" id="foreignObject">
+      <svg width="100" height="100">
+        <rect class="fillColor" width="100" height="100"/>
+      </svg>
+    </foreignObject>
+  </g>
+  <script><![CDATA[
+
+var options = {
+  offsetContainer: "rect",
+  filter: null,
+  mask: null,
+  updateOffsetOn: "reftestInvalidate" // | "initial" | "timeout"
+};
+
+location.search.substr(1).split("&").forEach(function (s) {
+  var pv = s.split("=");
+  options[pv[0]] = pv[1] || true;
+});
+
+var offsetContainer = document.getElementById(options.offsetContainer);
+
+function updateOffsetNow() {
+  offsetContainer.setAttribute("x", "100");
+}
+
+function updateOffsetOnReftestInvalidate() {
+  document.documentElement.setAttribute("class", "reftest-wait");
+  document.addEventListener("MozReftestInvalidate", function () {
+    updateOffsetNow();
+    document.documentElement.removeAttribute("class");
+  }, false);
+}
+
+function updateOffsetOnTimeout() {
+  setTimeout(updateOffsetNow, 500);
+}
+
+options.updateOffset = options.updateOffsetOn == "initial" ? updateOffsetNow :
+                         (options.updateOffsetOn == "timeout" ? updateOffsetOnTimeout :
+                          updateOffsetOnReftestInvalidate);
+
+var offsetContainers = Array.prototype.slice.call(document.getElementsByClassName("offsetContainer"), 0);
+for (var i = 0; i < offsetContainers.length; i++) {
+  if (offsetContainers[i] != offsetContainer) {
+    offsetContainers[i].parentNode.removeChild(offsetContainers[i]);
+  }
+}
+
+var fillColor = options.filter ? "red" : "lime";
+if (options.filter) {
+  offsetContainer.setAttribute("filter", "url(#" + options.filter + ")");
+}
+if (options.mask) {
+  offsetContainer.setAttribute("mask", "url(#" + options.mask + ")");
+}
+
+var fillColors = document.getElementsByClassName("fillColor");
+for (var j = 0; j < fillColors.length; j++) {
+  fillColors[j].setAttribute("fill", fillColor);
+}
+
+options.updateOffset();
+
+]]></script>
+</svg>
--- a/layout/reftests/invalidation/reftest.list
+++ b/layout/reftests/invalidation/reftest.list
@@ -3,8 +3,38 @@
 == table-repaint-c.html table-repaint-c-ref.html
 == table-repaint-d.html table-repaint-d-ref.html
 skip-if(B2G&&browserIsRemote) == 540247-1.xul 540247-1-ref.xul  # bug 974780
 skip-if(B2G&&browserIsRemote) == 543681-1.html 543681-1-ref.html
 == test-image-layers.html test-image-layers-ref.html
 pref(layout.animated-image-layers.enabled,true) == test-animated-image-layers.html test-animated-image-layers-ref.html
 pref(layout.animated-image-layers.enabled,true) == test-animated-image-layers-background.html test-animated-image-layers-ref.html
 == box-shadow-border-radius.html box-shadow-border-radius-ref.html
+== filter-userspace-offset.svg?offsetContainer=rect filter-userspace-offset.svg
+== filter-userspace-offset.svg?offsetContainer=use filter-userspace-offset.svg
+== filter-userspace-offset.svg?offsetContainer=innerSVG filter-userspace-offset.svg
+== filter-userspace-offset.svg?offsetContainer=foreignObject filter-userspace-offset.svg
+== filter-userspace-offset.svg?offsetContainer=rect&filter=flood-boundingBox filter-userspace-offset.svg
+== filter-userspace-offset.svg?offsetContainer=use&filter=flood-boundingBox filter-userspace-offset.svg
+== filter-userspace-offset.svg?offsetContainer=innerSVG&filter=flood-boundingBox filter-userspace-offset.svg
+== filter-userspace-offset.svg?offsetContainer=foreignObject&filter=flood-boundingBox filter-userspace-offset.svg
+== filter-userspace-offset.svg?offsetContainer=rect&filter=matrix-boundingBox filter-userspace-offset.svg
+== filter-userspace-offset.svg?offsetContainer=use&filter=matrix-boundingBox filter-userspace-offset.svg
+== filter-userspace-offset.svg?offsetContainer=innerSVG&filter=matrix-boundingBox filter-userspace-offset.svg
+== filter-userspace-offset.svg?offsetContainer=foreignObject&filter=matrix-boundingBox filter-userspace-offset.svg
+== filter-userspace-offset.svg?offsetContainer=rect&filter=flood-userSpace-at100 filter-userspace-offset.svg
+== filter-userspace-offset.svg?offsetContainer=use&filter=flood-userSpace-atZero filter-userspace-offset.svg
+== filter-userspace-offset.svg?offsetContainer=innerSVG&filter=flood-userSpace-atZero filter-userspace-offset.svg
+== filter-userspace-offset.svg?offsetContainer=foreignObject&filter=flood-userSpace-at100 filter-userspace-offset.svg
+== filter-userspace-offset.svg?offsetContainer=rect&filter=matrix-userSpace-at100 filter-userspace-offset.svg
+== filter-userspace-offset.svg?offsetContainer=use&filter=matrix-userSpace-atZero filter-userspace-offset.svg
+== filter-userspace-offset.svg?offsetContainer=innerSVG&filter=matrix-userSpace-atZero filter-userspace-offset.svg
+== filter-userspace-offset.svg?offsetContainer=foreignObject&filter=matrix-userSpace-at100 filter-userspace-offset.svg
+== filter-userspace-offset.svg?offsetContainer=rect&mask=boundingBox filter-userspace-offset.svg
+== filter-userspace-offset.svg?offsetContainer=use&mask=boundingBox filter-userspace-offset.svg
+== filter-userspace-offset.svg?offsetContainer=innerSVG&mask=boundingBox filter-userspace-offset.svg
+== filter-userspace-offset.svg?offsetContainer=foreignObject&mask=boundingBox filter-userspace-offset.svg
+== filter-userspace-offset.svg?offsetContainer=rect&mask=userSpace-at100 filter-userspace-offset.svg
+== filter-userspace-offset.svg?offsetContainer=use&mask=userSpace-atZero filter-userspace-offset.svg
+== filter-userspace-offset.svg?offsetContainer=innerSVG&mask=userSpace-atZero filter-userspace-offset.svg
+== filter-userspace-offset.svg?offsetContainer=foreignObject&mask=userSpace-at100 filter-userspace-offset.svg
+== filter-userspace-offset.svg?offsetContainer=rect&filter=matrix-fillPaint-boundingBox filter-userspace-offset.svg
+== filter-userspace-offset.svg?offsetContainer=rect&filter=matrix-fillPaint-userSpace-at100 filter-userspace-offset.svg
new file mode 100644
--- /dev/null
+++ b/layout/reftests/mathml/opentype-stretchy-ref.html
@@ -0,0 +1,73 @@
+<!doctype html>
+<html>
+  <head>
+    <title>Open Type MATH - stretchy operator</title>
+    <meta charset="utf-8"/>
+    <style type="text/css">
+      @font-face {
+        font-family: stretchy;
+        src: url(../fonts/math/stretchy.otf);
+      }
+      math {
+        font-family: stretchy;
+        font-size: 10px;
+      }
+      ::-moz-math-stretchy {
+        font-family: stretchy;
+        font-size: 10px;
+      }
+    </style>
+  </head>
+  <body>
+
+<!--
+hstretchy = [
+    0x219C, # leftwards wave arrow
+    0x219D, # rightwards wave arrow
+    0x219E, # leftwards two headed arrow
+    0x21A0, # rightwards two headed arrow
+    0x21A2  # leftwards arrow with tail
+]
+vstretchy = [
+    0x219F, # upwards two headed arrow
+    0x21A1, # downwards two headed arrow
+    0x21A5, # upwards arrow from bar
+    0x21A7, # downwards arrow from bar
+    0x21A8  # up down arrow with base
+]
+
+hstretchy[0] and vstretchy[0] have all the variants and the components. The others only have one of them.
+-->
+
+    <p>
+      <math>
+        <mstyle scriptsizemultiplier="1">
+          <mover><mo stretchy="true">&#x219D;</mo><mspace width="1em" height="1px" mathbackground="red"/></mover>
+          <mover><mo stretchy="true">&#x219E;</mo><mspace width="2em" height="1px" mathbackground="red"/></mover>
+          <mover><mo stretchy="true">&#x21A0;</mo><mspace width="3em" height="1px" mathbackground="red"/></mover>
+          <mover><mo stretchy="true">&#x21A2;</mo><mspace width="15em" height="1px" mathbackground="red"/></mover>
+        </mstyle>
+      </math>
+    </p>
+
+    <p>
+      <math><mrow><mo symmetric="false" stretchy="true" minsize="1em">&#x21A1;</mo></mrow></math>
+      <math><mrow><mo symmetric="false" stretchy="true" minsize="2em">&#x21A5;</mo></mrow></math>
+      <math><mrow><mo symmetric="false" stretchy="true" minsize="3em">&#x21A7;</mo></mrow></math>
+      <math><mrow><mo symmetric="false" stretchy="true" minsize="15em">&#x21A8;</mo></mrow></math>
+    </p>
+
+<!--
+DisplayOperatorMinHeight = 8em
+largeop = [0x2A1B, 0x2A1C] # integral with overbar/underbar
+largeop[0] has variants of size 7, 8, 9em
+largeop[1] has one variant of size 8em.
+-->
+    <p>
+      <math displaystyle="true">
+        <mrow><mo>&#x2A1C;</mo></mrow>
+      </math>
+    </p>
+
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/mathml/opentype-stretchy.html
@@ -0,0 +1,73 @@
+<!doctype html>
+<html>
+  <head>
+    <title>Open Type MATH - stretchy operator</title>
+    <meta charset="utf-8"/>
+    <style type="text/css">
+      @font-face {
+        font-family: stretchy;
+        src: url(../fonts/math/stretchy.otf);
+      }
+      math {
+        font-family: stretchy;
+        font-size: 10px;
+      }
+      ::-moz-math-stretchy {
+        font-family: stretchy;
+        font-size: 10px;
+      }
+    </style>
+  </head>
+  <body>
+
+<!--
+hstretchy = [
+    0x219C, # leftwards wave arrow
+    0x219D, # rightwards wave arrow
+    0x219E, # leftwards two headed arrow
+    0x21A0, # rightwards two headed arrow
+    0x21A2  # leftwards arrow with tail
+]
+vstretchy = [
+    0x219F, # upwards two headed arrow
+    0x21A1, # downwards two headed arrow
+    0x21A5, # upwards arrow from bar
+    0x21A7, # downwards arrow from bar
+    0x21A8  # up down arrow with base
+]
+
+hstretchy[0] and vstretchy[0] have all the variants and the components. The others only have one of them.
+-->
+
+    <p>
+      <math>
+        <mstyle scriptsizemultiplier="1">
+          <mover><mo stretchy="true">&#x219C;</mo><mspace width="1em" height="1px" mathbackground="red"/></mover>
+          <mover><mo stretchy="true">&#x219C;</mo><mspace width="2em" height="1px" mathbackground="red"/></mover>
+          <mover><mo stretchy="true">&#x219C;</mo><mspace width="3em" height="1px" mathbackground="red"/></mover>
+          <mover><mo stretchy="true">&#x219C;</mo><mspace width="15em" height="1px" mathbackground="red"/></mover>
+        </mstyle>
+      </math>
+    </p>
+
+    <p>
+      <math><mrow><mo symmetric="false" stretchy="true" minsize="1em">&#x219F;</mo></mrow></math>
+      <math><mrow><mo symmetric="false" stretchy="true" minsize="2em">&#x219F;</mo></mrow></math>
+      <math><mrow><mo symmetric="false" stretchy="true" minsize="3em">&#x219F;</mo></mrow></math>
+      <math><mrow><mo symmetric="false" stretchy="true" minsize="15em">&#x219F;</mo></mrow></math>
+    </p>
+
+<!--
+DisplayOperatorMinHeight = 8em
+largeop = [0x2A1B, 0x2A1C] # integral with overbar/underbar
+largeop[0] has variants of size 7, 8, 9em
+largeop[1] has one variant of size 8em.
+-->
+    <p>
+      <math displaystyle="true">
+        <mrow><mo>&#x2A1B;</mo></mrow>
+      </math>
+    </p>
+
+  </body>
+</html>
--- a/layout/reftests/mathml/reftest.list
+++ b/layout/reftests/mathml/reftest.list
@@ -147,16 +147,17 @@ skip-if(B2G) == maction-dynamic-1.html m
 == mo-invisibleoperators.html mo-invisibleoperators-ref.html
 == mo-invisibleoperators-2.html mo-invisibleoperators-2-ref.html
 skip-if(B2G) == maction-dynamic-3.html maction-dynamic-3-ref.html # bug 773482
 == whitespace-trim-1.html whitespace-trim-1-ref.html
 == whitespace-trim-2.html whitespace-trim-2-ref.html
 == whitespace-trim-3.html whitespace-trim-3-ref.html
 fails == whitespace-trim-4.html whitespace-trim-4-ref.html # Bug 787215
 == whitespace-trim-5.html whitespace-trim-5-ref.html
+fails-if(winWidget&&!d2d) == opentype-stretchy.html opentype-stretchy-ref.html
 == operator-1.xhtml operator-1-ref.xhtml
 == scriptshift-1.xhtml scriptshift-1-ref.xhtml
 == number-size-1.xhtml number-size-1-ref.xhtml
 fails-if(B2G) == multiscripts-1.html multiscripts-1-ref.html # B2G - slight height variation from font metrics
 == mathml-mmultiscript-base.html mathml-mmultiscript-base-ref.html
 == mathml-mmultiscript-mprescript.html mathml-mmultiscript-mprescript-ref.html
 != menclose-1a.html menclose-1-ref.html
 != menclose-1b.html menclose-1-ref.html
--- a/layout/style/nsCSSKeywordList.h
+++ b/layout/style/nsCSSKeywordList.h
@@ -367,16 +367,17 @@ CSS_KEY(looped, looped)
 CSS_KEY(lower-alpha, lower_alpha)
 CSS_KEY(lower-greek, lower_greek)
 CSS_KEY(lower-latin, lower_latin)
 CSS_KEY(lower-roman, lower_roman)
 CSS_KEY(lowercase, lowercase)
 CSS_KEY(ltr, ltr)
 CSS_KEY(luminance, luminance)
 CSS_KEY(luminosity, luminosity)
+CSS_KEY(manipulation, manipulation)
 CSS_KEY(manual, manual)
 CSS_KEY(margin-box, margin_box)
 CSS_KEY(markers, markers)
 CSS_KEY(matrix, matrix)
 CSS_KEY(matrix3d, matrix3d)
 CSS_KEY(max-content, max_content)
 CSS_KEY(medium, medium)
 CSS_KEY(menu, menu)
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -12400,18 +12400,20 @@ CSSParserImpl::ParseTextOverflow(nsCSSVa
     aValue = left;
   }
   return true;
 }
 
 bool
 CSSParserImpl::ParseTouchAction(nsCSSValue& aValue)
 {
-  if (!ParseVariant(aValue, VARIANT_HK | VARIANT_NONE | VARIANT_AUTO,
-                    nsCSSProps::kTouchActionKTable)) {
+  // Avaliable values of property touch-action:
+  // auto | none | [pan-x || pan-y] | manipulation
+
+  if (!ParseVariant(aValue, VARIANT_HK, nsCSSProps::kTouchActionKTable)) {
     return false;
   }
 
   // Auto and None keywords aren't allowed in conjunction with others.
   // Also inherit, initial and unset values are available.
   if (eCSSUnit_Enumerated != aValue.GetUnit()) {
     return true;
   }
@@ -12421,16 +12423,23 @@ CSSParserImpl::ParseTouchAction(nsCSSVal
   if (ParseEnum(nextValue, nsCSSProps::kTouchActionKTable)) {
     int32_t nextIntValue = nextValue.GetIntValue();
 
     // duplicates aren't allowed.
     if (nextIntValue & intValue) {
       return false;
     }
 
+    // Auto and None and Manipulation is not allowed in conjunction with others.
+    if ((intValue | nextIntValue) & (NS_STYLE_TOUCH_ACTION_NONE |
+                                     NS_STYLE_TOUCH_ACTION_AUTO |
+                                     NS_STYLE_TOUCH_ACTION_MANIPULATION)) {
+      return false;
+    }
+
     aValue.SetIntValue(nextIntValue | intValue, eCSSUnit_Enumerated);
   }
 
   return true;
 }
 
 bool
 CSSParserImpl::ParseTextCombineUpright(nsCSSValue& aValue)
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -1615,19 +1615,22 @@ const KTableValue nsCSSProps::kTextTrans
   eCSSKeyword_capitalize, NS_STYLE_TEXT_TRANSFORM_CAPITALIZE,
   eCSSKeyword_lowercase, NS_STYLE_TEXT_TRANSFORM_LOWERCASE,
   eCSSKeyword_uppercase, NS_STYLE_TEXT_TRANSFORM_UPPERCASE,
   eCSSKeyword_full_width, NS_STYLE_TEXT_TRANSFORM_FULLWIDTH,
   eCSSKeyword_UNKNOWN,-1
 };
 
 const KTableValue nsCSSProps::kTouchActionKTable[] = {
-  eCSSKeyword_pan_x, NS_STYLE_TOUCH_ACTION_PAN_X,
-  eCSSKeyword_pan_y, NS_STYLE_TOUCH_ACTION_PAN_Y,
-  eCSSKeyword_UNKNOWN, -1
+  eCSSKeyword_none,         NS_STYLE_TOUCH_ACTION_NONE,
+  eCSSKeyword_auto,         NS_STYLE_TOUCH_ACTION_AUTO,
+  eCSSKeyword_pan_x,        NS_STYLE_TOUCH_ACTION_PAN_X,
+  eCSSKeyword_pan_y,        NS_STYLE_TOUCH_ACTION_PAN_Y,
+  eCSSKeyword_manipulation, NS_STYLE_TOUCH_ACTION_MANIPULATION,
+  eCSSKeyword_UNKNOWN,      -1
 };
 
 const KTableValue nsCSSProps::kTransitionTimingFunctionKTable[] = {
   eCSSKeyword_ease, NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE,
   eCSSKeyword_linear, NS_STYLE_TRANSITION_TIMING_FUNCTION_LINEAR,
   eCSSKeyword_ease_in, NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE_IN,
   eCSSKeyword_ease_out, NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE_OUT,
   eCSSKeyword_ease_in_out, NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE_IN_OUT,
--- a/layout/style/nsCSSValue.cpp
+++ b/layout/style/nsCSSValue.cpp
@@ -1043,18 +1043,18 @@ nsCSSValue::AppendToString(nsCSSProperty
     case eCSSProperty_grid_row_start:
     case eCSSProperty_grid_row_end:
       // "span" is the only enumerated-unit value for these properties
       aResult.AppendLiteral("span");
       break;
 
     case eCSSProperty_touch_action:
       nsStyleUtil::AppendBitmaskCSSValue(aProperty, intValue,
-                                         NS_STYLE_TOUCH_ACTION_PAN_X,
-                                         NS_STYLE_TOUCH_ACTION_PAN_Y,
+                                         NS_STYLE_TOUCH_ACTION_NONE,
+                                         NS_STYLE_TOUCH_ACTION_MANIPULATION,
                                          aResult);
       break;
 
     default:
       const nsAFlatCString& name = nsCSSProps::LookupPropertyValue(aProperty, intValue);
       AppendASCIItoUTF16(name, aResult);
       break;
     }
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -4058,30 +4058,24 @@ nsComputedDOMStyle::DoGetPageBreakInside
 
 CSSValue*
 nsComputedDOMStyle::DoGetTouchAction()
 {
   nsROCSSPrimitiveValue *val = new nsROCSSPrimitiveValue;
 
   int32_t intValue = StyleDisplay()->mTouchAction;
 
-  // None and Auto values aren't allowed to be in conjunction with
-  // other values.
-  if (NS_STYLE_TOUCH_ACTION_AUTO == intValue) {
-    val->SetIdent(eCSSKeyword_auto);
-  } else if (NS_STYLE_TOUCH_ACTION_NONE == intValue) {
-    val->SetIdent(eCSSKeyword_none);
-  } else {
-    nsAutoString valueStr;
-    nsStyleUtil::AppendBitmaskCSSValue(eCSSProperty_touch_action,
-      intValue, NS_STYLE_TOUCH_ACTION_PAN_X,
-      NS_STYLE_TOUCH_ACTION_PAN_Y, valueStr);
-    val->SetString(valueStr);
-  }
-
+  // None and Auto and Manipulation values aren't allowed
+  // to be in conjunction with other values.
+  // But there are all checks in CSSParserImpl::ParseTouchAction
+  nsAutoString valueStr;
+  nsStyleUtil::AppendBitmaskCSSValue(eCSSProperty_touch_action, intValue,
+    NS_STYLE_TOUCH_ACTION_NONE, NS_STYLE_TOUCH_ACTION_MANIPULATION,
+    valueStr);
+  val->SetString(valueStr);
   return val;
 }
 
 CSSValue*
 nsComputedDOMStyle::DoGetHeight()
 {
   nsROCSSPrimitiveValue *val = new nsROCSSPrimitiveValue;
 
--- a/layout/style/nsStyleConsts.h
+++ b/layout/style/nsStyleConsts.h
@@ -774,16 +774,17 @@ static inline mozilla::css::Side operato
 #define NS_STYLE_TEXT_TRANSFORM_UPPERCASE       3
 #define NS_STYLE_TEXT_TRANSFORM_FULLWIDTH       4
 
 // See nsStyleDisplay
 #define NS_STYLE_TOUCH_ACTION_NONE            (1 << 0)
 #define NS_STYLE_TOUCH_ACTION_AUTO            (1 << 1)
 #define NS_STYLE_TOUCH_ACTION_PAN_X           (1 << 2)
 #define NS_STYLE_TOUCH_ACTION_PAN_Y           (1 << 3)
+#define NS_STYLE_TOUCH_ACTION_MANIPULATION    (1 << 4)
 
 // See nsStyleDisplay
 #define NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE         0
 #define NS_STYLE_TRANSITION_TIMING_FUNCTION_LINEAR       1
 #define NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE_IN      2
 #define NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE_OUT     3
 #define NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE_IN_OUT  4
 #define NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START   5
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -4368,20 +4368,27 @@ function get_computed_value(cs, property
 }
 
 if (SpecialPowers.getBoolPref("layout.css.touch_action.enabled")) {
     gCSSProperties["touch-action"] = {
         domProp: "touchAction",
         inherited: false,
         type: CSS_TYPE_LONGHAND,
         initial_values: ["auto"],
-        other_values: ["none", "pan-x", "pan-y", "pan-x pan-y", "pan-y pan-x"],
+        other_values: ["none", "pan-x", "pan-y", "pan-x pan-y", "pan-y pan-x", "manipulation"],
         invalid_values: ["zoom", "pinch", "tap", "10px", "2", "auto pan-x", "pan-x auto", "none pan-x", "pan-x none",
-        				 "auto pan-y", "pan-y auto", "none pan-y", "pan-y none",
-        				 "pan-x pan-y none", "none pan-x pan-y", "pan-x pan-y auto", "auto pan-x pan-y"]
+        				 "auto pan-y", "pan-y auto", "none pan-y", "pan-y none", "pan-x pan-x", "pan-y pan-y",
+        				 "pan-x pan-y none", "pan-x none pan-y", "none pan-x pan-y", "pan-y pan-x none", "pan-y none pan-x", "none pan-y pan-x",
+        				 "pan-x pan-y auto", "pan-x auto pan-y", "auto pan-x pan-y", "pan-y pan-x auto", "pan-y auto pan-x", "auto pan-y pan-x",
+        				 "pan-x pan-y zoom", "pan-x zoom pan-y", "zoom pan-x pan-y", "pan-y pan-x zoom", "pan-y zoom pan-x", "zoom pan-y pan-x",
+        				 "pan-x pan-y pan-x", "pan-x pan-x pan-y", "pan-y pan-x pan-x", "pan-y pan-x pan-y", "pan-y pan-y pan-x", "pan-x pan-y pan-y",
+        				 "manipulation none", "none manipulation", "manipulation auto", "auto manipulation", "manipulation zoom", "zoom manipulation",
+        				 "manipulation manipulation", "manipulation pan-x", "pan-x manipulation", "manipulation pan-y", "pan-y manipulation",
+        				 "manipulation pan-x pan-y", "pan-x manipulation pan-y", "pan-x pan-y manipulation",
+        				 "manipulation pan-y pan-x", "pan-y manipulation pan-x", "pan-y pan-x manipulation"]
     };
 }
 
 if (SpecialPowers.getBoolPref("layout.css.vertical-text.enabled")) {
 	var verticalTextProperties = {
 		"writing-mode": {
 			domProp: "writingMode",
 			inherited: true,
--- a/layout/svg/nsFilterInstance.cpp
+++ b/layout/svg/nsFilterInstance.cpp
@@ -584,35 +584,10 @@ nsFilterInstance::FilterSpaceToFrameSpac
   gfxRect r(aRect.x, aRect.y, aRect.width, aRect.height);
   r = mFilterSpaceToFrameSpaceInCSSPxTransform.TransformBounds(r);
   return nsLayoutUtils::RoundGfxRectToAppRect(r, mAppUnitsPerCSSPx);
 }
 
 gfxMatrix
 nsFilterInstance::GetUserSpaceToFrameSpaceInCSSPxTransform() const
 {
-  gfxMatrix userToFrameSpaceInCSSPx;
-
-  if ((mTargetFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
-    // As currently implemented by Mozilla for the purposes of filters, user
-    // space is the coordinate system established by GetCanvasTM(), since
-    // that's what we use to set filterToDeviceSpace above. In other words,
-    // for SVG, user space is actually the coordinate system aTarget
-    // establishes for _its_ children (i.e. after taking account of any x/y
-    // and viewBox attributes), not the coordinate system that is established
-    // for it by its 'transform' attribute (or by its _parent_) as it's
-    // normally defined. (XXX We should think about fixing this.) The only
-    // frame type for which these extra transforms are not simply an x/y
-    // translation is nsSVGInnerSVGFrame, hence we treat it specially here.
-    if (mTargetFrame->GetType() == nsGkAtoms::svgInnerSVGFrame) {
-      userToFrameSpaceInCSSPx =
-        static_cast<nsSVGElement*>(mTargetFrame->GetContent())->
-          PrependLocalTransformsTo(gfxMatrix());
-    } else {
-      gfxPoint targetsUserSpaceOffset =
-        nsLayoutUtils::RectToGfxRect(mTargetFrame->GetRect(),
-                                     mAppUnitsPerCSSPx).TopLeft();
-      userToFrameSpaceInCSSPx.Translate(-targetsUserSpaceOffset);
-    }
-  }
-  // else, for all other frames, leave as the identity matrix
-  return userToFrameSpaceInCSSPx;
+  return gfxMatrix().Translate(-nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(mTargetFrame));
 }
--- a/layout/svg/nsSVGForeignObjectFrame.cpp
+++ b/layout/svg/nsSVGForeignObjectFrame.cpp
@@ -377,16 +377,22 @@ nsSVGForeignObjectFrame::ReflowSVG()
 
   if (mState & NS_FRAME_FIRST_REFLOW) {
     // Make sure we have our filter property (if any) before calling
     // FinishAndStoreOverflow (subsequent filter changes are handled off
     // nsChangeHint_UpdateEffects):
     nsSVGEffects::UpdateEffects(this);
   }
 
+  // If we have a filter, we need to invalidate ourselves because filter
+  // output can change even if none of our descendants need repainting.
+  if (StyleSVGReset()->HasFilters()) {
+    InvalidateFrame();
+  }
+
   // TODO: once we support |overflow:visible| on foreignObject, then we will
   // need to take account of our descendants here.
   nsRect overflow = nsRect(nsPoint(0,0), mRect.Size());
   nsOverflowAreas overflowAreas(overflow, overflow);
   FinishAndStoreOverflow(overflowAreas, mRect.Size());
 
   // Now unset the various reflow bits:
   mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
--- a/layout/svg/nsSVGInnerSVGFrame.cpp
+++ b/layout/svg/nsSVGInnerSVGFrame.cpp
@@ -97,16 +97,23 @@ nsSVGInnerSVGFrame::ReflowSVG()
   // mRect must be set before FinishAndStoreOverflow is called in order
   // for our overflow areas to be clipped correctly.
   float x, y, width, height;
   static_cast<SVGSVGElement*>(mContent)->
     GetAnimatedLengthValues(&x, &y, &width, &height, nullptr);
   mRect = nsLayoutUtils::RoundGfxRectToAppRect(
                            gfxRect(x, y, width, height),
                            PresContext()->AppUnitsPerCSSPixel());
+
+  // If we have a filter, we need to invalidate ourselves because filter
+  // output can change even if none of our descendants need repainting.
+  if (StyleSVGReset()->HasFilters()) {
+    InvalidateFrame();
+  }
+
   nsSVGInnerSVGFrameBase::ReflowSVG();
 }
 
 void
 nsSVGInnerSVGFrame::NotifySVGChanged(uint32_t aFlags)
 {
   NS_ABORT_IF_FALSE(aFlags & (TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED),
                     "Invalidation logic may need adjusting");
--- a/layout/svg/nsSVGIntegrationUtils.cpp
+++ b/layout/svg/nsSVGIntegrationUtils.cpp
@@ -149,18 +149,20 @@ nsSVGIntegrationUtils::UsingEffectsForFr
   // Even when SVG display lists are disabled, returning true for SVG frames
   // does not adversely affect any of our callers. Therefore we don't bother
   // checking the SDL prefs here, since we don't know if we're being called for
   // painting or hit-testing anyway.
   const nsStyleSVGReset *style = aFrame->StyleSVGReset();
   return (style->HasFilters() || style->mClipPath || style->mMask);
 }
 
-/* static */ nsPoint
-nsSVGIntegrationUtils::GetOffsetToUserSpace(nsIFrame* aFrame)
+// For non-SVG frames, this gives the offset to the frame's "user space".
+// For SVG frames, this returns a zero offset.
+static nsPoint
+GetOffsetToBoundingBox(nsIFrame* aFrame)
 {
   if ((aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
     // Do NOT call GetAllInFlowRectsUnion for SVG - it will get the
     // covered region relative to the nsSVGOuterSVGFrame, which is absolutely
     // not what we want. SVG frames are always in user space, so they have
     // no offset adjustment to make.
     return nsPoint();
   }
@@ -203,26 +205,26 @@ gfxRect
 nsSVGIntegrationUtils::GetSVGBBoxForNonSVGFrame(nsIFrame* aNonSVGFrame)
 {
   NS_ASSERTION(!aNonSVGFrame->IsFrameOfType(nsIFrame::eSVG),
                "SVG frames should not get here");
   nsIFrame* firstFrame =
     nsLayoutUtils::FirstContinuationOrIBSplitSibling(aNonSVGFrame);
   // 'r' is in "user space":
   nsRect r = GetPreEffectsVisualOverflowUnion(firstFrame, nullptr, nsRect(),
-                                              GetOffsetToUserSpace(firstFrame));
+                                              GetOffsetToBoundingBox(firstFrame));
   return nsLayoutUtils::RectToGfxRect(r,
            aNonSVGFrame->PresContext()->AppUnitsPerCSSPixel());
 }
 
 // XXX Since we're called during reflow, this method is broken for frames with
 // continuations. When we're called for a frame with continuations, we're
 // called for each continuation in turn as it's reflowed. However, it isn't
 // until the last continuation is reflowed that this method's
-// GetOffsetToUserSpace() and GetPreEffectsVisualOverflowUnion() calls will
+// GetOffsetToBoundingBox() and GetPreEffectsVisualOverflowUnion() calls will
 // obtain valid border boxes for all the continuations. As a result, we'll
 // end up returning bogus post-filter visual overflow rects for all the prior
 // continuations. Unfortunately, by the time the last continuation is
 // reflowed, it's too late to go back and set and propagate the overflow
 // rects on the previous continuations.
 //
 // The reason that we need to pass an override bbox to
 // GetPreEffectsVisualOverflowUnion rather than just letting it call into our
@@ -257,33 +259,33 @@ nsRect
     nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
   nsSVGEffects::EffectProperties effectProperties =
     nsSVGEffects::GetEffectProperties(firstFrame);
   if (!effectProperties.HasValidFilter()) {
     return aPreEffectsOverflowRect;
   }
 
   // Create an override bbox - see comment above:
-  nsPoint firstFrameToUserSpace = GetOffsetToUserSpace(firstFrame);
+  nsPoint firstFrameToBoundingBox = GetOffsetToBoundingBox(firstFrame);
   // overrideBBox is in "user space", in _CSS_ pixels:
   // XXX Why are we rounding out to pixel boundaries? We don't do that in
   // GetSVGBBoxForNonSVGFrame, and it doesn't appear to be necessary.
   gfxRect overrideBBox =
     nsLayoutUtils::RectToGfxRect(
       GetPreEffectsVisualOverflowUnion(firstFrame, aFrame,
                                        aPreEffectsOverflowRect,
-                                       firstFrameToUserSpace),
+                                       firstFrameToBoundingBox),
       aFrame->PresContext()->AppUnitsPerCSSPixel());
   overrideBBox.RoundOut();
 
   nsRect overflowRect =
     nsFilterInstance::GetPostFilterBounds(firstFrame, &overrideBBox);
 
   // Return overflowRect relative to aFrame, rather than "user space":
-  return overflowRect - (aFrame->GetOffsetTo(firstFrame) + firstFrameToUserSpace);
+  return overflowRect - (aFrame->GetOffsetTo(firstFrame) + firstFrameToBoundingBox);
 }
 
 nsIntRect
 nsSVGIntegrationUtils::AdjustInvalidAreaForSVGEffects(nsIFrame* aFrame,
                                                       const nsPoint& aToReferenceFrame,
                                                       const nsIntRect& aInvalidRect)
 {
   if (aInvalidRect.IsEmpty()) {
@@ -305,28 +307,28 @@ nsSVGIntegrationUtils::AdjustInvalidArea
     // The frame is either not there or not currently available,
     // perhaps because we're in the middle of tearing stuff down.
     // Be conservative, return our visual overflow rect relative
     // to the reference frame.
     nsRect overflow = aFrame->GetVisualOverflowRect() + aToReferenceFrame;
     return overflow.ToOutsidePixels(appUnitsPerDevPixel);
   }
 
-  // Convert aInvalidRect into "user space" in app units:
-  nsPoint toUserSpace =
-    aFrame->GetOffsetTo(firstFrame) + GetOffsetToUserSpace(firstFrame);
+  // Convert aInvalidRect into bounding box frame space in app units:
+  nsPoint toBoundingBox =
+    aFrame->GetOffsetTo(firstFrame) + GetOffsetToBoundingBox(firstFrame);
   // The initial rect was relative to the reference frame, so we need to
   // remove that offset to get a rect relative to the current frame.
-  toUserSpace -= aToReferenceFrame;
-  nsRect preEffectsRect = aInvalidRect.ToAppUnits(appUnitsPerDevPixel) + toUserSpace;
+  toBoundingBox -= aToReferenceFrame;
+  nsRect preEffectsRect = aInvalidRect.ToAppUnits(appUnitsPerDevPixel) + toBoundingBox;
 
   // Adjust the dirty area for effects, and shift it back to being relative to
   // the reference frame.
   nsRect result = nsFilterInstance::GetPostFilterDirtyArea(firstFrame,
-    preEffectsRect) - toUserSpace;
+    preEffectsRect) - toBoundingBox;
   // Return the result, in pixels relative to the reference frame.
   return result.ToOutsidePixels(appUnitsPerDevPixel);
 }
 
 nsRect
 nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(nsIFrame* aFrame,
                                                        const nsRect& aDirtyRect)
 {
@@ -336,36 +338,37 @@ nsSVGIntegrationUtils::GetRequiredSource
     nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
   nsSVGFilterProperty *prop = nsSVGEffects::GetFilterProperty(firstFrame);
   if (!prop || !prop->ReferencesValidResources()) {
     return aDirtyRect;
   }
   
   // Convert aDirtyRect into "user space" in app units:
   nsPoint toUserSpace =
-    aFrame->GetOffsetTo(firstFrame) + GetOffsetToUserSpace(firstFrame);
+    aFrame->GetOffsetTo(firstFrame) + GetOffsetToBoundingBox(firstFrame);
   nsRect postEffectsRect = aDirtyRect + toUserSpace;
 
   // Return ther result, relative to aFrame, not in user space:
   return nsFilterInstance::GetPreFilterNeededArea(firstFrame, postEffectsRect)
     - toUserSpace;
 }
 
 bool
 nsSVGIntegrationUtils::HitTestFrameForEffects(nsIFrame* aFrame, const nsPoint& aPt)
 {
   nsIFrame* firstFrame =
     nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
   // Convert aPt to user space:
   nsPoint toUserSpace;
   if (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) {
+    // XXXmstange Isn't this wrong for svg:use and innerSVG frames?
     toUserSpace = aFrame->GetPosition();
   } else {
     toUserSpace =
-      aFrame->GetOffsetTo(firstFrame) + GetOffsetToUserSpace(firstFrame);
+      aFrame->GetOffsetTo(firstFrame) + GetOffsetToBoundingBox(firstFrame);
   }
   nsPoint pt = aPt + toUserSpace;
   return nsSVGUtils::HitTestClip(firstFrame, pt);
 }
 
 class RegularFramePaintCallback : public nsSVGFilterPaintCallback
 {
 public:
@@ -455,71 +458,80 @@ nsSVGIntegrationUtils::PaintFramesWithEf
     return; // Some resource is missing. We shouldn't paint anything.
   }
 
   bool isTrivialClip = clipPathFrame ? clipPathFrame->IsTrivial() : true;
 
   gfxContext* gfx = aCtx->ThebesContext();
   gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(gfx);
 
-  nsPoint firstFrameOffset = GetOffsetToUserSpace(firstFrame);
-  nsPoint offset = aBuilder->ToReferenceFrame(firstFrame) - firstFrameOffset;
-  nsPoint offsetWithoutSVGGeomFramePos;
-  if (firstFrame->IsFrameOfType(nsIFrame::eSVG)) {
-    offsetWithoutSVGGeomFramePos = offset;
-  } else {
+  nsPoint firstFrameOffset = GetOffsetToBoundingBox(firstFrame);
+  nsPoint offsetToBoundingBox = aBuilder->ToReferenceFrame(firstFrame) - firstFrameOffset;
+  if (!firstFrame->IsFrameOfType(nsIFrame::eSVG)) {
     /* Snap the offset if the reference frame is not a SVG frame,
      * since other frames will be snapped to pixel when rendering. */
-    offsetWithoutSVGGeomFramePos = nsPoint(
-      aFrame->PresContext()->RoundAppUnitsToNearestDevPixels(offset.x),
-      aFrame->PresContext()->RoundAppUnitsToNearestDevPixels(offset.y));
-  }
-  nsPoint svgGeomFramePos;
-  if (aFrame->IsFrameOfType(nsIFrame::eSVGGeometry) ||
-      aFrame->IsSVGText()) {
-    // SVG leaf frames apply their offset themselves, we need to unapply it at
-    // various points below to prevent it being double counted.
-    svgGeomFramePos = aFrame->GetPosition();
-    offsetWithoutSVGGeomFramePos -= svgGeomFramePos;
+    offsetToBoundingBox = nsPoint(
+      aFrame->PresContext()->RoundAppUnitsToNearestDevPixels(offsetToBoundingBox.x),
+      aFrame->PresContext()->RoundAppUnitsToNearestDevPixels(offsetToBoundingBox.y));
   }
 
-  aCtx->Translate(offsetWithoutSVGGeomFramePos);
+  // After applying only "offsetToBoundingBox", aCtx would have its origin at
+  // the top left corner of aFrame's bounding box (over all continuations).
+  // However, SVG painting needs the origin to be located at the origin of the
+  // SVG frame's "user space", i.e. the space in which, for example, the
+  // frame's BBox lives.
+  // SVG geometry frames and foreignObject frames apply their own offsets, so
+  // their position is relative to their user space. So for these frame types,
+  // if we want aCtx to be in user space, we first need to subtract the
+  // frame's position so that SVG painting can later add it again and the
+  // frame is painted in the right place.
+
+  gfxPoint toUserSpaceGfx = nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(aFrame);
+  nsPoint toUserSpace(nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.x)),
+                      nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.y)));
+  nsPoint offsetToUserSpace = offsetToBoundingBox - toUserSpace;
+
+  NS_ASSERTION(hasSVGLayout || offsetToBoundingBox == offsetToUserSpace,
+               "For non-SVG frames there shouldn't be any additional offset");
+
+  aCtx->Translate(offsetToUserSpace);
 
   gfxMatrix cssPxToDevPxMatrix = GetCSSPxToDevPxMatrix(aFrame);
 
   bool complexEffects = false;
   /* Check if we need to do additional operations on this child's
    * rendering, which necessitates rendering into another surface. */
   if (opacity != 1.0f || maskFrame || (clipPathFrame && !isTrivialClip)
       || aFrame->StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
     complexEffects = true;
     gfx->Save();
     aCtx->IntersectClip(aFrame->GetVisualOverflowRectRelativeToSelf() +
-                        svgGeomFramePos);
+                        toUserSpace);
     gfx->PushGroup(gfxContentType::COLOR_ALPHA);
   }
 
   /* If this frame has only a trivial clipPath, set up cairo's clipping now so
    * we can just do normal painting and get it clipped appropriately.
    */
   if (clipPathFrame && isTrivialClip) {
     gfx->Save();
     clipPathFrame->ClipPaint(aCtx, aFrame, cssPxToDevPxMatrix);
   }
 
   /* Paint the child */
   if (effectProperties.HasValidFilter()) {
     RegularFramePaintCallback callback(aBuilder, aLayerManager,
-                                       offsetWithoutSVGGeomFramePos);
-    nsRect dirtyRect = aDirtyRect - offset;
+                                       offsetToUserSpace);
+
+    nsRect dirtyRect = aDirtyRect - offsetToBoundingBox;
     nsFilterInstance::PaintFilteredFrame(aCtx, aFrame, &callback, &dirtyRect);
   } else {
     gfx->SetMatrix(matrixAutoSaveRestore.Matrix());
     aLayerManager->EndTransaction(FrameLayerBuilder::DrawThebesLayer, aBuilder);
-    aCtx->Translate(offsetWithoutSVGGeomFramePos);
+    aCtx->Translate(offsetToUserSpace);
   }
 
   if (clipPathFrame && isTrivialClip) {
     gfx->Restore();
   }
 
   /* No more effects, we're done. */
   if (!complexEffects) {
@@ -615,17 +627,17 @@ PaintFrameCallback::operator()(gfxContex
   aContext->Clip();
 
   aContext->Multiply(gfxMatrix(aTransform).Invert());
 
   // nsLayoutUtils::PaintFrame will anchor its painting at mFrame. But we want
   // to have it anchored at the top left corner of the bounding box of all of
   // mFrame's continuations. So we add a translation transform.
   int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
-  nsPoint offset = nsSVGIntegrationUtils::GetOffsetToUserSpace(mFrame);
+  nsPoint offset = GetOffsetToBoundingBox(mFrame);
   gfxPoint devPxOffset = gfxPoint(offset.x, offset.y) / appUnitsPerDevPixel;
   aContext->Multiply(gfxMatrix().Translate(devPxOffset));
 
   gfxSize paintServerSize =
     gfxSize(mPaintServerSize.width, mPaintServerSize.height) /
       mFrame->PresContext()->AppUnitsPerDevPixel();
 
   // nsLayoutUtils::PaintFrame wants to render with paintServerSize, but we
--- a/layout/svg/nsSVGIntegrationUtils.h
+++ b/layout/svg/nsSVGIntegrationUtils.h
@@ -38,43 +38,16 @@ class nsSVGIntegrationUtils MOZ_FINAL
 public:
   /**
    * Returns true if SVG effects are currently applied to this frame.
    */
   static bool
   UsingEffectsForFrame(const nsIFrame* aFrame);
 
   /**
-   * In SVG, an element's "user space" is simply the coordinate system in place
-   * at the time that it is drawn. For non-SVG frames, we want any SVG effects
-   * to be applied to the union of the border-box rects of all of a given
-   * frame's continuations. This means that, when we paint a non-SVG frame with
-   * effects, we want to offset the effects by the distance from the frame's
-   * origin (the top left of its border box) to the top left of the union of
-   * the border-box rects of all its continuations. In other words, we need to
-   * apply this offset as a suplimental translation to the current coordinate
-   * system in order to establish the correct user space before calling into
-   * the SVG effects code. For the purposes of the nsSVGIntegrationUtils code
-   * we somewhat misappropriate the term "user space" by using it to refer
-   * specifically to this adjusted coordinate system.
-   *
-   * For consistency with nsIFrame::GetOffsetTo, the offset this method returns
-   * is the offset you need to add to a point that's relative to aFrame's
-   * origin (the top left of its border box) to convert it to aFrame's user
-   * space. In other words the value returned is actually the offset from the
-   * origin of aFrame's user space to aFrame.
-   *
-   * Note: This method currently only accepts a frame's first continuation
-   * since none of our current callers need to be able to pass in other
-   * continuations.
-   */
-  static nsPoint
-  GetOffsetToUserSpace(nsIFrame* aFrame);
-
-  /**
    * Returns the size of the union of the border-box rects of all of
    * aNonSVGFrame's continuations.
    */
   static nsSize
   GetContinuationUnionSize(nsIFrame* aNonSVGFrame);
 
   /**
    * When SVG effects need to resolve percentage, userSpaceOnUse lengths, they
--- a/layout/svg/nsSVGUseFrame.cpp
+++ b/layout/svg/nsSVGUseFrame.cpp
@@ -182,16 +182,23 @@ nsSVGUseFrame::ReflowSVG()
   // handled by the nsSVGOuterSVGFrame for the anonymous <svg> that will be
   // created for that purpose.
   float x, y;
   static_cast<SVGUseElement*>(mContent)->
     GetAnimatedLengthValues(&x, &y, nullptr);
   mRect.MoveTo(nsLayoutUtils::RoundGfxRectToAppRect(
                  gfxRect(x, y, 0.0, 0.0),
                  PresContext()->AppUnitsPerCSSPixel()).TopLeft());
+
+  // If we have a filter, we need to invalidate ourselves because filter
+  // output can change even if none of our descendants need repainting.
+  if (StyleSVGReset()->HasFilters()) {
+    InvalidateFrame();
+  }
+
   nsSVGUseFrameBase::ReflowSVG();
 }
 
 void
 nsSVGUseFrame::NotifySVGChanged(uint32_t aFlags)
 {
   if (aFlags & COORD_CONTEXT_CHANGED &&
       !(aFlags & TRANSFORM_CHANGED)) {
--- a/layout/svg/nsSVGUtils.cpp
+++ b/layout/svg/nsSVGUtils.cpp
@@ -884,26 +884,57 @@ nsSVGUtils::GetBBox(nsIFrame *aFrame, ui
         !static_cast<const nsSVGElement*>(content)->HasValidDimensions()) {
       return bbox;
     }
     gfxMatrix matrix;
     if (aFrame->GetType() == nsGkAtoms::svgForeignObjectFrame) {
       // The spec says getBBox "Returns the tight bounding box in *current user
       // space*". So we should really be doing this for all elements, but that
       // needs investigation to check that we won't break too much content.
+      // NOTE: When changing this to apply to other frame types, make sure to
+      // also update nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset.
       NS_ABORT_IF_FALSE(content->IsSVG(), "bad cast");
       nsSVGElement *element = static_cast<nsSVGElement*>(content);
       matrix = element->PrependLocalTransformsTo(matrix,
                           nsSVGElement::eChildToUserSpace);
     }
     return svg->GetBBoxContribution(ToMatrix(matrix), aFlags).ToThebesRect();
   }
   return nsSVGIntegrationUtils::GetSVGBBoxForNonSVGFrame(aFrame);
 }
 
+gfxPoint
+nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(nsIFrame *aFrame)
+{
+  if (!(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
+    // The user space for non-SVG frames is defined as the bounding box of the
+    // frame's border-box rects over all continuations.
+    return gfxPoint();
+  }
+
+  // Leaf frames apply their own offset inside their user space.
+  if (aFrame->IsFrameOfType(nsIFrame::eSVGGeometry) ||
+      aFrame->IsSVGText()) {
+    return nsLayoutUtils::RectToGfxRect(aFrame->GetRect(),
+                                         nsPresContext::AppUnitsPerCSSPixel()).TopLeft();
+  }
+
+  // For foreignObject frames, nsSVGUtils::GetBBox applies their local
+  // transform, so we need to do the same here.
+  if (aFrame->GetType() == nsGkAtoms::svgForeignObjectFrame) {
+    gfxMatrix transform = static_cast<nsSVGElement*>(aFrame->GetContent())->
+        PrependLocalTransformsTo(gfxMatrix(),
+                                 nsSVGElement::eChildToUserSpace);
+    NS_ASSERTION(!transform.HasNonTranslation(), "we're relying on this being an offset-only transform");
+    return transform.GetTranslation();
+  }
+
+  return gfxPoint();
+}
+
 gfxRect
 nsSVGUtils::GetRelativeRect(uint16_t aUnits, const nsSVGLength2 *aXYWH,
                             const gfxRect &aBBox, nsIFrame *aFrame)
 {
   float x, y, width, height;
   if (aUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
     x = aBBox.X() + ObjectSpace(aBBox, &aXYWH[0]);
     y = aBBox.Y() + ObjectSpace(aBBox, &aXYWH[1]);
--- a/layout/svg/nsSVGUtils.h
+++ b/layout/svg/nsSVGUtils.h
@@ -400,16 +400,26 @@ public:
   };
   /**
    * Get the SVG bbox (the SVG spec's simplified idea of bounds) of aFrame in
    * aFrame's userspace.
    */
   static gfxRect GetBBox(nsIFrame *aFrame,
                          uint32_t aFlags = eBBoxIncludeFillGeometry);
 
+  /*
+   * "User space" is the space that the frame's BBox (as calculated by
+   * nsSVGUtils::GetBBox) is in. "Frame space" is the space that has its origin
+   * at the top left of the union of the frame's border-box rects over all
+   * continuations.
+   * This function returns the offset one needs to add to something in frame
+   * space in order to get its coordinates in user space.
+   */
+  static gfxPoint FrameSpaceInCSSPxToUserSpaceOffset(nsIFrame *aFrame);
+
   /**
    * Convert a userSpaceOnUse/objectBoundingBoxUnits rectangle that's specified
    * using four nsSVGLength2 values into a user unit rectangle in user space.
    *
    * @param aXYWH pointer to 4 consecutive nsSVGLength2 objects containing
    * the x, y, width and height values in that order
    * @param aBBox the bounding box of the object the rect is relative to;
    * may be null if aUnits is not SVG_UNIT_TYPE_OBJECTBOUNDINGBOX
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -1521,17 +1521,17 @@ pref("intl.hyphenation-alias.bs-*", "sh"
 
 // Norwegian has two forms, Bokmål and Nynorsk, with "no" as a macrolanguage encompassing both.
 // For "no", we'll alias to "nb" (Bokmål) as that is the more widely used written form.
 pref("intl.hyphenation-alias.no", "nb");
 pref("intl.hyphenation-alias.no-*", "nb");
 pref("intl.hyphenation-alias.nb-*", "nb");
 pref("intl.hyphenation-alias.nn-*", "nn");
 
-pref("font.mathfont-family", "MathJax_Main, STIXNonUnicode, STIXSizeOneSym, STIXSize1, STIXGeneral, Asana Math, Standard Symbols L, DejaVu Sans, Cambria Math");
+pref("font.mathfont-family", "MathJax_Main, STIXNonUnicode, STIXSizeOneSym, STIXGeneral, Asana Math, Standard Symbols L, DejaVu Sans, Cambria Math");
 
 // Some CJK fonts have bad underline offset, their CJK character glyphs are overlapped (or adjoined)  to its underline.
 // These fonts are ignored the underline offset, instead of it, the underline is lowered to bottom of its em descent.
 pref("font.blacklist.underline_offset", "FangSong,Gulim,GulimChe,MingLiU,MingLiU-ExtB,MingLiU_HKSCS,MingLiU-HKSCS-ExtB,MS Gothic,MS Mincho,MS PGothic,MS PMincho,MS UI Gothic,PMingLiU,PMingLiU-ExtB,SimHei,SimSun,SimSun-ExtB,Hei,Kai,Apple LiGothic,Apple LiSung,Osaka");
 
 #ifdef MOZ_WIDGET_GONK
 // Whitelist of fonts that ship with B2G that do not include space lookups in
 // default features. This allows us to skip analyzing the GSUB/GPOS tables
@@ -2318,21 +2318,22 @@ pref("font.name-list.monospace.zh-TW", "
 // Hong Kong users have the same demand about glyphs for Latin letters (bug 88579)
 pref("font.name.serif.zh-HK", "Times New Roman");
 pref("font.name.sans-serif.zh-HK", "Arial");
 pref("font.name.monospace.zh-HK", "MingLiu_HKSCS");
 pref("font.name-list.serif.zh-HK", "MingLiu_HKSCS, Ming(for ISO10646), MingLiU, MingLiU_HKSCS-ExtB");
 pref("font.name-list.sans-serif.zh-HK", "MingLiU_HKSCS, Ming(for ISO10646), MingLiU, MingLiU_HKSCS-ExtB");
 pref("font.name-list.monospace.zh-HK", "MingLiU_HKSCS, Ming(for ISO10646), MingLiU, MingLiU_HKSCS-ExtB");
 
-pref("font.name.serif.x-devanagari", "Mangal");
-pref("font.name.sans-serif.x-devanagari", "Raghindi");
+pref("font.name.serif.x-devanagari", "Kokila");
+pref("font.name.sans-serif.x-devanagari", "Nirmala UI");
 pref("font.name.monospace.x-devanagari", "Mangal");
-pref("font.name-list.serif.x-devanagari", "Mangal, Raghindi");
-pref("font.name-list.monospace.x-devanagari", "Mangal, Raghindi");
+pref("font.name-list.serif.x-devanagari", "Kokila, Raghindi");
+pref("font.name-list.sans-serif.x-devanagari", "Nirmala UI, Mangal");
+pref("font.name-list.monospace.x-devanagari", "Mangal, Nirmala UI");
 
 pref("font.name.serif.x-tamil", "Latha");
 pref("font.name.sans-serif.x-tamil", "Code2000");
 pref("font.name.monospace.x-tamil", "Latha");
 pref("font.name-list.serif.x-tamil", "Latha, Code2000");
 pref("font.name-list.monospace.x-tamil", "Latha, Code2000");
 
 # http://www.alanwood.net/unicode/fonts.html
@@ -2465,17 +2466,17 @@ pref("font.size.fixed.x-baltic", 13);
 pref("font.default.x-central-euro", "serif");
 pref("font.size.variable.x-central-euro", 16);
 pref("font.size.fixed.x-central-euro", 13);
 
 pref("font.default.x-cyrillic", "serif");
 pref("font.size.variable.x-cyrillic", 16);
 pref("font.size.fixed.x-cyrillic", 13);
 
-pref("font.default.x-devanagari", "serif");
+pref("font.default.x-devanagari", "sans-serif");
 pref("font.size.variable.x-devanagari", 16);
 pref("font.size.fixed.x-devanagari", 13);
 
 pref("font.default.x-tamil", "serif");
 pref("font.size.variable.x-tamil", 16);
 pref("font.size.fixed.x-tamil", 13);
 
 pref("font.default.x-armn", "serif");
@@ -2550,17 +2551,17 @@ pref("font.default.zh-TW", "sans-serif")
 pref("font.size.variable.zh-TW", 16);
 pref("font.size.fixed.zh-TW", 16);
 
 pref("font.default.zh-HK", "sans-serif");
 pref("font.size.variable.zh-HK", 16);
 pref("font.size.fixed.zh-HK", 16);
 
 // We have special support for Monotype Symbol on Windows.
-pref("font.mathfont-family", "MathJax_Main, STIXNonUnicode, STIXSizeOneSym, STIXSize1, STIXGeneral, Asana Math, Symbol, DejaVu Sans, Cambria Math");
+pref("font.mathfont-family", "MathJax_Main, STIXNonUnicode, STIXSizeOneSym, STIXGeneral, Asana Math, Symbol, DejaVu Sans, Cambria Math");
 
 // cleartype settings - false implies default system settings
 
 // use cleartype rendering for downloadable fonts (win xp only)
 pref("gfx.font_rendering.cleartype.use_for_downloadable_fonts", true);
 
 // use cleartype rendering for all fonts always (win xp only)
 pref("gfx.font_rendering.cleartype.always_use_for_content", false);
@@ -3082,17 +3083,17 @@ pref("font.default.zh-TW", "sans-serif")
 pref("font.size.variable.zh-TW", 15);
 pref("font.size.fixed.zh-TW", 16);
 
 pref("font.default.zh-HK", "sans-serif");
 pref("font.size.variable.zh-HK", 15);
 pref("font.size.fixed.zh-HK", 16);
 
 // Apple's Symbol is Unicode so use it
-pref("font.mathfont-family", "MathJax_Main, STIXNonUnicode, STIXSizeOneSym, STIXSize1, STIXGeneral, Asana Math, Symbol, DejaVu Sans, Cambria Math");
+pref("font.mathfont-family", "MathJax_Main, STIXNonUnicode, STIXSizeOneSym, STIXGeneral, Asana Math, Symbol, DejaVu Sans, Cambria Math");
 
 // individual font faces to be treated as independent families
 // names are Postscript names of each face
 pref("font.single-face-list", "Osaka-Mono");
 
 // optimization hint for fonts with localized names to be read in at startup, otherwise read in at lookup miss
 // names are canonical family names (typically English names)
 pref("font.preload-names-list", "Hiragino Kaku Gothic ProN,Hiragino Mincho ProN,STSong");
--- a/testing/mozbase/mozversion/mozversion/mozversion.py
+++ b/testing/mozbase/mozversion/mozversion/mozversion.py
@@ -38,24 +38,28 @@ class Version(mozlog.LoggingMixin):
 
     def get_gecko_info(self, config_path):
         for filename, section in (('application', 'App'),
                                   ('platform', 'Build')):
             config = ConfigParser.RawConfigParser()
             config_file = os.path.join(config_path, '%s.ini' % filename)
             if os.path.exists(config_file):
                 config.read(config_file)
-                name_map = {'CodeName': 'code_name',
+                name_map = {'CodeName': 'display_name',
                             'SourceRepository': 'repository',
                             'SourceStamp': 'changeset'}
                 for key in ('BuildID', 'Name', 'CodeName', 'Version',
                             'SourceRepository', 'SourceStamp'):
                     name = name_map.get(key, key).lower()
                     self._info['%s_%s' % (filename, name)] = config.has_option(
                         section, key) and config.get(section, key) or None
+
+                if not self._info.get('application_display_name'):
+                    self._info['application_display_name'] = \
+                        self._info.get('application_name')
             else: