Merge m-i to m-c, a=merge
authorPhil Ringnalda <philringnalda@gmail.com>
Wed, 24 Dec 2014 15:38:41 -0800
changeset 221301 2acb12da981391c0fdd9fe38b5089a0f20d5c43e
parent 221239 5cc172f5e94c21971023589d699b88be85a241a2 (current diff)
parent 221300 af9239757205e91a20070c548da5b3679c393f73 (diff)
child 221302 687012e14ec9fd24c81d48b1e2e076e96e23d1ce
child 221328 d6d081fd3b8148dfcf254a16e8e323b713e3f799
push id28015
push userphilringnalda@gmail.com
push dateWed, 24 Dec 2014 23:38:54 +0000
treeherdermozilla-central@2acb12da9813 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone37.0a1
first release with
nightly linux32
2acb12da9813 / 37.0a1 / 20141225030205 / files
nightly linux64
2acb12da9813 / 37.0a1 / 20141225030205 / files
nightly mac
2acb12da9813 / 37.0a1 / 20141225030205 / files
nightly win32
2acb12da9813 / 37.0a1 / 20141225030205 / files
nightly win64
2acb12da9813 / 37.0a1 / 20141225030205 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-i to m-c, a=merge
js/src/tests/ecma_7/SIMD/coercions.js
media/webrtc/trunk/webrtc/tools/e2e_quality/audio/perf
--- a/build/clang-plugin/tests/Makefile.in
+++ b/build/clang-plugin/tests/Makefile.in
@@ -3,12 +3,12 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 # Build without any warning flags, and with clang verify flag for a
 # syntax-only build (no codegen).
 OS_CXXFLAGS := $(filter-out -W%,$(OS_CXXFLAGS)) -fsyntax-only -Xclang -verify
 
 include $(topsrcdir)/config/rules.mk
 
-target:: $(OBJS)
+export:: $(OBJS)
 
 # We don't actually build anything.
 .PHONY: $(OBJS)
--- a/dom/base/nsHostObjectProtocolHandler.cpp
+++ b/dom/base/nsHostObjectProtocolHandler.cpp
@@ -637,18 +637,23 @@ NS_GetStreamForBlobURI(nsIURI* aURI, nsI
   return blobImpl->GetInternalStream(aStream);
 }
 
 nsresult
 NS_GetStreamForMediaStreamURI(nsIURI* aURI, mozilla::DOMMediaStream** aStream)
 {
   NS_ASSERTION(IsMediaStreamURI(aURI), "Only call this with mediastream URIs");
 
+  nsISupports* dataObject = GetDataObject(aURI);
+  if (!dataObject) {
+    return NS_ERROR_DOM_BAD_URI;
+  }
+
   *aStream = nullptr;
-  return CallQueryInterface(GetDataObject(aURI), aStream);
+  return CallQueryInterface(dataObject, aStream);
 }
 
 NS_IMETHODIMP
 nsFontTableProtocolHandler::NewURI(const nsACString& aSpec,
                                    const char *aCharset,
                                    nsIURI *aBaseURI,
                                    nsIURI **aResult)
 {
--- a/dom/browser-element/BrowserElementChildPreload.js
+++ b/dom/browser-element/BrowserElementChildPreload.js
@@ -1338,17 +1338,16 @@ BrowserElementChild.prototype = {
       else if (state & Ci.nsIWebProgressListener.STATE_IS_INSECURE) {
         stateDesc = 'insecure';
       }
       else {
         debug("Unexpected securitychange state!");
         stateDesc = '???';
       }
 
-      // XXX Until bug 764496 is fixed, this will always return false.
       var isEV = !!(state & Ci.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL);
 
       sendAsyncMsg('securitychange', { state: stateDesc, extendedValidation: isEV });
     },
 
     onStatusChange: function(webProgress, request, status, message) {},
     onProgressChange: function(webProgress, request, curSelfProgress,
                                maxSelfProgress, curTotalProgress, maxTotalProgress) {},
--- a/dom/media/fmp4/apple/AppleVDADecoder.cpp
+++ b/dom/media/fmp4/apple/AppleVDADecoder.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include <CoreFoundation/CFString.h>
 
 #include "AppleUtils.h"
 #include "AppleVDADecoder.h"
 #include "AppleVDALinker.h"
 #include "mp4_demuxer/DecoderData.h"
+#include "mp4_demuxer/H264.h"
 #include "MP4Decoder.h"
 #include "MediaData.h"
 #include "MacIOSurfaceImage.h"
 #include "mozilla/ArrayUtils.h"
 #include "nsAutoPtr.h"
 #include "nsCocoaFeatures.h"
 #include "nsThreadUtils.h"
 #include "prlog.h"
@@ -38,19 +39,38 @@ AppleVDADecoder::AppleVDADecoder(const m
   , mTaskQueue(aVideoTaskQueue)
   , mCallback(aCallback)
   , mImageContainer(aImageContainer)
   , mDecoder(nullptr)
   , mIs106(!nsCocoaFeatures::OnLionOrLater())
 {
   MOZ_COUNT_CTOR(AppleVDADecoder);
   // TODO: Verify aConfig.mime_type.
+
+  // Retrieve video dimensions from H264 SPS NAL.
+  mPictureWidth = mConfig.image_width;
+  mPictureHeight = mConfig.image_height;
+  mMaxRefFrames = 4;
+  mp4_demuxer::SPSData spsdata;
+  if (mp4_demuxer::H264::DecodeSPSFromExtraData(mConfig.extra_data, spsdata) &&
+      spsdata.pic_width && spsdata.pic_height) {
+    mPictureWidth = spsdata.pic_width;
+    mPictureHeight = spsdata.pic_height;
+    // max_num_ref_frames determines the size of the sliding window
+    // we need to queue that many frames in order to guarantee proper
+    // pts frames ordering. Use a minimum of 4 to ensure proper playback of
+    // non compliant videos.
+    mMaxRefFrames =
+      (spsdata.max_num_ref_frames + 1) > mMaxRefFrames ?
+        spsdata.max_num_ref_frames + 1 : mMaxRefFrames;
+  }
+
   LOG("Creating AppleVDADecoder for %dx%d h.264 video",
-      mConfig.image_width,
-      mConfig.image_height
+      mPictureWidth,
+      mPictureHeight
      );
 }
 
 AppleVDADecoder::~AppleVDADecoder()
 {
   MOZ_COUNT_DTOR(AppleVDADecoder);
 }
 
@@ -265,27 +285,19 @@ AppleVDADecoder::OutputFrame(CVPixelBuff
     mCallback->Error();
     return NS_ERROR_FAILURE;
   }
 
   // Frames come out in DTS order but we need to output them
   // in composition order.
   mReorderQueue.Push(data);
   // Assume a frame with a PTS <= current DTS is ready.
-  while (mReorderQueue.Length() > 0) {
+  while (mReorderQueue.Length() > mMaxRefFrames) {
     nsRefPtr<VideoData> readyData = mReorderQueue.Pop();
-    if (readyData->mTime <= aFrameRef->decode_timestamp) {
-      LOG("returning queued frame with pts %lld", readyData->mTime);
       mCallback->Output(readyData);
-    } else {
-      LOG("requeued frame with pts %lld > %lld",
-          readyData->mTime, aFrameRef->decode_timestamp);
-      mReorderQueue.Push(readyData);
-      break;
-    }
   }
   LOG("%llu decoded frames queued",
       static_cast<unsigned long long>(mReorderQueue.Length()));
 
   return NS_OK;
 }
 
 nsresult
@@ -406,21 +418,21 @@ AppleVDADecoder::CreateDecoderSpecificat
 {
   const uint8_t* extradata = mConfig.extra_data->Elements();
   int extrasize = mConfig.extra_data->Length();
 
   OSType format = 'avc1';
   AutoCFRelease<CFNumberRef> avc_width  =
     CFNumberCreate(kCFAllocatorDefault,
                    kCFNumberSInt32Type,
-                   &mConfig.image_width);
+                   &mPictureWidth);
   AutoCFRelease<CFNumberRef> avc_height =
     CFNumberCreate(kCFAllocatorDefault,
                    kCFNumberSInt32Type,
-                   &mConfig.image_height);
+                   &mPictureHeight);
   AutoCFRelease<CFNumberRef> avc_format =
     CFNumberCreate(kCFAllocatorDefault,
                    kCFNumberSInt32Type,
                    &format);
   AutoCFRelease<CFDataRef> avc_data =
     CFDataCreate(kCFAllocatorDefault,
                  extradata,
                  extrasize);
--- a/dom/media/fmp4/apple/AppleVDADecoder.h
+++ b/dom/media/fmp4/apple/AppleVDADecoder.h
@@ -85,16 +85,19 @@ public:
   void ClearReorderedFrames();
   CFDictionaryRef CreateOutputConfiguration();
 
   const mp4_demuxer::VideoDecoderConfig& mConfig;
   nsRefPtr<MediaTaskQueue> mTaskQueue;
   MediaDataDecoderCallback* mCallback;
   nsRefPtr<layers::ImageContainer> mImageContainer;
   ReorderQueue mReorderQueue;
+  uint32_t mPictureWidth;
+  uint32_t mPictureHeight;
+  uint32_t mMaxRefFrames;
 
 private:
   VDADecoder mDecoder;
   bool mIs106;
 
   // Method to pass a frame to VideoToolbox for decoding.
   nsresult SubmitFrame(mp4_demuxer::MP4Sample* aSample);
   // Method to set up the decompression session.
--- a/dom/media/fmp4/apple/AppleVTDecoder.cpp
+++ b/dom/media/fmp4/apple/AppleVTDecoder.cpp
@@ -6,16 +6,17 @@
 
 #include <CoreFoundation/CFString.h>
 
 #include "AppleCMLinker.h"
 #include "AppleUtils.h"
 #include "AppleVTDecoder.h"
 #include "AppleVTLinker.h"
 #include "mp4_demuxer/DecoderData.h"
+#include "mp4_demuxer/H264.h"
 #include "MediaData.h"
 #include "MacIOSurfaceImage.h"
 #include "mozilla/ArrayUtils.h"
 #include "nsAutoPtr.h"
 #include "nsThreadUtils.h"
 #include "prlog.h"
 #include "VideoUtils.h"
 
@@ -270,18 +271,18 @@ AppleVTDecoder::InitializeSession()
   LOG("AVCDecoderConfig %ld bytes sha1 %s",
       mConfig.extra_data->Length(), avc_digest.get());
 #endif // LOG_MEDIA_SHA1
 
   AutoCFRelease<CFDictionaryRef> extensions = CreateDecoderExtensions();
 
   rv = CMVideoFormatDescriptionCreate(kCFAllocatorDefault,
                                       kCMVideoCodecType_H264,
-                                      mConfig.image_width,
-                                      mConfig.image_height,
+                                      mPictureWidth,
+                                      mPictureHeight,
                                       extensions,
                                       &mFormat);
   if (rv != noErr) {
     NS_ERROR("Couldn't create format description!");
     return NS_ERROR_FAILURE;
   }
 
   // Contruct video decoder selection spec.
--- a/ipc/glue/Shmem.h
+++ b/ipc/glue/Shmem.h
@@ -260,17 +260,17 @@ private:
       reinterpret_cast<char*>(aSegment->memory()) + aSegment->Size();
     return reinterpret_cast<uint32_t*>(endOfSegment - sizeof(uint32_t));
   }
 
 #else
   void AssertInvariants() const;
 #endif
 
-  SharedMemory* mSegment;
+  SharedMemory* MOZ_NON_OWNING_REF mSegment;
   void* mData;
   size_t mSize;
   id_t mId;
 };
 
 
 } // namespace ipc
 } // namespace mozilla
--- a/ipc/ipdl/moz.build
+++ b/ipc/ipdl/moz.build
@@ -8,13 +8,11 @@ if CONFIG['MOZ_IPDL_TESTS']:
     DIRS += ['test']
 
 FAIL_ON_WARNINGS = True
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
-GENERATED_INCLUDES += ['/ipc/ipdl/_ipdlheaders']
-
 # We #include some things in the dom/plugins/ directory that rely on
 # toolkit libraries.
 CXXFLAGS += CONFIG['TK_CFLAGS']
--- a/js/src/builtin/SIMD.cpp
+++ b/js/src/builtin/SIMD.cpp
@@ -8,16 +8,17 @@
  * JS SIMD pseudo-module.
  * Specification matches polyfill:
  * https://github.com/johnmccutchan/ecmascript_simd/blob/master/src/ecmascript_simd.js
  * The objects float32x4 and int32x4 are installed on the SIMD pseudo-module.
  */
 
 #include "builtin/SIMD.h"
 
+#include "mozilla/IntegerTypeTraits.h"
 #include "jsapi.h"
 #include "jsfriendapi.h"
 
 #include "builtin/TypedObject.h"
 #include "js/Value.h"
 
 #include "jsobjinlines.h"
 
@@ -142,22 +143,27 @@ static bool SignMask(JSContext *cx, unsi
     TypeDescr &descr = typedObj.typeDescr();
     if (descr.kind() != type::Simd || descr.as<SimdTypeDescr>().type() != SimdType::type) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
                              SimdTypeDescr::class_.name, "signMask",
                              InformalValueTypeName(args.thisv()));
         return false;
     }
 
-    Elem *data = reinterpret_cast<Elem *>(typedObj.typedMem());
-    int32_t mx = data[0] < 0.0 ? 1 : 0;
-    int32_t my = data[1] < 0.0 ? 1 : 0;
-    int32_t mz = data[2] < 0.0 ? 1 : 0;
-    int32_t mw = data[3] < 0.0 ? 1 : 0;
-    int32_t result = mx | my << 1 | mz << 2 | mw << 3;
+    // Load the data as integer so that we treat the sign bit consistently,
+    // since -0.0 is not less than zero, but it still has the sign bit set.
+    typedef typename mozilla::SignedStdintTypeForSize<sizeof(Elem)>::Type Int;
+    static_assert(SimdType::lanes * sizeof(Int) <= jit::Simd128DataSize,
+                  "signMask access should respect the bounds of the type");
+    const Elem *elems = reinterpret_cast<const Elem *>(typedObj.typedMem());
+    int32_t result = 0;
+    for (unsigned i = 0; i < SimdType::lanes; ++i) {
+        Int x = mozilla::BitwiseCast<Int>(elems[i]);
+        result |= (x < 0) << i;
+    }
     args.rval().setInt32(result);
     return true;
 }
 
 #define SIGN_MASK(type) \
 static bool type##SignMask(JSContext *cx, unsigned argc, Value *vp) { \
     return SignMask<type>(cx, argc, vp); \
 }
@@ -291,53 +297,46 @@ CreateSimdClass(JSContext *cx, Handle<Gl
 
     return typeDescr;
 }
 
 bool
 SimdTypeDescr::call(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    const unsigned LANES = 4;
 
     Rooted<SimdTypeDescr*> descr(cx, &args.callee().as<SimdTypeDescr>());
     if (args.length() == 1) {
         // SIMD type used as a coercion
         if (!CheckVectorObject(args[0], descr->type())) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SIMD_NOT_A_VECTOR);
             return false;
         }
 
         args.rval().setObject(args[0].toObject());
         return true;
     }
 
-    if (args.length() < LANES) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
-                             args.callee().getClass()->name, "3", "s");
-        return false;
-    }
-
     Rooted<TypedObject*> result(cx, TypedObject::createZeroed(cx, descr, 0));
     if (!result)
         return false;
 
     switch (descr->type()) {
       case SimdTypeDescr::TYPE_INT32: {
         int32_t *mem = reinterpret_cast<int32_t*>(result->typedMem());
         for (unsigned i = 0; i < 4; i++) {
-            if (!ToInt32(cx, args[i], &mem[i]))
+            if (!ToInt32(cx, args.get(i), &mem[i]))
                 return false;
         }
         break;
       }
       case SimdTypeDescr::TYPE_FLOAT32: {
         float *mem = reinterpret_cast<float*>(result->typedMem());
         for (unsigned i = 0; i < 4; i++) {
-            if (!RoundFloat32(cx, args[i], &mem[i]))
+            if (!RoundFloat32(cx, args.get(i), &mem[i]))
                 return false;
         }
         break;
       }
     }
     args.rval().setObject(*result);
     return true;
 }
--- a/js/src/jit-test/tests/asm.js/testBug1099216.js
+++ b/js/src/jit-test/tests/asm.js/testBug1099216.js
@@ -1,8 +1,13 @@
+if (typeof SIMD === 'undefined' || !isSimdAvailable()) {
+    print("won't run tests as simd extensions aren't activated yet");
+    quit(0);
+}
+
 (function(global) {
     "use asm";
     var frd = global.Math.fround;
     var fx4 = global.SIMD.float32x4;
     var fsp = fx4.splat;
     function s(){}
     function d(x){x=fx4(x);}
     function e() {
--- a/js/src/jit-test/tests/asm.js/testSIMD.js
+++ b/js/src/jit-test/tests/asm.js/testSIMD.js
@@ -183,17 +183,17 @@ assertAsmTypeFail('glob', USE_ASM + "fun
 assertAsmTypeFail('glob', USE_ASM + "function f() {var x=42.; return x.signMask;} return f");
 assertAsmTypeFail('glob', USE_ASM + FROUND + "function f() {var x=f32(42.); return x.signMask;} return f");
 
 assertEq(asmLink(asmCompile('glob', USE_ASM + I32 + 'function f() { var x=i4(1,2,3,4); return x.signMask | 0 } return f'), this)(), 0b0000);
 assertEq(asmLink(asmCompile('glob', USE_ASM + I32 + 'function f() { var x=i4(0,-1, ' + INT32_MAX + ',' + INT32_MIN  + '); return x.signMask | 0 } return f'), this)(), 0b1010);
 
 assertEq(asmLink(asmCompile('glob', USE_ASM + F32 + FROUND + 'var Infinity = glob.Infinity; function f() { var x=f4(0,0,0,0); x=f4(f32(1), f32(-13.37), f32(42), f32(-Infinity)); return x.signMask | 0 } return f'), this)(), 0b1010);
 assertEq(asmLink(asmCompile('glob', USE_ASM + F32 + FROUND + 'var Infinity = glob.Infinity; function f() { var x=f4(0,0,0,0); x=f4(f32(-1), f32(0), f32(-0.000001), f32(Infinity)); return x.signMask | 0 } return f'), this)(), 0b0101);
-assertEq(asmLink(asmCompile('glob', USE_ASM + F32 + FROUND + 'var NaN = glob.NaN; function f() { var x=f4(0,0,0,0); x=f4(f32(-1), f32(NaN), f32(3.), f32(4.)); return x.signMask | 0 } return f'), this)(), 0b0001);
+assertEq(asmLink(asmCompile('glob', USE_ASM + F32 + FROUND + 'var NaN = glob.NaN; function f() { var x=f4(0,0,0,0); x=f4(f32(-1), f32(NaN), f32(-0), f32(0)); return x.signMask | 0 } return f'), this)(), 0b0101);
 
 // 1.3.3. Variable assignments
 assertAsmTypeFail('glob', USE_ASM + I32 + I32A + "function f() {var x=i4(1,2,3,4); x=i4();} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + I32A + "function f() {var x=i4(1,2,3,4); x=i4(1);} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + I32A + "function f() {var x=i4(1,2,3,4); x=i4(1, 2);} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + I32A + "function f() {var x=i4(1,2,3,4); x=i4(1, 2, 3);} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + I32A + "function f() {var x=i4(1,2,3,4); x=i4(1.0, 2, 3, 4);} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + I32A + "function f() {var x=i4(1,2,3,4); x=i4(1, 2.0, 3, 4);} return f");
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -1787,16 +1787,21 @@ CodeGenerator::visitLabel(LLabel *lir)
 }
 
 void
 CodeGenerator::visitNop(LNop *lir)
 {
 }
 
 void
+CodeGenerator::visitMop(LMop *lir)
+{
+}
+
+void
 CodeGenerator::visitOsiPoint(LOsiPoint *lir)
 {
     // Note: markOsiPoint ensures enough space exists between the last
     // LOsiPoint and this one to patch adjacent call instructions.
 
     MOZ_ASSERT(masm.framePushed() == frameSize());
 
     uint32_t osiCallPointOffset = markOsiPoint(lir);
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -58,16 +58,17 @@ class CodeGenerator : public CodeGenerat
 
   public:
     bool generate();
     bool generateAsmJS(AsmJSFunctionLabels *labels);
     bool link(JSContext *cx, types::CompilerConstraintList *constraints);
 
     void visitLabel(LLabel *lir);
     void visitNop(LNop *lir);
+    void visitMop(LMop *lir);
     void visitOsiPoint(LOsiPoint *lir);
     void visitGoto(LGoto *lir);
     void visitTableSwitch(LTableSwitch *ins);
     void visitTableSwitchV(LTableSwitchV *ins);
     void visitCloneLiteral(LCloneLiteral *lir);
     void visitParameter(LParameter *lir);
     void visitCallee(LCallee *lir);
     void visitIsConstructing(LIsConstructing *lir);
--- a/js/src/jit/EffectiveAddressAnalysis.cpp
+++ b/js/src/jit/EffectiveAddressAnalysis.cpp
@@ -16,20 +16,20 @@ AnalyzeLsh(TempAllocator &alloc, MLsh *l
 {
     if (lsh->specialization() != MIRType_Int32)
         return;
 
     MDefinition *index = lsh->lhs();
     MOZ_ASSERT(index->type() == MIRType_Int32);
 
     MDefinition *shift = lsh->rhs();
-    if (!shift->isConstant())
+    if (!shift->isConstantValue())
         return;
 
-    Value shiftValue = shift->toConstant()->value();
+    Value shiftValue = shift->constantValue();
     if (!shiftValue.isInt32() || !IsShiftInScaleRange(shiftValue.toInt32()))
         return;
 
     Scale scale = ShiftToScale(shiftValue.toInt32());
 
     int32_t displacement = 0;
     MInstruction *last = lsh;
     MDefinition *base = nullptr;
@@ -42,18 +42,18 @@ AnalyzeLsh(TempAllocator &alloc, MLsh *l
             break;
 
         MAdd *add = use->consumer()->toDefinition()->toAdd();
         if (add->specialization() != MIRType_Int32 || !add->isTruncated())
             break;
 
         MDefinition *other = add->getOperand(1 - add->indexOf(*use));
 
-        if (other->isConstant()) {
-            displacement += other->toConstant()->value().toInt32();
+        if (other->isConstantValue()) {
+            displacement += other->constantValue().toInt32();
         } else {
             if (base)
                 break;
             base = other;
         }
 
         last = add;
     }
@@ -67,21 +67,21 @@ AnalyzeLsh(TempAllocator &alloc, MLsh *l
             return;
 
         MUseIterator use = last->usesBegin();
         if (!use->consumer()->isDefinition() || !use->consumer()->toDefinition()->isBitAnd())
             return;
 
         MBitAnd *bitAnd = use->consumer()->toDefinition()->toBitAnd();
         MDefinition *other = bitAnd->getOperand(1 - bitAnd->indexOf(*use));
-        if (!other->isConstant() || !other->toConstant()->value().isInt32())
+        if (!other->isConstantValue() || !other->constantValue().isInt32())
             return;
 
         uint32_t bitsClearedByShift = elemSize - 1;
-        uint32_t bitsClearedByMask = ~uint32_t(other->toConstant()->value().toInt32());
+        uint32_t bitsClearedByMask = ~uint32_t(other->constantValue().toInt32());
         if ((bitsClearedByShift & bitsClearedByMask) != bitsClearedByMask)
             return;
 
         bitAnd->replaceAllUsesWith(last);
         return;
     }
 
     MEffectiveAddress *eaddr = MEffectiveAddress::New(alloc, base, index, scale, displacement);
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -255,29 +255,29 @@ MaybeFoldConditionBlock(MIRGraph &graph,
 
     // If either trueBranch or falseBranch just computes a constant for the
     // test, determine the block that branch will end up jumping to and eliminate
     // the branch. Otherwise, change the end of the block to a test that jumps
     // directly to successors of testBlock, rather than to testBlock itself.
 
     MBasicBlock *trueTarget = trueBranch;
     if (BlockComputesConstant(trueBranch, trueResult)) {
-        trueTarget = trueResult->toConstant()->valueToBoolean()
+        trueTarget = trueResult->constantToBoolean()
                      ? finalTest->ifTrue()
                      : finalTest->ifFalse();
         testBlock->removePredecessor(trueBranch);
         graph.removeBlock(trueBranch);
     } else {
         UpdateTestSuccessors(graph.alloc(), trueBranch, trueResult,
                              finalTest->ifTrue(), finalTest->ifFalse(), testBlock);
     }
 
     MBasicBlock *falseTarget = falseBranch;
     if (BlockComputesConstant(falseBranch, falseResult)) {
-        falseTarget = falseResult->toConstant()->valueToBoolean()
+        falseTarget = falseResult->constantToBoolean()
                       ? finalTest->ifTrue()
                       : finalTest->ifFalse();
         testBlock->removePredecessor(falseBranch);
         graph.removeBlock(falseBranch);
     } else {
         UpdateTestSuccessors(graph.alloc(), falseBranch, falseResult,
                              finalTest->ifTrue(), finalTest->ifFalse(), testBlock);
     }
@@ -2265,18 +2265,18 @@ SimpleLinearSum
 jit::ExtractLinearSum(MDefinition *ins)
 {
     if (ins->isBeta())
         ins = ins->getOperand(0);
 
     if (ins->type() != MIRType_Int32)
         return SimpleLinearSum(ins, 0);
 
-    if (ins->isConstant()) {
-        const Value &v = ins->toConstant()->value();
+    if (ins->isConstantValue()) {
+        const Value &v = ins->constantValue();
         MOZ_ASSERT(v.isInt32());
         return SimpleLinearSum(nullptr, v.toInt32());
     } else if (ins->isAdd() || ins->isSub()) {
         MDefinition *lhs = ins->getOperand(0);
         MDefinition *rhs = ins->getOperand(1);
         if (lhs->type() == MIRType_Int32 && rhs->type() == MIRType_Int32) {
             SimpleLinearSum lsum = ExtractLinearSum(lhs);
             SimpleLinearSum rsum = ExtractLinearSum(rhs);
@@ -2667,18 +2667,18 @@ LinearSum::add(SimpleLinearSum other, in
 bool
 LinearSum::add(MDefinition *term, int32_t scale)
 {
     MOZ_ASSERT(term);
 
     if (scale == 0)
         return true;
 
-    if (term->isConstant()) {
-        int32_t constant = term->toConstant()->value().toInt32();
+    if (term->isConstantValue()) {
+        int32_t constant = term->constantValue().toInt32();
         if (!SafeMul(constant, scale, &constant))
             return false;
         return add(constant);
     }
 
     for (size_t i = 0; i < terms_.length(); i++) {
         if (term == terms_[i].term) {
             if (!SafeAdd(scale, terms_[i].scale, &terms_[i].scale))
@@ -2746,17 +2746,17 @@ LinearSum::dump() const
 
 MDefinition *
 jit::ConvertLinearSum(TempAllocator &alloc, MBasicBlock *block, const LinearSum &sum, bool convertConstant)
 {
     MDefinition *def = nullptr;
 
     for (size_t i = 0; i < sum.numTerms(); i++) {
         LinearTerm term = sum.term(i);
-        MOZ_ASSERT(!term.term->isConstant());
+        MOZ_ASSERT(!term.term->isConstantValue());
         if (term.scale == 1) {
             if (def) {
                 def = MAdd::New(alloc, def, term.term);
                 def->toAdd()->setInt32();
                 block->insertAtEnd(def->toInstruction());
                 def->computeRange(alloc);
             } else {
                 def = term.term;
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -2256,19 +2256,18 @@ IonBuilder::processDoWhileCondEnd(CFGSta
 
     // Pop the last value, and create the successor block.
     MDefinition *vins = current->pop();
     MBasicBlock *successor = newBlock(current, GetNextPc(pc), loopDepth_ - 1);
     if (!successor)
         return ControlStatus_Error;
 
     // Test for do {} while(false) and don't create a loop in that case.
-    if (vins->isConstant()) {
-        MConstant *cte = vins->toConstant();
-        if (cte->value().isBoolean() && !cte->value().toBoolean()) {
+    if (vins->isConstantValue() && !vins->constantValue().isMagic()) {
+        if (!vins->constantToBoolean()) {
             current->end(MGoto::New(alloc(), successor));
             current = nullptr;
 
             state.loop.successor = successor;
             return processBrokenLoop(state);
         }
     }
 
@@ -4563,17 +4562,17 @@ IonBuilder::makeInliningDecision(JSObjec
                 //
                 //  - Inner function as argument. Inlining a function called
                 //    with an inner function might help scalar replacement at
                 //    removing the scope chain, and thus using registers within
                 //    the loop instead of writting everything back to memory.
                 bool hasOpportunities = false;
                 for (size_t i = 0, e = callInfo.argv().length(); !hasOpportunities && i < e; i++) {
                     MDefinition *arg = callInfo.argv()[i];
-                    hasOpportunities = arg->isLambda() || arg->isConstant();
+                    hasOpportunities = arg->isLambda() || arg->isConstantValue();
                 }
 
                 if (!hasOpportunities)
                     return DontInline(targetScript, "Vetoed: big function that contains a loop");
             }
 
             // Caller must not be excessively large.
             if (script()->length() >= optimizationInfo().inliningMaxCallerBytecodeLength())
@@ -5925,20 +5924,20 @@ IonBuilder::jsop_eval(uint32_t argc)
 
         current->pushSlot(info().thisSlot());
         MDefinition *thisValue = current->pop();
 
         // Try to pattern match 'eval(v + "()")'. In this case v is likely a
         // name on the scope chain and the eval is performing a call on that
         // value. Use a dynamic scope chain lookup rather than a full eval.
         if (string->isConcat() &&
-            string->getOperand(1)->isConstant() &&
-            string->getOperand(1)->toConstant()->value().isString())
+            string->getOperand(1)->isConstantValue() &&
+            string->getOperand(1)->constantValue().isString())
         {
-            JSAtom *atom = &string->getOperand(1)->toConstant()->value().toString()->asAtom();
+            JSAtom *atom = &string->getOperand(1)->constantValue().toString()->asAtom();
 
             if (StringEqualsAscii(atom, "()")) {
                 MDefinition *name = string->getOperand(0);
                 MInstruction *dynamicName = MGetDynamicName::New(alloc(), scopeChain, name);
                 current->add(dynamicName);
 
                 current->push(dynamicName);
                 current->push(thisValue);
@@ -7841,20 +7840,20 @@ IonBuilder::getElemTryArgumentsInlined(b
         return true;
 
     // Emit inlined arguments.
     obj->setImplicitlyUsedUnchecked();
 
     MOZ_ASSERT(!info().argsObjAliasesFormals());
 
     // When the id is constant, we can just return the corresponding inlined argument
-    if (index->isConstant() && index->toConstant()->value().isInt32()) {
+    if (index->isConstantValue() && index->constantValue().isInt32()) {
         MOZ_ASSERT(inliningDepth_ > 0);
 
-        int32_t id = index->toConstant()->value().toInt32();
+        int32_t id = index->constantValue().toInt32();
         index->setImplicitlyUsedUnchecked();
 
         if (id < (int32_t)inlineCallInfo_->argc() && id >= 0)
             current->push(inlineCallInfo_->getArg(id));
         else
             pushConstant(UndefinedValue());
 
         *emitted = true;
@@ -8060,18 +8059,18 @@ void
 IonBuilder::addTypedArrayLengthAndData(MDefinition *obj,
                                        BoundsChecking checking,
                                        MDefinition **index,
                                        MInstruction **length, MInstruction **elements)
 {
     MOZ_ASSERT((index != nullptr) == (elements != nullptr));
     JSObject *tarr = nullptr;
 
-    if (obj->isConstant() && obj->toConstant()->value().isObject())
-        tarr = &obj->toConstant()->value().toObject();
+    if (obj->isConstantValue() && obj->constantValue().isObject())
+        tarr = &obj->constantValue().toObject();
     else if (obj->resultTypeSet())
         tarr = obj->resultTypeSet()->getSingleton();
 
     if (tarr) {
         void *data = AnyTypedArrayViewData(tarr);
         // Bug 979449 - Optimistically embed the elements and use TI to
         //              invalidate if we move them.
         bool isTenured = !tarr->runtimeFromMainThread()->gc.nursery.isInside(data);
@@ -8118,28 +8117,28 @@ IonBuilder::convertShiftToMaskForStaticT
                                                   Scalar::Type viewType)
 {
     // No shifting is necessary if the typed array has single byte elements.
     if (TypedArrayShift(viewType) == 0)
         return id;
 
     // If the index is an already shifted constant, undo the shift to get the
     // absolute offset being accessed.
-    if (id->isConstant() && id->toConstant()->value().isInt32()) {
-        int32_t index = id->toConstant()->value().toInt32();
+    if (id->isConstantValue() && id->constantValue().isInt32()) {
+        int32_t index = id->constantValue().toInt32();
         MConstant *offset = MConstant::New(alloc(), Int32Value(index << TypedArrayShift(viewType)));
         current->add(offset);
         return offset;
     }
 
     if (!id->isRsh() || id->isEffectful())
         return nullptr;
-    if (!id->getOperand(1)->isConstant())
+    if (!id->getOperand(1)->isConstantValue())
         return nullptr;
-    const Value &value = id->getOperand(1)->toConstant()->value();
+    const Value &value = id->getOperand(1)->constantValue();
     if (!value.isInt32() || uint32_t(value.toInt32()) != TypedArrayShift(viewType))
         return nullptr;
 
     // Instead of shifting, mask off the low bits of the index so that
     // a non-scaled access on the typed array can be performed.
     MConstant *mask = MConstant::New(alloc(), Int32Value(~((1 << value.toInt32()) - 1)));
     MBitAnd *ptr = MBitAnd::New(alloc(), id->getOperand(0), mask);
 
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -38,16 +38,22 @@ class LLabel : public LInstructionHelper
 };
 
 class LNop : public LInstructionHelper<0, 0, 0>
 {
   public:
     LIR_HEADER(Nop)
 };
 
+class LMop : public LInstructionHelper<0, 0, 0>
+{
+  public:
+    LIR_HEADER(Mop)
+};
+
 // An LOsiPoint captures a snapshot after a call and ensures enough space to
 // patch in a call to the invalidation mechanism.
 //
 // Note: LSafepoints are 1:1 with LOsiPoints, so it holds a reference to the
 // corresponding LSafepoint to inform it of the LOsiPoint's masm offset when it
 // gets CG'd.
 class LOsiPoint : public LInstructionHelper<0, 0, 0>
 {
--- a/js/src/jit/LOpcodes.h
+++ b/js/src/jit/LOpcodes.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jit_LOpcodes_h
 #define jit_LOpcodes_h
 
 #define LIR_COMMON_OPCODE_LIST(_)   \
     _(Label)                        \
     _(Nop)                          \
+    _(Mop)                          \
     _(OsiPoint)                     \
     _(MoveGroup)                    \
     _(Integer)                      \
     _(Pointer)                      \
     _(Double)                       \
     _(Float32)                      \
     _(SimdSplatX4)                  \
     _(Int32x4)                      \
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -618,17 +618,17 @@ LIRGenerator::visitCallDirectEval(MCallD
 }
 
 static JSOp
 ReorderComparison(JSOp op, MDefinition **lhsp, MDefinition **rhsp)
 {
     MDefinition *lhs = *lhsp;
     MDefinition *rhs = *rhsp;
 
-    if (lhs->isConstant()) {
+    if (lhs->isConstantValue()) {
         *rhsp = lhs;
         *lhsp = rhs;
         return ReverseCompareOp(op);
     }
     return op;
 }
 
 void
@@ -638,18 +638,18 @@ LIRGenerator::visitTest(MTest *test)
     MBasicBlock *ifTrue = test->ifTrue();
     MBasicBlock *ifFalse = test->ifFalse();
 
     // String is converted to length of string in the type analysis phase (see
     // TestPolicy).
     MOZ_ASSERT(opd->type() != MIRType_String);
 
     // Testing a constant.
-    if (opd->isConstant()) {
-        bool result = opd->toConstant()->valueToBoolean();
+    if (opd->isConstantValue() && !opd->constantValue().isMagic()) {
+        bool result = opd->constantToBoolean();
         add(new(alloc()) LGoto(result ? ifTrue : ifFalse));
         return;
     }
 
     if (opd->type() == MIRType_Value) {
         LDefinition temp0, temp1;
         if (test->operandMightEmulateUndefined()) {
             temp0 = temp();
@@ -1486,35 +1486,35 @@ LIRGenerator::visitMul(MMul *ins)
     MOZ_ASSERT(lhs->type() == rhs->type());
 
     if (ins->specialization() == MIRType_Int32) {
         MOZ_ASSERT(lhs->type() == MIRType_Int32);
         ReorderCommutative(&lhs, &rhs, ins);
 
         // If our RHS is a constant -1 and we don't have to worry about
         // overflow, we can optimize to an LNegI.
-        if (!ins->fallible() && rhs->isConstant() && rhs->toConstant()->value() == Int32Value(-1))
+        if (!ins->fallible() && rhs->isConstantValue() && rhs->constantValue() == Int32Value(-1))
             defineReuseInput(new(alloc()) LNegI(useRegisterAtStart(lhs)), ins, 0);
         else
             lowerMulI(ins, lhs, rhs);
     } else if (ins->specialization() == MIRType_Double) {
         MOZ_ASSERT(lhs->type() == MIRType_Double);
         ReorderCommutative(&lhs, &rhs, ins);
 
         // If our RHS is a constant -1.0, we can optimize to an LNegD.
-        if (rhs->isConstant() && rhs->toConstant()->value() == DoubleValue(-1.0))
+        if (rhs->isConstantValue() && rhs->constantValue() == DoubleValue(-1.0))
             defineReuseInput(new(alloc()) LNegD(useRegisterAtStart(lhs)), ins, 0);
         else
             lowerForFPU(new(alloc()) LMathD(JSOP_MUL), ins, lhs, rhs);
     } else if (ins->specialization() == MIRType_Float32) {
         MOZ_ASSERT(lhs->type() == MIRType_Float32);
         ReorderCommutative(&lhs, &rhs, ins);
 
         // We apply the same optimizations as for doubles
-        if (rhs->isConstant() && rhs->toConstant()->value() == Float32Value(-1.0f))
+        if (rhs->isConstantValue() && rhs->constantValue() == Float32Value(-1.0f))
             defineReuseInput(new(alloc()) LNegF(useRegisterAtStart(lhs)), ins, 0);
         else
             lowerForFPU(new(alloc()) LMathF(JSOP_MUL), ins, lhs, rhs);
     } else {
         lowerBinaryV(JSOP_MUL, ins);
     }
 }
 
@@ -4112,20 +4112,30 @@ LIRGenerator::visitInstruction(MInstruct
 
     if (ins->resumePoint())
         updateResumeState(ins);
 
 #ifdef DEBUG
     ins->setInWorklistUnchecked();
 #endif
 
+    // If we added a Nop for this instruction, we'll also add a Mop, so that
+    // that live-ranges for fixed register defs, which with LSRA extend through
+    // the Nop so that they can extend through the OsiPoint don't, with their
+    // one-extra extension, extend into a position where they use the input
+    // move group for the following instruction.
+    bool needsMop = !current->instructions().empty() && current->rbegin()->isNop();
+
     // If no safepoint was created, there's no need for an OSI point.
     if (LOsiPoint *osiPoint = popOsiPoint())
         add(osiPoint);
 
+    if (needsMop)
+        add(new(alloc()) LMop);
+
     return !gen->errored();
 }
 
 void
 LIRGenerator::definePhis()
 {
     size_t lirIndex = 0;
     MBasicBlock *block = current->mir();
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -371,30 +371,30 @@ IonBuilder::inlineArray(CallInfo &callIn
         templateObject->clearShouldConvertDoubleElements();
 
     // A single integer argument denotes initial length.
     if (callInfo.argc() == 1) {
         if (callInfo.getArg(0)->type() != MIRType_Int32)
             return InliningStatus_NotInlined;
 
         MDefinition *arg = callInfo.getArg(0);
-        if (!arg->isConstant()) {
+        if (!arg->isConstantValue()) {
             callInfo.setImplicitlyUsedUnchecked();
             ArrayObject *templateArray = &templateObject->as<ArrayObject>();
             MNewArrayDynamicLength *ins =
                 MNewArrayDynamicLength::New(alloc(), constraints(), templateArray,
                                             templateArray->type()->initialHeap(constraints()),
                                             arg);
             current->add(ins);
             current->push(ins);
             return InliningStatus_Inlined;
         }
 
         // Negative lengths generate a RangeError, unhandled by the inline path.
-        initLength = arg->toConstant()->value().toInt32();
+        initLength = arg->constantValue().toInt32();
         if (initLength >= NativeObject::NELEMENTS_LIMIT)
             return InliningStatus_NotInlined;
 
         // Make sure initLength matches the template object's length. This is
         // not guaranteed to be the case, for instance if we're inlining the
         // MConstant may come from an outer script.
         if (initLength != templateObject->as<ArrayObject>().length())
             return InliningStatus_NotInlined;
@@ -1031,20 +1031,20 @@ IonBuilder::inlineMathPow(CallInfo &call
 
     callInfo.setImplicitlyUsedUnchecked();
 
     MDefinition *base = callInfo.getArg(0);
     MDefinition *power = callInfo.getArg(1);
     MDefinition *output = nullptr;
 
     // Optimize some constant powers.
-    if (callInfo.getArg(1)->isConstant() &&
-        callInfo.getArg(1)->toConstant()->value().isNumber())
+    if (callInfo.getArg(1)->isConstantValue() &&
+        callInfo.getArg(1)->constantValue().isNumber())
     {
-        double pow = callInfo.getArg(1)->toConstant()->value().toNumber();
+        double pow = callInfo.getArg(1)->constantValue().toNumber();
 
         // Math.pow(x, 0.5) is a sqrt with edge-case detection.
         if (pow == 0.5) {
             MPowHalf *half = MPowHalf::New(alloc(), base);
             current->add(half);
             output = half;
         }
 
@@ -1209,18 +1209,18 @@ IonBuilder::inlineMathMinMax(CallInfo &c
           case MIRType_Int32:
             if (!int32_cases.append(arg))
                 return InliningStatus_Error;
             break;
           case MIRType_Double:
           case MIRType_Float32:
             // Don't force a double MMinMax for arguments that would be a NOP
             // when doing an integer MMinMax.
-            if (arg->isConstant()) {
-                double cte = arg->toConstant()->value().toDouble();
+            if (arg->isConstantValue()) {
+                double cte = arg->constantValue().toDouble();
                 // min(int32, cte >= INT32_MAX) = int32
                 if (cte >= INT32_MAX && !max)
                     break;
                 // max(int32, cte <= INT32_MIN) = int32
                 if (cte <= INT32_MIN && max)
                     break;
             }
 
@@ -1361,24 +1361,24 @@ IonBuilder::inlineStrCharCodeAt(CallInfo
     current->add(charCode);
     current->push(charCode);
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
 IonBuilder::inlineConstantCharCodeAt(CallInfo &callInfo)
 {
-    if (!callInfo.thisArg()->isConstant())
+    if (!callInfo.thisArg()->isConstantValue())
         return InliningStatus_NotInlined;
 
-    if (!callInfo.getArg(0)->isConstant())
+    if (!callInfo.getArg(0)->isConstantValue())
         return InliningStatus_NotInlined;
 
-    const js::Value *strval = callInfo.thisArg()->toConstant()->vp();
-    const js::Value *idxval  = callInfo.getArg(0)->toConstant()->vp();
+    const js::Value *strval = callInfo.thisArg()->constantVp();
+    const js::Value *idxval  = callInfo.getArg(0)->constantVp();
 
     if (!strval->isString() || !idxval->isInt32())
         return InliningStatus_NotInlined;
 
     JSString *str = strval->toString();
     if (!str->isLinear())
         return InliningStatus_NotInlined;
 
@@ -2026,19 +2026,19 @@ IonBuilder::inlineUnsafeSetReservedSlot(
         return InliningStatus_NotInlined;
     if (callInfo.getArg(0)->type() != MIRType_Object)
         return InliningStatus_NotInlined;
     if (callInfo.getArg(1)->type() != MIRType_Int32)
         return InliningStatus_NotInlined;
 
     // Don't inline if we don't have a constant slot.
     MDefinition *arg = callInfo.getArg(1);
-    if (!arg->isConstant())
+    if (!arg->isConstantValue())
         return InliningStatus_NotInlined;
-    uint32_t slot = arg->toConstant()->value().toPrivateUint32();
+    uint32_t slot = arg->constantValue().toPrivateUint32();
 
     callInfo.setImplicitlyUsedUnchecked();
 
     MStoreFixedSlot *store = MStoreFixedSlot::New(alloc(), callInfo.getArg(0), slot, callInfo.getArg(2));
     current->add(store);
     current->push(store);
 
     if (NeedsPostBarrier(info(), callInfo.getArg(2)))
@@ -2054,19 +2054,19 @@ IonBuilder::inlineUnsafeGetReservedSlot(
         return InliningStatus_NotInlined;
     if (callInfo.getArg(0)->type() != MIRType_Object)
         return InliningStatus_NotInlined;
     if (callInfo.getArg(1)->type() != MIRType_Int32)
         return InliningStatus_NotInlined;
 
     // Don't inline if we don't have a constant slot.
     MDefinition *arg = callInfo.getArg(1);
-    if (!arg->isConstant())
+    if (!arg->isConstantValue())
         return InliningStatus_NotInlined;
-    uint32_t slot = arg->toConstant()->value().toPrivateUint32();
+    uint32_t slot = arg->constantValue().toPrivateUint32();
 
     callInfo.setImplicitlyUsedUnchecked();
 
     MLoadFixedSlot *load = MLoadFixedSlot::New(alloc(), callInfo.getArg(0), slot);
     current->add(load);
     current->push(load);
 
     // We don't track reserved slot types, so always emit a barrier.
@@ -2222,19 +2222,19 @@ IonBuilder::inlineBailout(CallInfo &call
 IonBuilder::InliningStatus
 IonBuilder::inlineAssertFloat32(CallInfo &callInfo)
 {
     callInfo.setImplicitlyUsedUnchecked();
 
     MDefinition *secondArg = callInfo.getArg(1);
 
     MOZ_ASSERT(secondArg->type() == MIRType_Boolean);
-    MOZ_ASSERT(secondArg->isConstant());
-
-    bool mustBeFloat32 = secondArg->toConstant()->value().toBoolean();
+    MOZ_ASSERT(secondArg->isConstantValue());
+
+    bool mustBeFloat32 = secondArg->constantValue().toBoolean();
     current->add(MAssertFloat32::New(alloc(), callInfo.getArg(0), mustBeFloat32));
 
     MConstant *undefined = MConstant::New(alloc(), UndefinedValue());
     current->add(undefined);
     current->push(undefined);
     return InliningStatus_Inlined;
 }
 
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -66,27 +66,55 @@ MDefinition::PrintOpcodeName(FILE *fp, M
 #undef NAME
     };
     const char *name = names[op];
     size_t len = strlen(name);
     for (size_t i = 0; i < len; i++)
         fprintf(fp, "%c", tolower(name[i]));
 }
 
+const Value &
+MDefinition::constantValue()
+{
+    MOZ_ASSERT(isConstantValue());
+
+    if (isBox())
+        return getOperand(0)->constantValue();
+    return toConstant()->value();
+}
+
+const Value *
+MDefinition::constantVp()
+{
+    MOZ_ASSERT(isConstantValue());
+    if (isBox())
+        return getOperand(0)->constantVp();
+    return toConstant()->vp();
+}
+
+bool
+MDefinition::constantToBoolean()
+{
+    MOZ_ASSERT(isConstantValue());
+    if (isBox())
+        return getOperand(0)->constantToBoolean();
+    return toConstant()->valueToBoolean();
+}
+
 static MConstant *
 EvaluateConstantOperands(TempAllocator &alloc, MBinaryInstruction *ins, bool *ptypeChange = nullptr)
 {
     MDefinition *left = ins->getOperand(0);
     MDefinition *right = ins->getOperand(1);
 
-    if (!left->isConstant() || !right->isConstant())
+    if (!left->isConstantValue() || !right->isConstantValue())
         return nullptr;
 
-    Value lhs = left->toConstant()->value();
-    Value rhs = right->toConstant()->value();
+    Value lhs = left->constantValue();
+    Value rhs = right->constantValue();
     Value ret = UndefinedValue();
 
     switch (ins->op()) {
       case MDefinition::Op_BitAnd:
         ret = Int32Value(lhs.toInt32() & rhs.toInt32());
         break;
       case MDefinition::Op_BitOr:
         ret = Int32Value(lhs.toInt32() | rhs.toInt32());
@@ -141,20 +169,20 @@ EvaluateExactReciprocal(TempAllocator &a
 {
     // we should fold only when it is a floating point operation
     if (!IsFloatingPointType(ins->type()))
         return nullptr;
 
     MDefinition *left = ins->getOperand(0);
     MDefinition *right = ins->getOperand(1);
 
-    if (!right->isConstant())
+    if (!right->isConstantValue())
         return nullptr;
 
-    Value rhs = right->toConstant()->value();
+    Value rhs = right->constantValue();
 
     int32_t num;
     if (!mozilla::NumberIsInt32(rhs.toNumber(), &num))
         return nullptr;
 
     // check if rhs is a power of two
     if (mozilla::Abs(num) & (mozilla::Abs(num) - 1))
         return nullptr;
@@ -350,18 +378,18 @@ MTest::foldsTo(TempAllocator &alloc)
     if (op->isNot()) {
         // If the operand of the Not is itself a Not, they cancel out.
         MDefinition *opop = op->getOperand(0);
         if (opop->isNot())
             return MTest::New(alloc, opop->toNot()->input(), ifTrue(), ifFalse());
         return MTest::New(alloc, op->toNot()->input(), ifFalse(), ifTrue());
     }
 
-    if (op->isConstant())
-        return MGoto::New(alloc, op->toConstant()->valueToBoolean() ? ifTrue() : ifFalse());
+    if (op->isConstantValue() && !op->constantValue().isMagic())
+        return MGoto::New(alloc, op->constantToBoolean() ? ifTrue() : ifFalse());
 
     switch (op->type()) {
       case MIRType_Undefined:
       case MIRType_Null:
         return MGoto::New(alloc, ifFalse());
       case MIRType_Symbol:
         return MGoto::New(alloc, ifTrue());
       case MIRType_Object:
@@ -763,39 +791,39 @@ MSimdValueX4::foldsTo(TempAllocator &all
 {
     DebugOnly<MIRType> scalarType = SimdTypeToScalarType(type());
     bool allConstants = true;
     bool allSame = true;
 
     for (size_t i = 0; i < 4; ++i) {
         MDefinition *op = getOperand(i);
         MOZ_ASSERT(op->type() == scalarType);
-        if (!op->isConstant())
+        if (!op->isConstantValue())
             allConstants = false;
         if (i > 0 && op != getOperand(i - 1))
             allSame = false;
     }
 
     if (!allConstants && !allSame)
         return this;
 
     if (allConstants) {
         SimdConstant cst;
         switch (type()) {
           case MIRType_Int32x4: {
             int32_t a[4];
             for (size_t i = 0; i < 4; ++i)
-                a[i] = getOperand(i)->toConstant()->value().toInt32();
+                a[i] = getOperand(i)->constantValue().toInt32();
             cst = SimdConstant::CreateX4(a);
             break;
           }
           case MIRType_Float32x4: {
             float a[4];
             for (size_t i = 0; i < 4; ++i)
-                a[i] = getOperand(i)->toConstant()->value().toNumber();
+                a[i] = getOperand(i)->constantValue().toNumber();
             cst = SimdConstant::CreateX4(a);
             break;
           }
           default: MOZ_CRASH("unexpected type in MSimdValueX4::foldsTo");
         }
 
         return MSimdConstant::New(alloc, cst, type());
     }
@@ -804,33 +832,33 @@ MSimdValueX4::foldsTo(TempAllocator &all
     return MSimdSplatX4::New(alloc, type(), getOperand(0));
 }
 
 MDefinition*
 MSimdSplatX4::foldsTo(TempAllocator &alloc)
 {
     DebugOnly<MIRType> scalarType = SimdTypeToScalarType(type());
     MDefinition *op = getOperand(0);
-    if (!op->isConstant())
+    if (!op->isConstantValue())
         return this;
     MOZ_ASSERT(op->type() == scalarType);
 
     SimdConstant cst;
     switch (type()) {
       case MIRType_Int32x4: {
         int32_t a[4];
-        int32_t v = getOperand(0)->toConstant()->value().toInt32();
+        int32_t v = getOperand(0)->constantValue().toInt32();
         for (size_t i = 0; i < 4; ++i)
             a[i] = v;
         cst = SimdConstant::CreateX4(a);
         break;
       }
       case MIRType_Float32x4: {
         float a[4];
-        float v = getOperand(0)->toConstant()->value().toNumber();
+        float v = getOperand(0)->constantValue().toNumber();
         for (size_t i = 0; i < 4; ++i)
             a[i] = v;
         cst = SimdConstant::CreateX4(a);
         break;
       }
       default: MOZ_CRASH("unexpected type in MSimdSplatX4::foldsTo");
     }
 
@@ -1091,18 +1119,18 @@ MApplyArgs::New(TempAllocator &alloc, JS
                 MDefinition *self)
 {
     return new(alloc) MApplyArgs(target, fun, argc, self);
 }
 
 MDefinition*
 MStringLength::foldsTo(TempAllocator &alloc)
 {
-    if ((type() == MIRType_Int32) && (string()->isConstant())) {
-        Value value = string()->toConstant()->value();
+    if ((type() == MIRType_Int32) && (string()->isConstantValue())) {
+        Value value = string()->constantValue();
         JSAtom *atom = &value.toString()->asAtom();
         return MConstant::New(alloc, Int32Value(atom->length()));
     }
 
     return this;
 }
 
 static bool
@@ -1879,22 +1907,20 @@ MMinMax::trySpecializeFloat32(TempAlloca
 }
 
 MDefinition *
 MMinMax::foldsTo(TempAllocator &alloc)
 {
     if (!lhs()->isConstant() && !rhs()->isConstant())
         return this;
 
-    MDefinition *operand = lhs()->isConstant() ? rhs() : lhs();
-    MConstant *constant = lhs()->isConstant() ? lhs()->toConstant() : rhs()->toConstant();
+    MDefinition *operand = lhs()->isConstantValue() ? rhs() : lhs();
+    const js::Value &val = lhs()->isConstantValue() ? lhs()->constantValue() : rhs()->constantValue();
 
     if (operand->isToDouble() && operand->getOperand(0)->type() == MIRType_Int32) {
-        const js::Value &val = constant->value();
-
         // min(int32, cte >= INT32_MAX) = int32
         if (val.isDouble() && val.toDouble() >= INT32_MAX && !isMax()) {
             MLimitedTruncate *limit =
                 MLimitedTruncate::New(alloc, operand->getOperand(0), MDefinition::NoTruncate);
             block()->insertBefore(this, limit);
             MToDouble *toDouble = MToDouble::New(alloc, limit);
             block()->insertBefore(this, toDouble);
             return toDouble;
@@ -1954,35 +1980,35 @@ MDiv::foldsTo(TempAllocator &alloc)
 void
 MDiv::analyzeEdgeCasesForward()
 {
     // This is only meaningful when doing integer division.
     if (specialization_ != MIRType_Int32)
         return;
 
     // Try removing divide by zero check
-    if (rhs()->isConstant() && !rhs()->toConstant()->value().isInt32(0))
+    if (rhs()->isConstantValue() && !rhs()->constantValue().isInt32(0))
         canBeDivideByZero_ = false;
 
     // If lhs is a constant int != INT32_MIN, then
     // negative overflow check can be skipped.
-    if (lhs()->isConstant() && !lhs()->toConstant()->value().isInt32(INT32_MIN))
+    if (lhs()->isConstantValue() && !lhs()->constantValue().isInt32(INT32_MIN))
         canBeNegativeOverflow_ = false;
 
     // If rhs is a constant int != -1, likewise.
-    if (rhs()->isConstant() && !rhs()->toConstant()->value().isInt32(-1))
+    if (rhs()->isConstantValue() && !rhs()->constantValue().isInt32(-1))
         canBeNegativeOverflow_ = false;
 
     // If lhs is != 0, then negative zero check can be skipped.
-    if (lhs()->isConstant() && !lhs()->toConstant()->value().isInt32(0))
+    if (lhs()->isConstantValue() && !lhs()->constantValue().isInt32(0))
         setCanBeNegativeZero(false);
 
     // If rhs is >= 0, likewise.
-    if (rhs()->isConstant()) {
-        const js::Value &val = rhs()->toConstant()->value();
+    if (rhs()->isConstantValue()) {
+        const js::Value &val = rhs()->constantValue();
         if (val.isInt32() && val.toInt32() >= 0)
             setCanBeNegativeZero(false);
     }
 }
 
 void
 MDiv::analyzeEdgeCasesBackward()
 {
@@ -2010,21 +2036,21 @@ MMod::foldsTo(TempAllocator &alloc)
 
 void
 MMod::analyzeEdgeCasesForward()
 {
     // These optimizations make sense only for integer division
     if (specialization_ != MIRType_Int32)
         return;
 
-    if (rhs()->isConstant() && !rhs()->toConstant()->value().isInt32(0))
+    if (rhs()->isConstantValue() && !rhs()->constantValue().isInt32(0))
         canBeDivideByZero_ = false;
 
-    if (rhs()->isConstant()) {
-        int32_t n = rhs()->toConstant()->value().toInt32();
+    if (rhs()->isConstantValue()) {
+        int32_t n = rhs()->constantValue().toInt32();
         if (n > 0 && !IsPowerOfTwo(n))
             canBePowerOfTwoDivisor_ = false;
     }
 }
 
 bool
 MMod::fallible() const
 {
@@ -2088,25 +2114,25 @@ void
 MMul::analyzeEdgeCasesForward()
 {
     // Try to remove the check for negative zero
     // This only makes sense when using the integer multiplication
     if (specialization() != MIRType_Int32)
         return;
 
     // If lhs is > 0, no need for negative zero check.
-    if (lhs()->isConstant()) {
-        const js::Value &val = lhs()->toConstant()->value();
+    if (lhs()->isConstantValue()) {
+        const js::Value &val = lhs()->constantValue();
         if (val.isInt32() && val.toInt32() > 0)
             setCanBeNegativeZero(false);
     }
 
     // If rhs is > 0, likewise.
-    if (rhs()->isConstant()) {
-        const js::Value &val = rhs()->toConstant()->value();
+    if (rhs()->isConstantValue()) {
+        const js::Value &val = rhs()->constantValue();
         if (val.isInt32() && val.toInt32() > 0)
             setCanBeNegativeZero(false);
     }
 }
 
 void
 MMul::analyzeEdgeCasesBackward()
 {
@@ -2365,25 +2391,25 @@ MCompare::inputType()
 
 static inline bool
 MustBeUInt32(MDefinition *def, MDefinition **pwrapped)
 {
     if (def->isUrsh()) {
         *pwrapped = def->toUrsh()->getOperand(0);
         MDefinition *rhs = def->toUrsh()->getOperand(1);
         return !def->toUrsh()->bailoutsDisabled()
-            && rhs->isConstant()
-            && rhs->toConstant()->value().isInt32()
-            && rhs->toConstant()->value().toInt32() == 0;
+            && rhs->isConstantValue()
+            && rhs->constantValue().isInt32()
+            && rhs->constantValue().toInt32() == 0;
     }
 
-    if (def->isConstant()) {
+    if (def->isConstantValue()) {
         *pwrapped = def;
-        return def->toConstant()->value().isInt32()
-            && def->toConstant()->value().toInt32() >= 0;
+        return def->constantValue().isInt32()
+            && def->constantValue().toInt32() >= 0;
     }
 
     return false;
 }
 
 bool
 MBinaryInstruction::tryUseUnsignedOperands()
 {
@@ -2548,17 +2574,17 @@ MDefinition *
 MBitNot::foldsTo(TempAllocator &alloc)
 {
     if (specialization_ != MIRType_Int32)
         return this;
 
     MDefinition *input = getOperand(0);
 
     if (input->isConstant()) {
-        js::Value v = Int32Value(~(input->toConstant()->value().toInt32()));
+        js::Value v = Int32Value(~(input->constantValue().toInt32()));
         return MConstant::New(alloc, v);
     }
 
     if (input->isBitNot() && input->toBitNot()->specialization_ == MIRType_Int32) {
         MOZ_ASSERT(input->toBitNot()->getOperand(0)->type() == MIRType_Int32);
         return input->toBitNot()->getOperand(0); // ~~x => x
     }
 
@@ -2873,82 +2899,95 @@ MToInt32::analyzeEdgeCasesBackward()
     if (!NeedNegativeZeroCheck(this))
         setCanBeNegativeZero(false);
 }
 
 MDefinition *
 MTruncateToInt32::foldsTo(TempAllocator &alloc)
 {
     MDefinition *input = getOperand(0);
+    if (input->isBox())
+        input = input->getOperand(0);
+
     if (input->type() == MIRType_Int32)
         return input;
 
     if (input->type() == MIRType_Double && input->isConstant()) {
-        const Value &v = input->toConstant()->value();
+        const Value &v = input->constantValue();
         int32_t ret = ToInt32(v.toDouble());
         return MConstant::New(alloc, Int32Value(ret));
     }
 
     return this;
 }
 
 MDefinition *
 MToDouble::foldsTo(TempAllocator &alloc)
 {
-    MDefinition *in = input();
-    if (in->type() == MIRType_Double)
-        return in;
-
-    if (in->isConstant()) {
-        const Value &v = in->toConstant()->value();
+    MDefinition *input = getOperand(0);
+    if (input->isBox())
+        input = input->getOperand(0);
+
+    if (input->type() == MIRType_Double)
+        return input;
+
+    if (input->isConstant()) {
+        const Value &v = input->toConstant()->value();
         if (v.isNumber()) {
             double out = v.toNumber();
             return MConstant::New(alloc, DoubleValue(out));
         }
     }
 
     return this;
 }
 
 MDefinition *
 MToFloat32::foldsTo(TempAllocator &alloc)
 {
-    if (input()->type() == MIRType_Float32)
-        return input();
+    MDefinition *input = getOperand(0);
+    if (input->isBox())
+        input = input->getOperand(0);
+
+    if (input->type() == MIRType_Float32)
+        return input;
 
     // If x is a Float32, Float32(Double(x)) == x
-    if (input()->isToDouble() && input()->toToDouble()->input()->type() == MIRType_Float32)
-        return input()->toToDouble()->input();
-
-    if (input()->isConstant()) {
-        const Value &v = input()->toConstant()->value();
+    if (input->isToDouble() && input->toToDouble()->input()->type() == MIRType_Float32)
+        return input->toToDouble()->input();
+
+    if (input->isConstant()) {
+        const Value &v = input->toConstant()->value();
         if (v.isNumber()) {
             float out = v.toNumber();
             MConstant *c = MConstant::New(alloc, DoubleValue(out));
             c->setResultType(MIRType_Float32);
             return c;
         }
     }
     return this;
 }
 
 MDefinition *
 MToString::foldsTo(TempAllocator &alloc)
 {
     MDefinition *in = input();
+    if (in->isBox())
+        in = in->getOperand(0);
+
     if (in->type() == MIRType_String)
         return in;
     return this;
 }
 
 MDefinition *
 MClampToUint8::foldsTo(TempAllocator &alloc)
 {
-    if (input()->isConstant()) {
-        const Value &v = input()->toConstant()->value();
+    if (input()->isConstantValue()) {
+        const Value &v = input()->constantValue();
         if (v.isDouble()) {
             int32_t clamped = ClampDoubleToUint8(v.toDouble());
             return MConstant::New(alloc, Int32Value(clamped));
         }
         if (v.isInt32()) {
             int32_t clamped = ClampIntForUint8Array(v.toInt32());
             return MConstant::New(alloc, Int32Value(clamped));
         }
@@ -3353,18 +3392,18 @@ MNot::cacheOperandMightEmulateUndefined(
     if (!MaybeEmulatesUndefined(getOperand(0)))
         markOperandCantEmulateUndefined();
 }
 
 MDefinition *
 MNot::foldsTo(TempAllocator &alloc)
 {
     // Fold if the input is constant
-    if (input()->isConstant()) {
-        bool result = input()->toConstant()->valueToBoolean();
+    if (input()->isConstantValue() && !input()->constantValue().isMagic()) {
+        bool result = input()->constantToBoolean();
         if (type() == MIRType_Int32)
             return MConstant::New(alloc, Int32Value(!result));
 
         // ToBoolean can't cause side effects, so this is safe.
         return MConstant::New(alloc, BooleanValue(!result));
     }
 
     // If the operand of the Not is itself a Not, they cancel out. But we can't
@@ -3920,30 +3959,30 @@ MGetPropertyCache::updateForReplacement(
     MGetPropertyCache *other = ins->toGetPropertyCache();
     location_.append(&other->location_);
     return true;
 }
 
 MDefinition *
 MAsmJSUnsignedToDouble::foldsTo(TempAllocator &alloc)
 {
-    if (input()->isConstant()) {
-        const Value &v = input()->toConstant()->value();
+    if (input()->isConstantValue()) {
+        const Value &v = input()->constantValue();
         if (v.isInt32())
             return MConstant::New(alloc, DoubleValue(uint32_t(v.toInt32())));
     }
 
     return this;
 }
 
 MDefinition *
 MAsmJSUnsignedToFloat32::foldsTo(TempAllocator &alloc)
 {
-    if (input()->isConstant()) {
-        const Value &v = input()->toConstant()->value();
+    if (input()->isConstantValue()) {
+        const Value &v = input()->constantValue();
         if (v.isInt32()) {
             double dval = double(uint32_t(v.toInt32()));
             if (IsFloat32Representable(dval))
                 return MConstant::NewAsmJS(alloc, JS::Float32Value(float(dval)), MIRType_Float32);
         }
     }
 
     return this;
@@ -3982,32 +4021,32 @@ MSqrt::trySpecializeFloat32(TempAllocato
 
     setResultType(MIRType_Float32);
     setPolicyType(MIRType_Float32);
 }
 
 MDefinition *
 MClz::foldsTo(TempAllocator &alloc)
 {
-    if (num()->isConstant()) {
-        int32_t n = num()->toConstant()->value().toInt32();
+    if (num()->isConstantValue()) {
+        int32_t n = num()->constantValue().toInt32();
         if (n == 0)
             return MConstant::New(alloc, Int32Value(32));
         return MConstant::New(alloc, Int32Value(mozilla::CountLeadingZeroes32(n)));
     }
 
     return this;
 }
 
 MDefinition *
 MBoundsCheck::foldsTo(TempAllocator &alloc)
 {
-    if (index()->isConstant() && length()->isConstant()) {
-       uint32_t len = length()->toConstant()->value().toInt32();
-       uint32_t idx = index()->toConstant()->value().toInt32();
+    if (index()->isConstantValue() && length()->isConstantValue()) {
+       uint32_t len = length()->constantValue().toInt32();
+       uint32_t idx = index()->constantValue().toInt32();
        if (idx + uint32_t(minimum()) < len && idx + uint32_t(maximum()) < len)
            return index();
     }
 
     return this;
 }
 
 MDefinition *
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -747,16 +747,23 @@ class MDefinition : public MNode
         return this->to<M##opcode>();     \
     }                                     \
     const M##opcode *to##opcode() const { \
         return this->to<M##opcode>();     \
     }
     MIR_OPCODE_LIST(OPCODE_CASTS)
 #   undef OPCODE_CASTS
 
+    bool isConstantValue() {
+        return isConstant() || (isBox() && getOperand(0)->isConstant());
+    }
+    const Value &constantValue();
+    const Value *constantVp();
+    bool constantToBoolean();
+
     inline MInstruction *toInstruction();
     inline const MInstruction *toInstruction() const;
     bool isInstruction() const {
         return !isPhi();
     }
 
     virtual bool isControlInstruction() const {
         return false;
--- a/js/src/jit/RangeAnalysis.cpp
+++ b/js/src/jit/RangeAnalysis.cpp
@@ -150,16 +150,19 @@ RangeAnalysis::addBetaNodes()
         BranchDirection branch_dir;
         MTest *test = block->immediateDominatorBranch(&branch_dir);
 
         if (!test || !test->getOperand(0)->isCompare())
             continue;
 
         MCompare *compare = test->getOperand(0)->toCompare();
 
+        if (compare->compareType() == MCompare::Compare_Unknown)
+            continue;
+
         // TODO: support unsigned comparisons
         if (compare->compareType() == MCompare::Compare_UInt32)
             continue;
 
         MDefinition *left = compare->getOperand(0);
         MDefinition *right = compare->getOperand(1);
         double bound;
         double conservativeLower = NegativeInfinity<double>();
@@ -169,22 +172,22 @@ RangeAnalysis::addBetaNodes()
         JSOp jsop = compare->jsop();
 
         if (branch_dir == FALSE_BRANCH) {
             jsop = NegateCompareOp(jsop);
             conservativeLower = GenericNaN();
             conservativeUpper = GenericNaN();
         }
 
-        if (left->isConstant() && left->toConstant()->value().isNumber()) {
-            bound = left->toConstant()->value().toNumber();
+        if (left->isConstantValue() && left->constantValue().isNumber()) {
+            bound = left->constantValue().toNumber();
             val = right;
             jsop = ReverseCompareOp(jsop);
-        } else if (right->isConstant() && right->toConstant()->value().isNumber()) {
-            bound = right->toConstant()->value().toNumber();
+        } else if (right->isConstantValue() && right->constantValue().isNumber()) {
+            bound = right->constantValue().toNumber();
             val = left;
         } else if (left->type() == MIRType_Int32 && right->type() == MIRType_Int32) {
             MDefinition *smaller = nullptr;
             MDefinition *greater = nullptr;
             if (jsop == JSOP_LT) {
                 smaller = left;
                 greater = right;
             } else if (jsop == JSOP_GT) {
@@ -1305,42 +1308,42 @@ MBitNot::computeRange(TempAllocator &all
 void
 MLsh::computeRange(TempAllocator &alloc)
 {
     Range left(getOperand(0));
     Range right(getOperand(1));
     left.wrapAroundToInt32();
 
     MDefinition *rhs = getOperand(1);
-    if (!rhs->isConstant()) {
-        right.wrapAroundToShiftCount();
-        setRange(Range::lsh(alloc, &left, &right));
+    if (rhs->isConstantValue() && rhs->constantValue().isInt32()) {
+        int32_t c = rhs->constantValue().toInt32();
+        setRange(Range::lsh(alloc, &left, c));
         return;
     }
 
-    int32_t c = rhs->toConstant()->value().toInt32();
-    setRange(Range::lsh(alloc, &left, c));
+    right.wrapAroundToShiftCount();
+    setRange(Range::lsh(alloc, &left, &right));
 }
 
 void
 MRsh::computeRange(TempAllocator &alloc)
 {
     Range left(getOperand(0));
     Range right(getOperand(1));
     left.wrapAroundToInt32();
 
     MDefinition *rhs = getOperand(1);
-    if (!rhs->isConstant()) {
-        right.wrapAroundToShiftCount();
-        setRange(Range::rsh(alloc, &left, &right));
+    if (rhs->isConstantValue() && rhs->constantValue().isInt32()) {
+        int32_t c = rhs->constantValue().toInt32();
+        setRange(Range::rsh(alloc, &left, c));
         return;
     }
 
-    int32_t c = rhs->toConstant()->value().toInt32();
-    setRange(Range::rsh(alloc, &left, c));
+    right.wrapAroundToShiftCount();
+    setRange(Range::rsh(alloc, &left, &right));
 }
 
 void
 MUrsh::computeRange(TempAllocator &alloc)
 {
     Range left(getOperand(0));
     Range right(getOperand(1));
 
@@ -1348,21 +1351,21 @@ MUrsh::computeRange(TempAllocator &alloc
     // can be thought of as converting its left operand to int32, and then
     // reinterpreting the int32 bits as a uint32 value. Both approaches yield
     // the same result. Since we lack support for full uint32 ranges, we use
     // the second interpretation, though it does cause us to be conservative.
     left.wrapAroundToInt32();
     right.wrapAroundToShiftCount();
 
     MDefinition *rhs = getOperand(1);
-    if (!rhs->isConstant()) {
-        setRange(Range::ursh(alloc, &left, &right));
+    if (rhs->isConstantValue() && rhs->constantValue().isInt32()) {
+        int32_t c = rhs->constantValue().toInt32();
+        setRange(Range::ursh(alloc, &left, c));
     } else {
-        int32_t c = rhs->toConstant()->value().toInt32();
-        setRange(Range::ursh(alloc, &left, c));
+        setRange(Range::ursh(alloc, &left, &right));
     }
 
     MOZ_ASSERT(range()->lower() >= 0);
 }
 
 void
 MAbs::computeRange(TempAllocator &alloc)
 {
@@ -2729,17 +2732,17 @@ CloneForDeadBranches(TempAllocator &allo
     clone->setRange(nullptr);
 
     // Set UseRemoved flag on the cloned instruction in order to chain recover
     // instruction for the bailout path.
     clone->setUseRemovedUnchecked();
 
     candidate->block()->insertBefore(candidate, clone);
 
-    if (!candidate->isConstant()) {
+    if (!candidate->isConstantValue()) {
         MOZ_ASSERT(clone->canRecoverOnBailout());
         clone->setRecoveredOnBailout();
     }
 
     // Replace the candidate by its recovered on bailout clone within recovered
     // instructions and resume points operands.
     for (MUseIterator i(candidate->usesBegin()); i != candidate->usesEnd(); ) {
         MUse *use = *i++;
--- a/js/src/jit/ScalarReplacement.cpp
+++ b/js/src/jit/ScalarReplacement.cpp
@@ -528,20 +528,20 @@ static bool
 IndexOf(MDefinition *ins, int32_t *res)
 {
     MOZ_ASSERT(ins->isLoadElement() || ins->isStoreElement());
     MDefinition *indexDef = ins->getOperand(1); // ins->index();
     if (indexDef->isBoundsCheck())
         indexDef = indexDef->toBoundsCheck()->index();
     if (indexDef->isToInt32())
         indexDef = indexDef->toToInt32()->getOperand(0);
-    if (!indexDef->isConstant())
+    if (!indexDef->isConstantValue())
         return false;
 
-    Value index = indexDef->toConstant()->value();
+    Value index = indexDef->constantValue();
     if (!index.isInt32())
         return false;
     *res = index.toInt32();
     return true;
 }
 
 // Returns False if the array is not escaped and if it is optimizable by
 // ScalarReplacementOfArray.
@@ -961,17 +961,17 @@ ArrayMemoryView::visitSetInitializedLeng
     if (!isArrayStateElements(elements))
         return;
 
     // Replace by the new initialized length.  Note that the argument of
     // MSetInitalizedLength is the last index and not the initialized length.
     // To obtain the length, we need to add 1 to it, and thus we need to create
     // a new constant that we register in the ArrayState.
     state_ = BlockState::Copy(alloc_, state_);
-    int32_t initLengthValue = ins->index()->toConstant()->value().toInt32() + 1;
+    int32_t initLengthValue = ins->index()->constantValue().toInt32() + 1;
     MConstant *initLength = MConstant::New(alloc_, Int32Value(initLengthValue));
     ins->block()->insertBefore(ins, initLength);
     ins->block()->insertBefore(ins, state_);
     state_->setInitializedLength(initLength);
 
     // Remove original instruction.
     discardInstruction(ins, elements);
 }
--- a/js/src/jit/arm/Lowering-arm.cpp
+++ b/js/src/jit/arm/Lowering-arm.cpp
@@ -487,37 +487,37 @@ LIRGeneratorARM::visitAsmJSUnsignedToFlo
 void
 LIRGeneratorARM::visitAsmJSLoadHeap(MAsmJSLoadHeap *ins)
 {
     MDefinition *ptr = ins->ptr();
     MOZ_ASSERT(ptr->type() == MIRType_Int32);
     LAllocation ptrAlloc;
 
     // For the ARM it is best to keep the 'ptr' in a register if a bounds check is needed.
-    if (ptr->isConstant() && !ins->needsBoundsCheck()) {
+    if (ptr->isConstantValue() && !ins->needsBoundsCheck()) {
         // A bounds check is only skipped for a positive index.
-        MOZ_ASSERT(ptr->toConstant()->value().toInt32() >= 0);
-        ptrAlloc = LAllocation(ptr->toConstant()->vp());
+        MOZ_ASSERT(ptr->constantValue().toInt32() >= 0);
+        ptrAlloc = LAllocation(ptr->constantVp());
     } else {
         ptrAlloc = useRegisterAtStart(ptr);
     }
 
     define(new(alloc()) LAsmJSLoadHeap(ptrAlloc), ins);
 }
 
 void
 LIRGeneratorARM::visitAsmJSStoreHeap(MAsmJSStoreHeap *ins)
 {
     MDefinition *ptr = ins->ptr();
     MOZ_ASSERT(ptr->type() == MIRType_Int32);
     LAllocation ptrAlloc;
 
-    if (ptr->isConstant() && !ins->needsBoundsCheck()) {
-        MOZ_ASSERT(ptr->toConstant()->value().toInt32() >= 0);
-        ptrAlloc = LAllocation(ptr->toConstant()->vp());
+    if (ptr->isConstantValue() && !ins->needsBoundsCheck()) {
+        MOZ_ASSERT(ptr->constantValue().toInt32() >= 0);
+        ptrAlloc = LAllocation(ptr->constantVp());
     } else {
         ptrAlloc = useRegisterAtStart(ptr);
     }
 
     add(new(alloc()) LAsmJSStoreHeap(ptrAlloc, useRegisterAtStart(ins->value())), ins);
 }
 
 void
--- a/js/src/jit/mips/Lowering-mips.cpp
+++ b/js/src/jit/mips/Lowering-mips.cpp
@@ -465,37 +465,37 @@ void
 LIRGeneratorMIPS::visitAsmJSLoadHeap(MAsmJSLoadHeap *ins)
 {
     MDefinition *ptr = ins->ptr();
     MOZ_ASSERT(ptr->type() == MIRType_Int32);
     LAllocation ptrAlloc;
 
     // For MIPS it is best to keep the 'ptr' in a register if a bounds check
     // is needed.
-    if (ptr->isConstant() && !ins->needsBoundsCheck()) {
-        int32_t ptrValue = ptr->toConstant()->value().toInt32();
+    if (ptr->isConstantValue() && !ins->needsBoundsCheck()) {
+        int32_t ptrValue = ptr->constantValue().toInt32();
         // A bounds check is only skipped for a positive index.
         MOZ_ASSERT(ptrValue >= 0);
-        ptrAlloc = LAllocation(ptr->toConstant()->vp());
+        ptrAlloc = LAllocation(ptr->constantVp());
     } else
         ptrAlloc = useRegisterAtStart(ptr);
 
     define(new(alloc()) LAsmJSLoadHeap(ptrAlloc), ins);
 }
 
 void
 LIRGeneratorMIPS::visitAsmJSStoreHeap(MAsmJSStoreHeap *ins)
 {
     MDefinition *ptr = ins->ptr();
     MOZ_ASSERT(ptr->type() == MIRType_Int32);
     LAllocation ptrAlloc;
 
-    if (ptr->isConstant() && !ins->needsBoundsCheck()) {
-        MOZ_ASSERT(ptr->toConstant()->value().toInt32() >= 0);
-        ptrAlloc = LAllocation(ptr->toConstant()->vp());
+    if (ptr->isConstantValue() && !ins->needsBoundsCheck()) {
+        MOZ_ASSERT(ptr->constantValue().toInt32() >= 0);
+        ptrAlloc = LAllocation(ptr->constantVp());
     } else
         ptrAlloc = useRegisterAtStart(ptr);
 
     add(new(alloc()) LAsmJSStoreHeap(ptrAlloc, useRegisterAtStart(ins->value())), ins);
 }
 
 void
 LIRGeneratorMIPS::visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins)
--- a/js/src/jsapi-tests/testJitRangeAnalysis.cpp
+++ b/js/src/jsapi-tests/testJitRangeAnalysis.cpp
@@ -122,16 +122,17 @@ BEGIN_TEST(testJitRangeAnalysis_MathSign
     // if (p < 0)
     MParameter *p = func.createParameter();
     entry->add(p);
     MConstant *c0 = MConstant::New(func.alloc, DoubleValue(0.0));
     entry->add(c0);
     MConstant *cm0 = MConstant::New(func.alloc, DoubleValue(-0.0));
     entry->add(cm0);
     MCompare *cmp = MCompare::New(func.alloc, p, c0, JSOP_LT);
+    cmp->setCompareType(MCompare::Compare_Double);
     entry->add(cmp);
     entry->end(MTest::New(func.alloc, cmp, thenBlock, elseBlock));
 
     // {
     //   return Math.sign(p + -0);
     // }
     MAdd *thenAdd = MAdd::NewAsmJS(func.alloc, p, cm0, MIRType_Double);
     thenBlock->add(thenAdd);
@@ -139,16 +140,17 @@ BEGIN_TEST(testJitRangeAnalysis_MathSign
     thenBlock->add(thenSign);
     MReturn *thenRet = MReturn::New(func.alloc, thenSign);
     thenBlock->end(thenRet);
 
     // else
     // {
     //   if (p >= 0)
     MCompare *elseCmp = MCompare::New(func.alloc, p, c0, JSOP_GE);
+    elseCmp->setCompareType(MCompare::Compare_Double);
     elseBlock->add(elseCmp);
     elseBlock->end(MTest::New(func.alloc, elseCmp, elseThenBlock, elseElseBlock));
 
     //   {
     //     return Math.sign(p + -0);
     //   }
     MAdd *elseThenAdd = MAdd::NewAsmJS(func.alloc, p, cm0, MIRType_Double);
     elseThenBlock->add(elseThenAdd);
deleted file mode 100644
--- a/js/src/tests/ecma_7/SIMD/coercions.js
+++ /dev/null
@@ -1,66 +0,0 @@
-// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
-var BUGNUMBER = 1061229;
-var float32x4 = SIMD.float32x4;
-var int32x4 = SIMD.int32x4;
-var {StructType, int32} = TypedObject;
-var summary = 'constructors used as coercions';
-
-/*
- * Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/licenses/publicdomain/
- */
-
-function assertCaught(f) {
-    var caught = false;
-    var args = Array.slice(arguments, 1);
-    try {
-        f.apply(null, args);
-    } catch (e) {
-        caught = true;
-        print(e)
-    }
-    assertEq(caught, true);
-}
-
-function test() {
-    var x = int32x4(1, 2, 3, 4);
-    var y = int32x4(x);
-
-    assertEq(x, y);
-
-    assertEq(y.x, x.x);
-    assertEq(y.x, 1);
-    assertEq(y.y, x.y);
-    assertEq(y.y, 2);
-    assertEq(y.z, x.z);
-    assertEq(y.z, 3);
-    assertEq(y.w, x.w);
-    assertEq(y.w, 4);
-
-    assertCaught(int32x4, 3);
-    assertCaught(int32x4, float32x4(1, 2, 3, 4));
-    assertCaught(int32x4, 'pony x 4');
-
-    var x = float32x4(NaN, 13.37, -Infinity, 4);
-    var y = float32x4(x);
-
-    assertEq(x, y);
-
-    assertEq(y.x, x.x);
-    assertEq(y.x, Math.fround(NaN));
-    assertEq(y.y, x.y);
-    assertEq(y.y, Math.fround(13.37));
-    assertEq(y.z, x.z);
-    assertEq(y.z, Math.fround(-Infinity));
-    assertEq(y.w, x.w);
-    assertEq(y.w, Math.fround(4));
-
-    assertCaught(float32x4, 3);
-    assertCaught(float32x4, int32x4(1, 2, 3, 4));
-    assertCaught(float32x4, 'pony x 4');
-
-    if (typeof reportCompare === "function")
-        reportCompare(true, true);
-}
-
-test();
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/SIMD/constructors.js
@@ -0,0 +1,66 @@
+// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
+
+var float32x4 = SIMD.float32x4;
+var int32x4 = SIMD.int32x4;
+
+function test() {
+
+    // Constructors.
+    assertEqX4(int32x4(1, 2, 3, 4),         [1,2,3,4]);
+    assertEqX4(int32x4(1, 2, 3),            [1,2,3,0]);
+    assertEqX4(int32x4(1, 2),               [1,2,0,0]);
+    // The 1-argument form is reserved for coercions.
+    assertEqX4(int32x4(),                   [0,0,0,0]);
+    assertEqX4(int32x4(1, 2, 3, 4, 5),      [1,2,3,4]);
+    assertEqX4(int32x4(1, 2, 3, 4, 5, 6),   [1,2,3,4]);
+
+    assertEqX4(float32x4(1, 2, 3, 4),       [1,2,3,4]);
+    assertEqX4(float32x4(1, 2, 3),          [1,2,3,NaN]);
+    assertEqX4(float32x4(1, 2),             [1,2,NaN,NaN]);
+    // The 1-argument form is reserved for coercions.
+    assertEqX4(float32x4(),                 [NaN,NaN,NaN,NaN]);
+    assertEqX4(float32x4(1, 2, 3, 4, 5),    [1,2,3,4]);
+    assertEqX4(float32x4(1, 2, 3, 4, 5, 6), [1,2,3,4]);
+
+    // Constructors used as coercion.
+    var x = int32x4(1, 2, 3, 4);
+    var y = int32x4(x);
+
+    assertEq(x, y);
+
+    assertEq(y.x, x.x);
+    assertEq(y.x, 1);
+    assertEq(y.y, x.y);
+    assertEq(y.y, 2);
+    assertEq(y.z, x.z);
+    assertEq(y.z, 3);
+    assertEq(y.w, x.w);
+    assertEq(y.w, 4);
+
+    assertThrowsInstanceOf(() => int32x4(3), TypeError);
+    assertThrowsInstanceOf(() => int32x4(float32x4(1,2,3,4)), TypeError);
+    assertThrowsInstanceOf(() => int32x4('pony x 4'), TypeError);
+
+    var x = float32x4(NaN, 13.37, -Infinity, 4);
+    var y = float32x4(x);
+
+    assertEq(x, y);
+
+    assertEq(y.x, x.x);
+    assertEq(y.x, Math.fround(NaN));
+    assertEq(y.y, x.y);
+    assertEq(y.y, Math.fround(13.37));
+    assertEq(y.z, x.z);
+    assertEq(y.z, Math.fround(-Infinity));
+    assertEq(y.w, x.w);
+    assertEq(y.w, Math.fround(4));
+
+    assertThrowsInstanceOf(() => float32x4(3), TypeError);
+    assertThrowsInstanceOf(() => float32x4(int32x4(1,2,3,4)), TypeError);
+    assertThrowsInstanceOf(() => float32x4('pony x 4'), TypeError);
+
+    if (typeof reportCompare === "function")
+        reportCompare(true, true);
+}
+
+test();
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/SIMD/signmask.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
+
+var float32x4 = SIMD.float32x4;
+var int32x4 = SIMD.int32x4;
+
+function test_float32x4() {
+  var v, w;
+  for ([v, w] of [[float32x4(-1, 20, 30, 4), 0b0001],
+                  [float32x4(9.999, 2.1234, 30.4443, -4), 0b1000],
+                  [float32x4(0, -Infinity, +Infinity, -0), 0b1010]])
+  {
+      assertEq(v.signMask, w);
+  }
+
+  if (typeof reportCompare === "function")
+    reportCompare(true, true);
+}
+
+function test_int32x4() {
+  var v, w;
+  for ([v, w] of [[int32x4(-1, 20, 30, 4), 0b0001],
+                  [int32x4(10, 2, 30.2, -4), 0b1000],
+                  [int32x4(0, 0x80000000, 0x7fffffff, -0), 0b0010]])
+  {
+      assertEq(v.signMask, w);
+  }
+
+  if (typeof reportCompare === "function")
+    reportCompare(true, true);
+}
+
+test_float32x4();
+test_int32x4();
new file mode 100644
--- /dev/null
+++ b/layout/generic/RubyUtils.cpp
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "RubyUtils.h"
+#include "nsIFrame.h"
+
+using namespace mozilla;
+
+NS_DECLARE_FRAME_PROPERTY(ReservedISize, nullptr);
+
+union NSCoordValue
+{
+  nscoord mCoord;
+  void* mPointer;
+  static_assert(sizeof(nscoord) <= sizeof(void*),
+                "Cannot store nscoord in pointer");
+};
+
+/* static */ void
+RubyUtils::SetReservedISize(nsIFrame* aFrame, nscoord aISize)
+{
+  MOZ_ASSERT(IsExpandableRubyBox(aFrame));
+  NSCoordValue value = { aISize };
+  aFrame->Properties().Set(ReservedISize(), value.mPointer);
+}
+
+/* static */ void
+RubyUtils::ClearReservedISize(nsIFrame* aFrame)
+{
+  MOZ_ASSERT(IsExpandableRubyBox(aFrame));
+  aFrame->Properties().Remove(ReservedISize());
+}
+
+/* static */ nscoord
+RubyUtils::GetReservedISize(nsIFrame* aFrame)
+{
+  MOZ_ASSERT(IsExpandableRubyBox(aFrame));
+  NSCoordValue value;
+  value.mPointer = aFrame->Properties().Get(ReservedISize());
+  return value.mCoord;
+}
new file mode 100644
--- /dev/null
+++ b/layout/generic/RubyUtils.h
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_RubyUtils_h_
+#define mozilla_RubyUtils_h_
+
+#include "nsGkAtoms.h"
+#include "nsIFrame.h"
+
+namespace mozilla {
+
+/**
+ * Reserved ISize
+ *
+ * With some exceptions, each ruby internal box has two isizes, which
+ * are the reflowed isize and the final isize. The reflowed isize is
+ * what a box itself needs. It is determined when the box gets reflowed.
+ *
+ * The final isize is what a box should be as the final result. For a
+ * ruby base/text box, the final isize is the size of its ruby column.
+ * For a ruby base/text container, the final isize is the size of its
+ * ruby segment. The final isize is never smaller than the reflowed
+ * isize. It is initially determined when a ruby column/segment gets
+ * fully reflowed, and may be advanced when a box is expanded, e.g.
+ * for justification.
+ *
+ * The difference between the reflowed isize and the final isize is
+ * reserved in the line layout after reflowing a box, hence it is called
+ * "Reserved ISize" here. It is used to expand the ruby boxes from their
+ * reflowed isize to the final isize during alignment of the line.
+ *
+ * There are three exceptions for the final isize:
+ * 1. A ruby text container has a larger final isize only if it is for
+ *    a span or collapsed annotations.
+ * 2. A ruby base container has a larger final isize only if at least
+ *    one of its ruby text containers does.
+ * 3. If a ruby text container has a larger final isize, its children
+ *    must not have.
+ */
+
+class RubyUtils
+{
+public:
+  static inline bool IsExpandableRubyBox(nsIFrame* aFrame)
+  {
+    nsIAtom* type = aFrame->GetType();
+    return type == nsGkAtoms::rubyBaseFrame ||
+           type == nsGkAtoms::rubyTextFrame ||
+           type == nsGkAtoms::rubyBaseContainerFrame ||
+           type == nsGkAtoms::rubyTextContainerFrame;
+  }
+
+  static void SetReservedISize(nsIFrame* aFrame, nscoord aISize);
+  static void ClearReservedISize(nsIFrame* aFrame);
+  static nscoord GetReservedISize(nsIFrame* aFrame);
+};
+
+}
+
+#endif /* !defined(mozilla_RubyUtils_h_) */
--- a/layout/generic/moz.build
+++ b/layout/generic/moz.build
@@ -89,16 +89,17 @@ UNIFIED_SOURCES += [
     'nsSimplePageSequenceFrame.cpp',
     'nsSplittableFrame.cpp',
     'nsSubDocumentFrame.cpp',
     'nsTextFrame.cpp',
     'nsTextFrameUtils.cpp',
     'nsTextRunTransformations.cpp',
     'nsVideoFrame.cpp',
     'nsViewportFrame.cpp',
+    'RubyUtils.cpp',
     'ScrollbarActivity.cpp',
     'StickyScrollContainer.cpp',
     'TextOverflow.cpp',
 ]
 
 # nsLineLayout.cpp needs to be built separately because it uses plarena.h.
 # nsPluginFrame.cpp needs to be built separately because of name clashes in the OS X headers.
 SOURCES += [
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -757,18 +757,22 @@ public:
   /**
    * Return frame's position without relative positioning
    */
   nsPoint GetNormalPosition() const;
   mozilla::LogicalPoint
   GetLogicalNormalPosition(mozilla::WritingMode aWritingMode,
                            nscoord aContainerWidth) const
   {
+    // Subtract the width of this frame from the container width to get
+    // the correct position in rtl frames where the origin is on the
+    // right instead of the left
     return mozilla::LogicalPoint(aWritingMode,
-                                 GetNormalPosition(), aContainerWidth);
+                                 GetNormalPosition(),
+                                 aContainerWidth - mRect.width);
   }
 
   virtual nsPoint GetPositionOfChildIgnoringScrolling(nsIFrame* aChild)
   { return aChild->GetPosition(); }
   
   nsPoint GetPositionIgnoringScrolling();
 
   static void DestroyRegion(void* aPropertyValue);
--- a/layout/generic/nsLineLayout.cpp
+++ b/layout/generic/nsLineLayout.cpp
@@ -19,16 +19,17 @@
 #include "nsStyleContext.h"
 #include "nsPresContext.h"
 #include "nsGkAtoms.h"
 #include "nsIContent.h"
 #include "nsLayoutUtils.h"
 #include "nsTextFrame.h"
 #include "nsStyleStructInlines.h"
 #include "nsBidiPresUtils.h"
+#include "RubyUtils.h"
 #include <algorithm>
 
 #ifdef DEBUG
 #undef  NOISY_INLINEDIR_ALIGN
 #undef  NOISY_BLOCKDIR_ALIGN
 #undef  REALLY_NOISY_BLOCKDIR_ALIGN
 #undef  NOISY_REFLOW
 #undef  REALLY_NOISY_REFLOW
@@ -72,17 +73,18 @@ nsLineLayout::nsLineLayout(nsPresContext
     mLineIsEmpty(false),
     mLineEndsInBR(false),
     mNeedBackup(false),
     mInFirstLine(false),
     mGotLineBox(false),
     mInFirstLetter(false),
     mHasBullet(false),
     mDirtyNextLine(false),
-    mLineAtStart(false)
+    mLineAtStart(false),
+    mHasRuby(false)
 {
   MOZ_ASSERT(aOuterReflowState, "aOuterReflowState must not be null");
   NS_ASSERTION(aFloatManager || aOuterReflowState->frame->GetType() ==
                                   nsGkAtoms::letterFrame,
                "float manager should be present");
   MOZ_COUNT_CTOR(nsLineLayout);
 
   // Stash away some style data that we need
@@ -91,16 +93,19 @@ nsLineLayout::nsLineLayout(nsPresContext
     mStyleText = blockFrame->StyleTextForLineLayout();
   else
     mStyleText = aOuterReflowState->frame->StyleText();
 
   mLineNumber = 0;
   mTotalPlacedFrames = 0;
   mBStartEdge = 0;
   mTrimmableISize = 0;
+#ifdef DEBUG
+  mFinalLineBSize = nscoord_MIN;
+#endif
 
   mInflationMinFontSize =
     nsLayoutUtils::InflationMinFontSizeFor(aOuterReflowState->frame);
 
   // Instead of always pre-initializing the free-lists for frames and
   // spans, we do it on demand so that situations that only use a few
   // frames and spans won't waste a lot of time in unneeded
   // initialization.
@@ -188,21 +193,16 @@ nsLineLayout::BeginLineReflow(nscoord aI
   PerSpanData* psd = NewPerSpanData();
   mCurrentSpan = mRootSpan = psd;
   psd->mReflowState = mBlockReflowState;
   psd->mIStart = aICoord;
   psd->mICoord = aICoord;
   psd->mIEnd = aICoord + aISize;
   mContainerWidth = aContainerWidth;
 
-  PerFrameData* pfd = NewPerFrameData(mBlockReflowState->frame);
-  pfd->mAscent = 0;
-  pfd->mSpan = psd;
-  psd->mFrame = pfd;
-
   // If we're in a constrained height frame, then we don't allow a
   // max line box width to take effect.
   if (!(LineContainerFrame()->GetStateBits() &
         NS_FRAME_IN_CONSTRAINED_HEIGHT)) {
 
     // If the available size is greater than the maximum line box width (if
     // specified), then we need to adjust the line box width to be at the max
     // possible width.
@@ -236,16 +236,21 @@ nsLineLayout::BeginLineReflow(nscoord aI
       }
     }
     nscoord indent = nsRuleNode::ComputeCoordPercentCalc(textIndent, pctBasis);
 
     mTextIndent = indent;
 
     psd->mICoord += indent;
   }
+
+  PerFrameData* pfd = NewPerFrameData(mBlockReflowState->frame);
+  pfd->mAscent = 0;
+  pfd->mSpan = psd;
+  psd->mFrame = pfd;
 }
 
 void
 nsLineLayout::EndLineReflow()
 {
 #ifdef NOISY_REFLOW
   nsFrame::ListTag(stdout, mBlockReflowState->frame);
   printf(": EndLineReflow: width=%d\n", mRootSpan->mICoord - mRootSpan->mIStart);
@@ -461,22 +466,22 @@ nsLineLayout::EndSpan(nsIFrame* aFrame)
 void
 nsLineLayout::AttachFrameToBaseLineLayout(PerFrameData* aFrame)
 {
   NS_PRECONDITION(this != mBaseLineLayout,
                   "This method must not be called in a base line layout.");
 
   PerFrameData* baseFrame = mBaseLineLayout->LastFrame();
   MOZ_ASSERT(aFrame && baseFrame);
-  MOZ_ASSERT(!aFrame->GetFlag(PFD_ISLINKEDTOBASE),
+  MOZ_ASSERT(!aFrame->mIsLinkedToBase,
              "The frame must not have been linked with the base");
 
   aFrame->mNextAnnotation = baseFrame->mNextAnnotation;
   baseFrame->mNextAnnotation = aFrame;
-  aFrame->SetFlag(PFD_ISLINKEDTOBASE, true);
+  aFrame->mIsLinkedToBase = true;
 }
 
 int32_t
 nsLineLayout::GetCurrentSpanCount() const
 {
   NS_ASSERTION(mCurrentSpan == mRootSpan, "bad linelayout user");
   int32_t count = 0;
   PerFrameData* pfd = mRootSpan->mFirstFrame;
@@ -554,17 +559,17 @@ nsLineLayout::PushFrame(nsIFrame* aFrame
 #endif
 }
 
 void
 nsLineLayout::UnlinkFrame(PerFrameData* pfd)
 {
   while (nullptr != pfd) {
     PerFrameData* next = pfd->mNext;
-    if (pfd->GetFlag(PFD_ISLINKEDTOBASE)) {
+    if (pfd->mIsLinkedToBase) {
       // This frame is linked to a ruby base, and should not be freed
       // now. Just unlink it from the span. It will be freed when its
       // base frame gets unlinked.
       pfd->mNext = pfd->mPrev = nullptr;
       pfd = next;
       continue;
     }
 
@@ -641,19 +646,30 @@ nsLineLayout::NewPerFrameData(nsIFrame* 
   }
   else {
     mBaseLineLayout->mFrameFreeList = pfd->mNext;
   }
   pfd->mSpan = nullptr;
   pfd->mNext = nullptr;
   pfd->mPrev = nullptr;
   pfd->mNextAnnotation = nullptr;
-  pfd->mFlags = 0;  // all flags default to false
   pfd->mFrame = aFrame;
 
+  // all flags default to false
+  pfd->mRelativePos = false;
+  pfd->mIsTextFrame = false;
+  pfd->mIsNonEmptyTextFrame = false;
+  pfd->mIsNonWhitespaceTextFrame = false;
+  pfd->mIsLetterFrame = false;
+  pfd->mRecomputeOverflow = false;
+  pfd->mIsBullet = false;
+  pfd->mSkipWhenTrimmingWhitespace = false;
+  pfd->mIsEmpty = false;
+  pfd->mIsLinkedToBase = false;
+
   WritingMode frameWM = aFrame->GetWritingMode();
   WritingMode lineWM = mRootSpan->mWritingMode;
   pfd->mBounds = LogicalRect(lineWM);
   pfd->mMargin = LogicalMargin(lineWM);
   pfd->mBorderPadding = LogicalMargin(lineWM);
   pfd->mOffsets = LogicalMargin(frameWM);
 
   pfd->mJustificationInfo = JustificationInfo();
@@ -851,19 +867,19 @@ nsLineLayout::ReflowFrame(nsIFrame* aFra
     if (reflowState.ComputedISize() == NS_UNCONSTRAINEDSIZE) {
       reflowState.AvailableISize() = availableSpaceOnLine;
     }
     WritingMode stateWM = reflowState.GetWritingMode();
     pfd->mMargin =
       reflowState.ComputedLogicalMargin().ConvertTo(lineWM, stateWM);
     pfd->mBorderPadding =
       reflowState.ComputedLogicalBorderPadding().ConvertTo(lineWM, stateWM);
-    pfd->SetFlag(PFD_RELATIVEPOS,
-                 reflowState.mStyleDisplay->IsRelativelyPositionedStyle());
-    if (pfd->GetFlag(PFD_RELATIVEPOS)) {
+    pfd->mRelativePos =
+      reflowState.mStyleDisplay->IsRelativelyPositionedStyle();
+    if (pfd->mRelativePos) {
       pfd->mOffsets =
         reflowState.ComputedLogicalOffsets().ConvertTo(frameWM, stateWM);
     }
 
     // Calculate whether the the frame should have a start margin and
     // subtract the margin from the available width if necessary.
     // The margin will be applied to the starting inline coordinates of
     // the frame in CanPlaceFrame() after reflowing the frame.
@@ -907,28 +923,29 @@ nsLineLayout::ReflowFrame(nsIFrame* aFra
     aFrame->Reflow(mPresContext, metrics, *reflowStateHolder, aReflowStatus);
   } else {
     static_cast<nsTextFrame*>(aFrame)->
       ReflowText(*this, availableSpaceOnLine, psd->mReflowState->rendContext,
                  metrics, aReflowStatus);
   }
 
   pfd->mJustificationInfo = mJustificationInfo;
+  mJustificationInfo = JustificationInfo();
 
   // See if the frame is a placeholderFrame and if it is process
   // the float. At the same time, check if the frame has any non-collapsed-away
   // content.
   bool placedFloat = false;
   bool isEmpty;
   if (!frameType) {
     isEmpty = pfd->mFrame->IsEmpty();
   } else {
     if (nsGkAtoms::placeholderFrame == frameType) {
       isEmpty = true;
-      pfd->SetFlag(PFD_SKIPWHENTRIMMINGWHITESPACE, true);
+      pfd->mSkipWhenTrimmingWhitespace = true;
       nsIFrame* outOfFlowFrame = nsLayoutUtils::GetFloatFromPlaceholder(aFrame);
       if (outOfFlowFrame) {
         // Add mTrimmableISize to the available width since if the line ends
         // here, the width of the inline content will be reduced by
         // mTrimmableISize.
         nscoord availableISize = psd->mIEnd - (psd->mICoord - mTrimmableISize);
         if (psd->mNoWrap) {
           // If we place floats after inline content where there's
@@ -945,45 +962,44 @@ nsLineLayout::ReflowFrame(nsIFrame* aFra
         placedFloat = mBaseLineLayout->AddFloat(outOfFlowFrame, availableISize);
         NS_ASSERTION(!(outOfFlowFrame->GetType() == nsGkAtoms::letterFrame &&
                        GetFirstLetterStyleOK()),
                     "FirstLetterStyle set on line with floating first letter");
       }
     }
     else if (isText) {
       // Note non-empty text-frames for inline frame compatibility hackery
-      pfd->SetFlag(PFD_ISTEXTFRAME, true);
+      pfd->mIsTextFrame = true;
       nsTextFrame* textFrame = static_cast<nsTextFrame*>(pfd->mFrame);
       isEmpty = !textFrame->HasNoncollapsedCharacters();
       if (!isEmpty) {
-        pfd->SetFlag(PFD_ISNONEMPTYTEXTFRAME, true);
+        pfd->mIsNonEmptyTextFrame = true;
         nsIContent* content = textFrame->GetContent();
 
         const nsTextFragment* frag = content->GetText();
         if (frag) {
-          pfd->SetFlag(PFD_ISNONWHITESPACETEXTFRAME,
-                       !content->TextIsOnlyWhitespace());
+          pfd->mIsNonWhitespaceTextFrame = !content->TextIsOnlyWhitespace();
         }
       }
     }
     else if (nsGkAtoms::brFrame == frameType) {
-      pfd->SetFlag(PFD_SKIPWHENTRIMMINGWHITESPACE, true);
+      pfd->mSkipWhenTrimmingWhitespace = true;
       isEmpty = false;
     } else {
       if (nsGkAtoms::letterFrame==frameType) {
-        pfd->SetFlag(PFD_ISLETTERFRAME, true);
+        pfd->mIsLetterFrame = true;
       }
       if (pfd->mSpan) {
         isEmpty = !pfd->mSpan->mHasNonemptyContent && pfd->mFrame->IsSelfEmpty();
       } else {
         isEmpty = pfd->mFrame->IsEmpty();
       }
     }
   }
-  pfd->SetFlag(PFD_ISEMPTY, isEmpty);
+  pfd->mIsEmpty = isEmpty;
 
   mFloatManager->Untranslate(oldWM, tPt);
 
   NS_ASSERTION(metrics.ISize(lineWM) >= 0, "bad inline size");
   NS_ASSERTION(metrics.BSize(lineWM) >= 0,"bad block size");
   if (metrics.ISize(lineWM) < 0) {
     metrics.ISize(lineWM) = 0;
   }
@@ -1048,17 +1064,17 @@ nsLineLayout::ReflowFrame(nsIFrame* aFra
       }
     }
 
     // Check whether this frame breaks up text runs. All frames break up text
     // runs (hence return false here) except for text frames and inline containers.
     bool continuingTextRun = aFrame->CanContinueTextRun();
     
     // Clear any residual mTrimmableISize if this isn't a text frame
-    if (!continuingTextRun && !pfd->GetFlag(PFD_SKIPWHENTRIMMINGWHITESPACE)) {
+    if (!continuingTextRun && !pfd->mSkipWhenTrimmingWhitespace) {
       mTrimmableISize = 0;
     }
 
     // See if we can place the frame. If we can't fit it, then we
     // return now.
     bool optionalBreakAfterFits;
     NS_ASSERTION(isText ||
                  !reflowStateHolder->IsFloating(),
@@ -1069,16 +1085,19 @@ nsLineLayout::ReflowFrame(nsIFrame* aFra
                       aReflowStatus, &optionalBreakAfterFits)) {
       if (!isEmpty) {
         psd->mHasNonemptyContent = true;
         mLineIsEmpty = false;
         if (!pfd->mSpan) {
           // nonempty leaf content has been placed
           mLineAtStart = false;
         }
+        if (nsGkAtoms::rubyFrame == frameType) {
+          mHasRuby = true;
+        }
       }
 
       // Place the frame, updating aBounds with the final size and
       // location.  Then apply the bottom+right margins (as
       // appropriate) to the frame.
       PlaceFrame(pfd, metrics);
       PerSpanData* span = pfd->mSpan;
       if (span) {
@@ -1215,17 +1234,17 @@ nsLineLayout::CanPlaceFrame(PerFrameData
    * However, none of that applies if this is a letter frame (XXXbz why?)
    *
    * For box-decoration-break:clone we apply the end margin on all
    * continuations (that are not letter frames).
    */
   if ((NS_FRAME_IS_NOT_COMPLETE(aStatus) ||
        pfd->mFrame->LastInFlow()->GetNextContinuation() ||
        pfd->mFrame->FrameIsNonLastInIBSplit()) &&
-      !pfd->GetFlag(PFD_ISLETTERFRAME) &&
+      !pfd->mIsLetterFrame &&
       pfd->mFrame->StyleBorder()->mBoxDecorationBreak ==
         NS_STYLE_BOX_DECORATION_BREAK_SLICE) {
     pfd->mMargin.IEnd(lineWM) = 0;
   }
 
   // Apply the start margin to the frame bounds.
   nscoord startMargin = pfd->mMargin.IStart(lineWM);
   nscoord endMargin = pfd->mMargin.IEnd(lineWM);
@@ -1390,17 +1409,17 @@ nsLineLayout::AddBulletFrame(nsIFrame* a
   if (!static_cast<nsBlockFrame*>(blockFrame)->BulletIsEmpty()) {
     mHasBullet = true;
     mLineBox->SetHasBullet();
   }
 
   WritingMode lineWM = mRootSpan->mWritingMode;
   PerFrameData* pfd = NewPerFrameData(aFrame);
   mRootSpan->AppendFrame(pfd);
-  pfd->SetFlag(PFD_ISBULLET, true);
+  pfd->mIsBullet = true;
   if (aMetrics.BlockStartAscent() == nsHTMLReflowMetrics::ASK_FOR_BASELINE) {
     pfd->mAscent = aFrame->GetLogicalBaseline(lineWM);
   } else {
     pfd->mAscent = aMetrics.BlockStartAscent();
   }
 
   // Note: block-coord value will be updated during block-direction alignment
   pfd->mBounds = LogicalRect(lineWM, aFrame->GetRect(), mContainerWidth);
@@ -1508,31 +1527,33 @@ nsLineLayout::VerticalAlignLine()
   for (PerFrameData* pfd = psd->mFirstFrame; pfd; pfd = pfd->mNext) {
     if (pfd->mBlockDirAlign == VALIGN_OTHER) {
       pfd->mBounds.BStart(lineWM) += baselineBCoord;
       pfd->mFrame->SetRect(lineWM, pfd->mBounds, mContainerWidth);
     }
   }
   PlaceTopBottomFrames(psd, -mBStartEdge, lineBSize);
 
-  // Fill in returned line-box and max-element-width data
-  mLineBox->SetBounds(lineWM,
-                      psd->mIStart, mBStartEdge,
-                      psd->mICoord - psd->mIStart, lineBSize,
-                      mContainerWidth);
+  mFinalLineBSize = lineBSize;
+  if (mGotLineBox) {
+    // Fill in returned line-box and max-element-width data
+    mLineBox->SetBounds(lineWM,
+                        psd->mIStart, mBStartEdge,
+                        psd->mICoord - psd->mIStart, lineBSize,
+                        mContainerWidth);
 
-  mFinalLineBSize = lineBSize;
-  mLineBox->SetLogicalAscent(baselineBCoord - mBStartEdge);
+    mLineBox->SetLogicalAscent(baselineBCoord - mBStartEdge);
 #ifdef NOISY_BLOCKDIR_ALIGN
-  printf(
-    "  [line]==> bounds{x,y,w,h}={%d,%d,%d,%d} lh=%d a=%d\n",
-    mLineBox->GetBounds().IStart(lineWM), mLineBox->GetBounds().BStart(lineWM),
-    mLineBox->GetBounds().ISize(lineWM), mLineBox->GetBounds().BSize(lineWM),
-    mFinalLineBSize, mLineBox->GetLogicalAscent());
+    printf(
+      "  [line]==> bounds{x,y,w,h}={%d,%d,%d,%d} lh=%d a=%d\n",
+      mLineBox->GetBounds().IStart(lineWM), mLineBox->GetBounds().BStart(lineWM),
+      mLineBox->GetBounds().ISize(lineWM), mLineBox->GetBounds().BSize(lineWM),
+      mFinalLineBSize, mLineBox->GetLogicalAscent());
 #endif
+  }
 }
 
 // Place frames with CSS property vertical-align: top or bottom.
 void
 nsLineLayout::PlaceTopBottomFrames(PerSpanData* psd,
                                    nscoord aDistanceFromStart,
                                    nscoord aLineBSize)
 {
@@ -1709,18 +1730,18 @@ nsLineLayout::VerticalAlignFrames(PerSpa
 
     // We shouldn't include any whitespace that collapses, unless we're
     // preformatted (in which case it shouldn't, but the width=0 test is
     // perhaps incorrect).  This includes whitespace at the beginning of
     // a line and whitespace preceded (?) by other whitespace.
     // See bug 134580 and bug 155333.
     zeroEffectiveSpanBox = true;
     for (PerFrameData* pfd = psd->mFirstFrame; pfd; pfd = pfd->mNext) {
-      if (pfd->GetFlag(PFD_ISTEXTFRAME) &&
-          (pfd->GetFlag(PFD_ISNONWHITESPACETEXTFRAME) || preMode ||
+      if (pfd->mIsTextFrame &&
+          (pfd->mIsNonWhitespaceTextFrame || preMode ||
            pfd->mBounds.ISize(mRootSpan->mWritingMode) != 0)) {
         zeroEffectiveSpanBox = false;
         break;
       }
     }
   }
   psd->mZeroEffectiveSpanBox = zeroEffectiveSpanBox;
 
@@ -1754,17 +1775,17 @@ nsLineLayout::VerticalAlignFrames(PerSpa
       CalcLineHeight(spanFrame->GetContent(), spanFrame->StyleContext(),
                      mBlockReflowState->ComputedHeight(),
                      inflation);
     nscoord contentBSize = spanFramePFD->mBounds.BSize(lineWM) -
       spanFramePFD->mBorderPadding.BStartEnd(lineWM);
 
     // Special-case for a ::first-letter frame, set the line height to
     // the frame block size if the user has left line-height == normal
-    if (spanFramePFD->GetFlag(PFD_ISLETTERFRAME) &&
+    if (spanFramePFD->mIsLetterFrame &&
         !spanFrame->GetPrevInFlow() &&
         spanFrame->StyleText()->mLineHeight.GetUnit() == eStyleUnit_Normal) {
       logicalBSize = spanFramePFD->mBounds.BSize(lineWM);
     }
 
     nscoord leading = logicalBSize - contentBSize;
     psd->mBStartLeading = leading / 2;
     psd->mBEndLeading = leading - psd->mBStartLeading;
@@ -2064,21 +2085,21 @@ nsLineLayout::VerticalAlignFrames(PerSpa
       //        -- Note #1: With this code enabled and with the fact that we are not
       //           using Em[Ascent|Descent] as nsDimensions for text metrics in
       //           GFX mean that the discussion in bug 13072 cannot hold.
       //        -- Note #2: We still don't want empty-text frames to interfere.
       //           For example in quirks mode, avoiding empty text frames prevents
       //           "tall" lines around elements like <hr> since the rules of <hr>
       //           in quirks.css have pseudo text contents with LF in them.
 #if 0
-      if (!pfd->GetFlag(PFD_ISTEXTFRAME)) {
+      if (!pfd->mIsTextFrame) {
 #else
       // Only consider non empty text frames when line-height=normal
-      bool canUpdate = !pfd->GetFlag(PFD_ISTEXTFRAME);
-      if (!canUpdate && pfd->GetFlag(PFD_ISNONWHITESPACETEXTFRAME)) {
+      bool canUpdate = !pfd->mIsTextFrame;
+      if (!canUpdate && pfd->mIsNonWhitespaceTextFrame) {
         canUpdate =
           frame->StyleText()->mLineHeight.GetUnit() == eStyleUnit_Normal;
       }
       if (canUpdate) {
 #endif
         nscoord blockStart, blockEnd;
         if (frameSpan) {
           // For spans that were are now placing, use their position
@@ -2133,17 +2154,18 @@ nsLineLayout::VerticalAlignFrames(PerSpa
     //      is handled by mZeroEffectiveSpanBox
     //  (2) if this line has a bullet
     //  (3) if this is the last line of an LI, DT, or DD element
     //      (The last line before a block also counts, but not before a
     //      BR) (NN4/IE5 quirk)
 
     // (1) and (2) above
     bool applyMinLH = !psd->mZeroEffectiveSpanBox || mHasBullet;
-    bool isLastLine = (!mLineBox->IsLineWrapped() && !mLineEndsInBR);
+    bool isLastLine = !mGotLineBox ||
+      (!mLineBox->IsLineWrapped() && !mLineEndsInBR);
     if (!applyMinLH && isLastLine) {
       nsIContent* blockContent = mRootSpan->mFrame->mFrame->GetContent();
       if (blockContent) {
         nsIAtom *blockTagAtom = blockContent->Tag();
         // (3) above, if the last line of LI, DT, or DD
         if (blockTagAtom == nsGkAtoms::li ||
             blockTagAtom == nsGkAtoms::dt ||
             blockTagAtom == nsGkAtoms::dd) {
@@ -2383,38 +2405,37 @@ nsLineLayout::TrimTrailingWhiteSpaceIn(P
               // will be run after this method.
               SlideSpanFrameRect(pfd->mFrame, deltaISize);
             }
           }
         }
         return true;
       }
     }
-    else if (!pfd->GetFlag(PFD_ISTEXTFRAME) &&
-             !pfd->GetFlag(PFD_SKIPWHENTRIMMINGWHITESPACE)) {
+    else if (!pfd->mIsTextFrame && !pfd->mSkipWhenTrimmingWhitespace) {
       // If we hit a frame on the end that's not text and not a placeholder,
       // then there is no trailing whitespace to trim. Stop the search.
       *aDeltaISize = 0;
       return true;
     }
-    else if (pfd->GetFlag(PFD_ISTEXTFRAME)) {
+    else if (pfd->mIsTextFrame) {
       // Call TrimTrailingWhiteSpace even on empty textframes because they
       // might have a soft hyphen which should now appear, changing the frame's
       // width
       nsTextFrame::TrimOutput trimOutput = static_cast<nsTextFrame*>(pfd->mFrame)->
           TrimTrailingWhiteSpace(mBlockReflowState->rendContext);
 #ifdef NOISY_TRIM
       nsFrame::ListTag(stdout, psd->mFrame->mFrame);
       printf(": trim of ");
       nsFrame::ListTag(stdout, pfd->mFrame);
       printf(" returned %d\n", trimOutput.mDeltaWidth);
 #endif
 
       if (trimOutput.mChanged) {
-        pfd->SetFlag(PFD_RECOMPUTEOVERFLOW, true);
+        pfd->mRecomputeOverflow = true;
       }
 
       // Delta width not being zero means that
       // there is trimmed space in the frame.
       if (trimOutput.mDeltaWidth) {
         pfd->mBounds.ISize(lineWM) -= trimOutput.mDeltaWidth;
 
         // If any trailing space is trimmed, the justification opportunity
@@ -2446,17 +2467,17 @@ nsLineLayout::TrimTrailingWhiteSpaceIn(P
             // that are direct children of the block will be updated
             // later, however, because the VerticalAlignFrames method
             // will be run after this method.
             SlideSpanFrameRect(pfd->mFrame, trimOutput.mDeltaWidth);
           }
         }
       }
 
-      if (pfd->GetFlag(PFD_ISNONEMPTYTEXTFRAME) || trimOutput.mChanged) {
+      if (pfd->mIsNonEmptyTextFrame || trimOutput.mChanged) {
         // Pass up to caller so they can shrink their span
         *aDeltaISize = trimOutput.mDeltaWidth;
         return true;
       }
     }
     pfd = pfd->mPrev;
   }
 
@@ -2470,92 +2491,154 @@ nsLineLayout::TrimTrailingWhiteSpace()
   PerSpanData* psd = mRootSpan;
   nscoord deltaISize;
   TrimTrailingWhiteSpaceIn(psd, &deltaISize);
   return 0 != deltaISize;
 }
 
 struct nsLineLayout::JustificationComputationState
 {
+  PerFrameData* mFirstParticipant;
   PerFrameData* mLastParticipant;
+  // Whether we are going across a boundary of ruby base, i.e.
+  // entering one, leaving one, or both.
+  bool mCrossingRubyBaseBoundary;
+
+  JustificationComputationState()
+    : mFirstParticipant(nullptr)
+    , mLastParticipant(nullptr)
+    , mCrossingRubyBaseBoundary(false) { }
 };
 
 /**
- * This function returns the total number of
- * expansion opportunities in the given span.
+ * Compute the justification info of the given span, and store the
+ * number of inner opportunities into the frame's justification info.
+ * It returns the number of non-inner opportunities it detects.
  */
 int32_t
 nsLineLayout::ComputeFrameJustification(PerSpanData* aPSD,
                                         JustificationComputationState& aState)
 {
   NS_ASSERTION(aPSD, "null arg");
   NS_ASSERTION(!aState.mLastParticipant || !aState.mLastParticipant->mSpan,
                "Last participant shall always be a leaf frame");
-  int32_t result = 0;
+  bool firstChild = true;
+  int32_t& innerOpportunities =
+    aPSD->mFrame->mJustificationInfo.mInnerOpportunities;
+  MOZ_ASSERT(innerOpportunities == 0,
+             "Justification info should not have been set yet.");
+  int32_t outerOpportunities = 0;
 
   for (PerFrameData* pfd = aPSD->mFirstFrame; pfd; pfd = pfd->mNext) {
     if (!pfd->ParticipatesInJustification()) {
       continue;
     }
 
+    bool isRubyBase = pfd->mFrame->GetType() == nsGkAtoms::rubyBaseFrame;
+    if (isRubyBase) {
+      aState.mCrossingRubyBaseBoundary = true;
+    }
+
+    int extraOpportunities = 0;
     if (pfd->mSpan) {
       PerSpanData* span = pfd->mSpan;
-      result += ComputeFrameJustification(span, aState);
+      extraOpportunities = ComputeFrameJustification(span, aState);
+      innerOpportunities += pfd->mJustificationInfo.mInnerOpportunities;
     } else {
       const auto& info = pfd->mJustificationInfo;
-      if (pfd->GetFlag(PFD_ISTEXTFRAME)) {
-        result += info.mInnerOpportunities;
+      if (pfd->mIsTextFrame) {
+        innerOpportunities += info.mInnerOpportunities;
       }
 
       PerFrameData* prev = aState.mLastParticipant;
-      if (prev) {
+      if (!prev) {
+        aState.mFirstParticipant = pfd;
+      } else {
         auto& assign = pfd->mJustificationAssignment;
         auto& prevAssign = prev->mJustificationAssignment;
         const auto& prevInfo = prev->mJustificationInfo;
 
-        if (info.mIsStartJustifiable || prevInfo.mIsEndJustifiable) {
-          result++;
-          if (!info.mIsStartJustifiable) {
+        if (info.mIsStartJustifiable ||
+            prevInfo.mIsEndJustifiable ||
+            aState.mCrossingRubyBaseBoundary) {
+          extraOpportunities = 1;
+          if (aState.mCrossingRubyBaseBoundary) {
+            // For ruby alignment with value space-around, there is
+            // always an expansion opportunity at the boundary of a ruby
+            // base, and it always generates one gap at each side. If we
+            // don't do it here, the interaction between text align and
+            // and ruby align could be strange.
+            prevAssign.mGapsAtEnd = 1;
+            assign.mGapsAtStart = 1;
+          } else if (!info.mIsStartJustifiable) {
             prevAssign.mGapsAtEnd = 2;
             assign.mGapsAtStart = 0;
           } else if (!prevInfo.mIsEndJustifiable) {
             prevAssign.mGapsAtEnd = 0;
             assign.mGapsAtStart = 2;
           } else {
             prevAssign.mGapsAtEnd = 1;
             assign.mGapsAtStart = 1;
           }
         }
       }
 
       aState.mLastParticipant = pfd;
+      aState.mCrossingRubyBaseBoundary = isRubyBase;
+    }
+
+    if (firstChild) {
+      outerOpportunities = extraOpportunities;
+      firstChild = false;
+    } else {
+      innerOpportunities += extraOpportunities;
     }
   }
 
-  return result;
+  return outerOpportunities;
 }
 
 void
 nsLineLayout::AdvanceAnnotationInlineBounds(PerFrameData* aPFD,
                                             nscoord aContainerWidth,
                                             nscoord aDeltaICoord,
                                             nscoord aDeltaISize)
 {
-  MOZ_ASSERT(aPFD->mFrame->GetType() == nsGkAtoms::rubyTextFrame ||
-             aPFD->mFrame->GetType() == nsGkAtoms::rubyTextContainerFrame);
+  nsIFrame* frame = aPFD->mFrame;
+  nsIAtom* frameType = frame->GetType();
+  MOZ_ASSERT(frameType == nsGkAtoms::rubyTextFrame ||
+             frameType == nsGkAtoms::rubyTextContainerFrame);
   MOZ_ASSERT(aPFD->mSpan, "rt and rtc should have span.");
 
+  PerSpanData* psd = aPFD->mSpan;
   WritingMode lineWM = mRootSpan->mWritingMode;
-  WritingMode frameWM = aPFD->mSpan->mWritingMode;
-  LogicalRect bounds = aPFD->mFrame->GetLogicalRect(aContainerWidth);
-  bounds = bounds.ConvertTo(lineWM, frameWM, aContainerWidth);
+  LogicalRect bounds(lineWM, frame->GetRect(), aContainerWidth);
   bounds.IStart(lineWM) += aDeltaICoord;
-  bounds.ISize(lineWM) += aDeltaISize;
-  aPFD->mBounds = bounds.ConvertTo(frameWM, lineWM, aContainerWidth);
-  aPFD->mFrame->SetRect(frameWM, aPFD->mBounds, aContainerWidth);
+
+  // Check whether this expansion should be counted into the reserved
+  // isize or not. When it is a ruby text container, and it has some
+  // children linked to the base, it must not have reserved isize,
+  // or its children won't align with their bases.  Otherwise, this
+  // expansion should be reserved.  There are two cases a ruby text
+  // container does not have children linked to the base:
+  // 1. it is a container for span; 2. its children are collapsed.
+  // See bug 1055674 for the second case.
+  if (frameType == nsGkAtoms::rubyTextFrame ||
+      // This ruby text container is a span.
+      (psd->mFirstFrame == psd->mLastFrame && psd->mFirstFrame &&
+       !psd->mFirstFrame->mIsLinkedToBase)) {
+    nscoord reservedISize = RubyUtils::GetReservedISize(frame);
+    RubyUtils::SetReservedISize(frame, reservedISize + aDeltaISize);
+  } else {
+    // It is a normal ruby text container. Its children will expand
+    // themselves properly. We only need to expand its own size here.
+    bounds.ISize(lineWM) += aDeltaISize;
+  }
+  aPFD->mBounds = bounds;
+  aPFD->mFrame->SetRect(lineWM, bounds, aContainerWidth);
 }
 
 /**
  * This function applies the changes of icoord and isize caused by
  * justification to annotations of the given frame.
  * aPFD must be one of the frames in aContainingSpan.
  */
 void
@@ -2573,17 +2656,17 @@ nsLineLayout::ApplyLineJustificationToAn
     // There are two cases where an annotation frame has siblings which
     // do not attached to a ruby base-level frame:
     // 1. there's an intra-annotation whitespace which has no intra-base
     //    white-space to pair with;
     // 2. there are not enough ruby bases to be paired with annotations.
     // In these cases, their size should not be affected, but we still
     // need to move them so that they won't overlap other frames.
     PerFrameData* sibling = pfd->mNext;
-    while (sibling && !sibling->GetFlag(PFD_ISLINKEDTOBASE)) {
+    while (sibling && !sibling->mIsLinkedToBase) {
       AdvanceAnnotationInlineBounds(sibling, containerWidth,
                                     aDeltaICoord + aDeltaISize, 0);
       sibling = sibling->mNext;
     }
 
     pfd = pfd->mNextAnnotation;
   }
 }
@@ -2592,53 +2675,131 @@ nscoord
 nsLineLayout::ApplyFrameJustification(PerSpanData* aPSD,
                                       JustificationApplicationState& aState)
 {
   NS_ASSERTION(aPSD, "null arg");
 
   nscoord deltaICoord = 0;
   for (PerFrameData* pfd = aPSD->mFirstFrame; pfd != nullptr; pfd = pfd->mNext) {
     // Don't reposition bullets (and other frames that occur out of X-order?)
-    if (!pfd->GetFlag(PFD_ISBULLET)) {
+    if (!pfd->mIsBullet) {
       nscoord dw = 0;
       WritingMode lineWM = mRootSpan->mWritingMode;
+      const auto& assign = pfd->mJustificationAssignment;
 
-      pfd->mBounds.IStart(lineWM) += deltaICoord;
-
-      if (true == pfd->GetFlag(PFD_ISTEXTFRAME)) {
+      if (true == pfd->mIsTextFrame) {
         if (aState.IsJustifiable()) {
           // Set corresponding justification gaps here, so that the
           // text frame knows how it should add gaps at its sides.
           const auto& info = pfd->mJustificationInfo;
-          const auto& assign = pfd->mJustificationAssignment;
           auto textFrame = static_cast<nsTextFrame*>(pfd->mFrame);
           textFrame->AssignJustificationGaps(assign);
           dw = aState.Consume(JustificationUtils::CountGaps(info, assign));
         }
 
         if (dw) {
-          pfd->SetFlag(PFD_RECOMPUTEOVERFLOW, true);
+          pfd->mRecomputeOverflow = true;
         }
       }
       else {
         if (nullptr != pfd->mSpan) {
           dw = ApplyFrameJustification(pfd->mSpan, aState);
         }
       }
 
       pfd->mBounds.ISize(lineWM) += dw;
+      if (!pfd->mIsTextFrame && assign.TotalGaps()) {
+        // It is possible that we assign gaps to non-text frame.
+        // Apply the gaps as margin around the frame.
+        deltaICoord += aState.Consume(assign.mGapsAtStart);
+        dw += aState.Consume(assign.mGapsAtEnd);
+      }
+      pfd->mBounds.IStart(lineWM) += deltaICoord;
 
       ApplyLineJustificationToAnnotations(pfd, aPSD, deltaICoord, dw);
       deltaICoord += dw;
       pfd->mFrame->SetRect(lineWM, pfd->mBounds, ContainerWidthForSpan(aPSD));
     }
   }
   return deltaICoord;
 }
 
+/**
+ * This method expands the given frame by the given reserved isize.
+ */
+void
+nsLineLayout::ExpandRubyBox(PerFrameData* aFrame, nscoord aReservedISize,
+                            nscoord aContainerWidth)
+{
+  int32_t opportunities = aFrame->mJustificationInfo.mInnerOpportunities;
+  // Each expandable ruby box has an gap at each of its sides. For
+  // rb/rbc, see comment in ComputeFrameJustification; for rt/rtc,
+  // see comment in this method below.
+  int32_t gaps = opportunities * 2 + 2;
+  JustificationApplicationState state(gaps, aReservedISize);
+  ApplyFrameJustification(aFrame->mSpan, state);
+
+  WritingMode lineWM = mRootSpan->mWritingMode;
+  aFrame->mBounds.ISize(lineWM) += aReservedISize;
+  aFrame->mFrame->SetRect(lineWM, aFrame->mBounds, aContainerWidth);
+}
+
+/**
+ * This method expands the given frame by the reserved inline size.
+ * It also expands its annotations if they are expandable and have
+ * reserved isize larger than zero.
+ */
+void
+nsLineLayout::ExpandRubyBoxWithAnnotations(PerFrameData* aFrame,
+                                           nscoord aContainerWidth)
+{
+  nscoord reservedISize = RubyUtils::GetReservedISize(aFrame->mFrame);
+  if (reservedISize) {
+    ExpandRubyBox(aFrame, reservedISize, aContainerWidth);
+  }
+
+  for (PerFrameData* annotation = aFrame->mNextAnnotation;
+       annotation; annotation = annotation->mNextAnnotation) {
+    nscoord reservedISize = RubyUtils::GetReservedISize(annotation->mFrame);
+    if (!reservedISize) {
+      continue;
+    }
+
+    MOZ_ASSERT(annotation->mSpan);
+    JustificationComputationState computeState;
+    ComputeFrameJustification(annotation->mSpan, computeState);
+    if (!computeState.mFirstParticipant) {
+      continue;
+    }
+    // Add one gap at each side of this annotation.
+    computeState.mFirstParticipant->mJustificationAssignment.mGapsAtStart = 1;
+    computeState.mLastParticipant->mJustificationAssignment.mGapsAtEnd = 1;
+    ExpandRubyBox(annotation, reservedISize, aContainerWidth);
+    ExpandInlineRubyBoxes(annotation->mSpan);
+  }
+}
+
+/**
+ * This method looks for all expandable ruby box in the given span, and
+ * calls ExpandRubyBox to expand them in depth-first preorder.
+ */
+void
+nsLineLayout::ExpandInlineRubyBoxes(PerSpanData* aSpan)
+{
+  nscoord containerWidth = ContainerWidthForSpan(aSpan);
+  for (PerFrameData* pfd = aSpan->mFirstFrame; pfd; pfd = pfd->mNext) {
+    if (RubyUtils::IsExpandableRubyBox(pfd->mFrame)) {
+      ExpandRubyBoxWithAnnotations(pfd, containerWidth);
+    }
+    if (pfd->mSpan) {
+      ExpandInlineRubyBoxes(pfd->mSpan);
+    }
+  }
+}
+
 // Align inline frames within the line according to the CSS text-align
 // property.
 void
 nsLineLayout::TextAlignLine(nsLineBox* aLine,
                             bool aIsLastLine)
 {
   /**
    * NOTE: aIsLastLine ain't necessarily so: it is correctly set by caller
@@ -2673,28 +2834,48 @@ nsLineLayout::TextAlignLine(nsLineBox* a
       if (textAlign == NS_STYLE_TEXT_ALIGN_JUSTIFY) {
         textAlign = NS_STYLE_TEXT_ALIGN_DEFAULT;
       }
     } else {
       textAlign = mStyleText->mTextAlignLast;
     }
   }
 
-  if ((remainingISize > 0 || textAlignTrue) &&
-      !(mBlockReflowState->frame->IsSVGText())) {
+  bool isSVG = mBlockReflowState->frame->IsSVGText();
+  bool doTextAlign = remainingISize > 0 || textAlignTrue;
 
+  int32_t additionalGaps = 0;
+  if (!isSVG && (mHasRuby || (doTextAlign &&
+                              textAlign == NS_STYLE_TEXT_ALIGN_JUSTIFY))) {
+    JustificationComputationState computeState;
+    ComputeFrameJustification(psd, computeState);
+    if (mHasRuby && computeState.mFirstParticipant) {
+      PerFrameData* firstFrame = computeState.mFirstParticipant;
+      if (firstFrame->mFrame->StyleContext()->IsDirectlyInsideRuby()) {
+        MOZ_ASSERT(!firstFrame->mJustificationAssignment.mGapsAtStart);
+        firstFrame->mJustificationAssignment.mGapsAtStart = 1;
+        additionalGaps++;
+      }
+      PerFrameData* lastFrame = computeState.mLastParticipant;
+      if (lastFrame->mFrame->StyleContext()->IsDirectlyInsideRuby()) {
+        MOZ_ASSERT(!lastFrame->mJustificationAssignment.mGapsAtEnd);
+        lastFrame->mJustificationAssignment.mGapsAtEnd = 1;
+        additionalGaps++;
+      }
+    }
+  }
+
+  if (!isSVG && doTextAlign) {
     switch (textAlign) {
       case NS_STYLE_TEXT_ALIGN_JUSTIFY: {
-        JustificationComputationState computeState = {
-          nullptr // mLastParticipant
-        };
-        int32_t opportunities = ComputeFrameJustification(psd, computeState);
+        int32_t opportunities =
+          psd->mFrame->mJustificationInfo.mInnerOpportunities;
         if (opportunities > 0) {
-          JustificationApplicationState applyState(
-              opportunities * 2, remainingISize);
+          int32_t gaps = opportunities * 2 + additionalGaps;
+          JustificationApplicationState applyState(gaps, remainingISize);
 
           // Apply the justification, and make sure to update our linebox
           // width to account for it.
           aLine->ExpandBy(ApplyFrameJustification(psd, applyState),
                           ContainerWidthForSpan(psd));
 
           MOZ_ASSERT(applyState.mGaps.mHandled == applyState.mGaps.mCount,
                      "Unprocessed justification gaps");
@@ -2730,16 +2911,20 @@ nsLineLayout::TextAlignLine(nsLineBox* a
 
       case NS_STYLE_TEXT_ALIGN_CENTER:
       case NS_STYLE_TEXT_ALIGN_MOZ_CENTER:
         dx = remainingISize / 2;
         break;
     }
   }
 
+  if (mHasRuby) {
+    ExpandInlineRubyBoxes(mRootSpan);
+  }
+
   if (mPresContext->BidiEnabled() &&
       (!mPresContext->IsVisualMode() || !lineWM.IsBidiLTR())) {
     nsBidiPresUtils::ReorderFrames(psd->mFirstFrame->mFrame,
                                    aLine->GetChildCount(),
                                    lineWM, mContainerWidth,
                                    psd->mIStart + mTextIndent + dx);
     if (dx) {
       aLine->IndentBy(dx, mContainerWidth);
@@ -2793,17 +2978,17 @@ nsLineLayout::RelativePositionFrames(Per
     overflowAreas.ScrollableOverflow() = overflowAreas.VisualOverflow();
   }
 
   for (PerFrameData* pfd = psd->mFirstFrame; pfd; pfd = pfd->mNext) {
     nsIFrame* frame = pfd->mFrame;
     nsPoint origin = frame->GetPosition();
 
     // Adjust the origin of the frame
-    if (pfd->GetFlag(PFD_RELATIVEPOS)) {
+    if (pfd->mRelativePos) {
       //XXX temporary until ApplyRelativePositioning can handle logical offsets
       nsMargin physicalOffsets =
         pfd->mOffsets.GetPhysicalMargin(pfd->mFrame->GetWritingMode());
       // right and bottom are handled by
       // nsHTMLReflowState::ComputeRelativeOffsets
       nsHTMLReflowState::ApplyRelativePositioning(pfd->mFrame,
                                                   physicalOffsets,
                                                   &origin);
@@ -2824,22 +3009,22 @@ nsLineLayout::RelativePositionFrames(Per
     // <b>x</b> and <b>y</b> which were computed above.
     nsOverflowAreas r;
     if (pfd->mSpan) {
       // Compute a new combined area for the child span before
       // aggregating it into our combined area.
       RelativePositionFrames(pfd->mSpan, r);
     } else {
       r = pfd->mOverflowAreas;
-      if (pfd->GetFlag(PFD_ISTEXTFRAME)) {
+      if (pfd->mIsTextFrame) {
         // We need to recompute overflow areas in two cases:
         // (1) When PFD_RECOMPUTEOVERFLOW is set due to trimming
         // (2) When there are text decorations, since we can't recompute the
         //     overflow area until Reflow and VerticalAlignLine have finished
-        if (pfd->GetFlag(PFD_RECOMPUTEOVERFLOW) ||
+        if (pfd->mRecomputeOverflow ||
             frame->StyleContext()->HasTextDecorationLines()) {
           nsTextFrame* f = static_cast<nsTextFrame*>(frame);
           r = f->RecomputeOverflow(*mBlockReflowState);
         }
         frame->FinishAndStoreOverflow(r, frame->GetSize());
       }
 
       // If we have something that's not an inline but with a complex frame
--- a/layout/generic/nsLineLayout.h
+++ b/layout/generic/nsLineLayout.h
@@ -111,16 +111,25 @@ public:
     PushFrame(aFrame);
   }
 
   /**
    * Place frames in the block direction (CSS property vertical-align)
    */
   void VerticalAlignLine();
 
+  // Get the final size of the line, in the block direction.
+  // Do not call this until after we've called VerticalAlignLine.
+  nscoord GetFinalLineBSize() const
+  {
+    NS_ASSERTION(mFinalLineBSize != nscoord_MIN,
+                 "VerticalAlignLine should have been called before");
+    return mFinalLineBSize;
+  }
+
   bool TrimTrailingWhiteSpace();
 
   /**
    * Place frames in the inline direction (CSS property text-align).
    */
   void TextAlignLine(nsLineBox* aLine, bool aIsLastLine);
 
   /**
@@ -432,57 +441,37 @@ protected:
     nsOverflowAreas mOverflowAreas;
 
     // From reflow-state
     mozilla::LogicalMargin mMargin;        // in *line* writing mode
     mozilla::LogicalMargin mBorderPadding; // in *line* writing mode
     mozilla::LogicalMargin mOffsets;       // in *frame* writing mode
 
     // state for text justification
+    // Note that, although all frames would have correct inner
+    // opportunities computed after ComputeFrameJustification, start
+    // and end justifiable info are not reliable for non-text frames.
     mozilla::JustificationInfo mJustificationInfo;
     mozilla::JustificationAssignment mJustificationAssignment;
     
-// PerFrameData flags
-#define PFD_RELATIVEPOS                 0x00000001
-#define PFD_ISTEXTFRAME                 0x00000002
-#define PFD_ISNONEMPTYTEXTFRAME         0x00000004
-#define PFD_ISNONWHITESPACETEXTFRAME    0x00000008
-#define PFD_ISLETTERFRAME               0x00000010
-#define PFD_RECOMPUTEOVERFLOW           0x00000020
-#define PFD_ISBULLET                    0x00000040
-#define PFD_SKIPWHENTRIMMINGWHITESPACE  0x00000080
-#define PFD_ISEMPTY                     0x00000100
-#define PFD_ISLINKEDTOBASE              0x00000200
-#define PFD_LASTFLAG                    PFD_ISLINKEDTOBASE
+    // PerFrameData flags
+    bool mRelativePos : 1;
+    bool mIsTextFrame : 1;
+    bool mIsNonEmptyTextFrame : 1;
+    bool mIsNonWhitespaceTextFrame : 1;
+    bool mIsLetterFrame : 1;
+    bool mRecomputeOverflow : 1;
+    bool mIsBullet : 1;
+    bool mSkipWhenTrimmingWhitespace : 1;
+    bool mIsEmpty : 1;
+    bool mIsLinkedToBase : 1;
 
     // Other state we use
-    uint16_t mFlags;
     uint8_t mBlockDirAlign;
 
-    static_assert(PFD_LASTFLAG <= UINT16_MAX,
-                  "Flag value exceeds the length of flags variable.");
-
-    void SetFlag(uint32_t aFlag, bool aValue)
-    {
-      NS_ASSERTION(aFlag<=PFD_LASTFLAG, "bad flag");
-      if (aValue) { // set flag
-        mFlags |= aFlag;
-      }
-      else {        // unset flag
-        mFlags &= ~aFlag;
-      }
-    }
-
-    bool GetFlag(uint32_t aFlag) const
-    {
-      NS_ASSERTION(aFlag<=PFD_LASTFLAG, "bad flag");
-      return !!(mFlags & aFlag);
-    }
-
-
     PerFrameData* Last() {
       PerFrameData* pfd = this;
       while (pfd->mNext) {
         pfd = pfd->mNext;
       }
       return pfd;
     }
 
@@ -494,17 +483,17 @@ protected:
     bool IsEndJustifiable() const
     {
       return mJustificationInfo.mIsEndJustifiable;
     }
 
     bool ParticipatesInJustification() const
     {
       // Skip bullets and empty frames
-      return !GetFlag(PFD_ISBULLET) && !GetFlag(PFD_ISEMPTY);
+      return !mIsBullet && !mIsEmpty;
     }
   };
   PerFrameData* mFrameFreeList;
 
   struct PerSpanData {
     union {
       PerSpanData* mParent;
       PerSpanData* mNextFreeSpan;
@@ -596,16 +585,17 @@ protected:
   bool mLineEndsInBR            : 1;
   bool mNeedBackup              : 1;
   bool mInFirstLine             : 1;
   bool mGotLineBox              : 1;
   bool mInFirstLetter           : 1;
   bool mHasBullet               : 1;
   bool mDirtyNextLine           : 1;
   bool mLineAtStart             : 1;
+  bool mHasRuby                 : 1;
 
   int32_t mSpanDepth;
 #ifdef DEBUG
   int32_t mSpansAllocated, mSpansFreed;
   int32_t mFramesAllocated, mFramesFreed;
 #endif
   PLArenaPool mArena; // Per span and per frame data, 4 byte aligned
 
@@ -680,16 +670,24 @@ protected:
                                            nscoord aDeltaICoord,
                                            nscoord aDeltaISize);
 
   // Apply justification.  The return value is the amount by which the width of
   // the span corresponding to aPSD got increased due to justification.
   nscoord ApplyFrameJustification(
       PerSpanData* aPSD, mozilla::JustificationApplicationState& aState);
 
+  void ExpandRubyBox(PerFrameData* aFrame, nscoord aReservedISize,
+                     nscoord aContainerWidth);
+
+  void ExpandRubyBoxWithAnnotations(PerFrameData* aFrame,
+                                    nscoord aContainerWidth);
+
+  void ExpandInlineRubyBoxes(PerSpanData* aSpan);
+
   void AttachFrameToBaseLineLayout(PerFrameData* aFrame);
 
 #ifdef DEBUG
   void DumpPerSpanData(PerSpanData* psd, int32_t aIndent);
 #endif
 };
 
 #endif /* nsLineLayout_h___ */
--- a/layout/generic/nsRubyBaseContainerFrame.cpp
+++ b/layout/generic/nsRubyBaseContainerFrame.cpp
@@ -8,16 +8,17 @@
 
 #include "nsRubyBaseContainerFrame.h"
 #include "nsContentUtils.h"
 #include "nsLineLayout.h"
 #include "nsPresContext.h"
 #include "nsStyleContext.h"
 #include "nsStyleStructInlines.h"
 #include "WritingModes.h"
+#include "RubyUtils.h"
 
 using namespace mozilla;
 
 //----------------------------------------------------------------------
 
 // Frame class boilerplate
 // =======================
 
@@ -371,49 +372,66 @@ nsRubyBaseContainerFrame::Reflow(nsPresC
   // reflowed, or be not reflowed at all.
   MOZ_ASSERT(NS_INLINE_IS_BREAK_BEFORE(aStatus) ||
              NS_FRAME_IS_COMPLETE(aStatus) || mSpanContainers.IsEmpty());
   if (!NS_INLINE_IS_BREAK_BEFORE(aStatus) &&
       NS_FRAME_IS_COMPLETE(aStatus) && !mSpanContainers.IsEmpty()) {
     // Reflow spans
     nscoord spanISize = ReflowSpans(aPresContext, aReflowState,
                                     spanReflowStates);
-    if (isize < spanISize) {
-      nscoord delta = spanISize - isize;
-      if (allowLineBreak && ShouldBreakBefore(aReflowState, delta)) {
-        aStatus = NS_INLINE_LINE_BREAK_BEFORE();
-      } else {
-        aReflowState.mLineLayout->AdvanceICoord(delta);
-        isize = spanISize;
-      }
+    nscoord deltaISize = spanISize - isize;
+    if (deltaISize <= 0) {
+      RubyUtils::ClearReservedISize(this);
+    } else if (allowLineBreak && ShouldBreakBefore(aReflowState, deltaISize)) {
+      aStatus = NS_INLINE_LINE_BREAK_BEFORE();
+    } else {
+      RubyUtils::SetReservedISize(this, deltaISize);
+      aReflowState.mLineLayout->AdvanceICoord(deltaISize);
+      isize = spanISize;
     }
+  }
     // When there are spans, ReflowPairs and ReflowOnePair won't
     // record any optional break position. We have to record one
     // at the end of this segment.
     if (!NS_INLINE_IS_BREAK(aStatus) && allowLineBreak &&
         aReflowState.mLineLayout->NotifyOptionalBreakPosition(
           this, INT32_MAX, startEdge + isize <= aReflowState.AvailableISize(),
           gfxBreakPriority::eNormalBreak)) {
       aStatus = NS_INLINE_LINE_BREAK_AFTER(aStatus);
     }
-  }
 
   DebugOnly<nscoord> lineSpanSize = aReflowState.mLineLayout->EndSpan(this);
   // When there are no frames inside the ruby base container, EndSpan
   // will return 0. However, in this case, the actual width of the
   // container could be non-zero because of non-empty ruby annotations.
   MOZ_ASSERT(NS_INLINE_IS_BREAK_BEFORE(aStatus) ||
              isize == lineSpanSize || mFrames.IsEmpty());
   for (uint32_t i = 0; i < totalCount; i++) {
     // It happens before the ruby text container is reflowed, and that
     // when it is reflowed, it will just use this size.
     nsRubyTextContainerFrame* textContainer = i < rtcCount ?
       mTextContainers[i] : mSpanContainers[i - rtcCount];
-    textContainer->SetISize(isize);
-    lineLayouts[i]->EndLineReflow();
+    nsLineLayout* lineLayout = lineLayouts[i].get();
+
+    RubyUtils::ClearReservedISize(textContainer);
+    nscoord rtcISize = lineLayout->GetCurrentICoord();
+    // Only span containers and containers with collapsed annotations
+    // need reserving isize. For normal ruby text containers, their
+    // children will be expanded properly. We only need to expand their
+    // own size.
+    if (i < rtcCount) {
+      rtcISize = isize;
+    } else if (isize > rtcISize) {
+      RubyUtils::SetReservedISize(textContainer, isize - rtcISize);
+    }
+
+    lineLayout->VerticalAlignLine();
+    LogicalSize lineSize(lineWM, isize, lineLayout->GetFinalLineBSize());
+    textContainer->SetLineSize(lineSize);
+    lineLayout->EndLineReflow();
   }
 
   aDesiredSize.ISize(lineWM) = isize;
   nsLayoutUtils::SetBSizeFromFontMetrics(this, aDesiredSize, aReflowState,
                                          borderPadding, lineWM, frameWM);
 }
 
 /**
@@ -563,16 +581,17 @@ nsRubyBaseContainerFrame::ReflowOnePair(
       if (annotationText.Equals(baseText)) {
         textFrame->AddStateBits(NS_RUBY_TEXT_FRAME_AUTOHIDE);
       } else {
         textFrame->RemoveStateBits(NS_RUBY_TEXT_FRAME_AUTOHIDE);
       }
 
       nsReflowStatus reflowStatus;
       nsHTMLReflowMetrics metrics(*aReflowStates[i]);
+      RubyUtils::ClearReservedISize(textFrame);
 
       bool pushedFrame;
       aReflowStates[i]->mLineLayout->ReflowFrame(textFrame, reflowStatus,
                                                  &metrics, pushedFrame);
       MOZ_ASSERT(!NS_INLINE_IS_BREAK(reflowStatus) && !pushedFrame,
                  "Any line break inside ruby box should has been suppressed");
       pairISize = std::max(pairISize, metrics.ISize(lineWM));
     }
@@ -585,33 +604,46 @@ nsRubyBaseContainerFrame::ReflowOnePair(
     return 0;
   }
 
   // Reflow the base frame
   if (aBaseFrame) {
     MOZ_ASSERT(aBaseFrame->GetType() == nsGkAtoms::rubyBaseFrame);
     nsReflowStatus reflowStatus;
     nsHTMLReflowMetrics metrics(aReflowState);
+    RubyUtils::ClearReservedISize(aBaseFrame);
 
     bool pushedFrame;
     aReflowState.mLineLayout->ReflowFrame(aBaseFrame, reflowStatus,
                                           &metrics, pushedFrame);
     MOZ_ASSERT(!NS_INLINE_IS_BREAK(reflowStatus) && !pushedFrame,
                "Any line break inside ruby box should has been suppressed");
     pairISize = std::max(pairISize, metrics.ISize(lineWM));
   }
 
   // Align all the line layout to the new coordinate.
   nscoord icoord = istart + pairISize;
-  aReflowState.mLineLayout->AdvanceICoord(
-    icoord - aReflowState.mLineLayout->GetCurrentICoord());
+  nscoord deltaISize = icoord - aReflowState.mLineLayout->GetCurrentICoord();
+  if (deltaISize > 0) {
+    aReflowState.mLineLayout->AdvanceICoord(deltaISize);
+    if (aBaseFrame) {
+      RubyUtils::SetReservedISize(aBaseFrame, deltaISize);
+    }
+  }
   for (uint32_t i = 0; i < rtcCount; i++) {
     nsLineLayout* lineLayout = aReflowStates[i]->mLineLayout;
-    lineLayout->AdvanceICoord(icoord - lineLayout->GetCurrentICoord());
-    if (aBaseFrame && aTextFrames[i]) {
+    nsIFrame* textFrame = aTextFrames[i];
+    nscoord deltaISize = icoord - lineLayout->GetCurrentICoord();
+    if (deltaISize > 0) {
+      lineLayout->AdvanceICoord(deltaISize);
+      if (textFrame) {
+        RubyUtils::SetReservedISize(textFrame, deltaISize);
+      }
+    }
+    if (aBaseFrame && textFrame) {
       lineLayout->AttachLastFrameToBaseLineLayout();
     }
   }
 
   mPairCount++;
   if (aAllowLineBreak &&
       aReflowState.mLineLayout->NotifyOptionalBreakPosition(
         this, mPairCount, icoord <= aReflowState.AvailableISize(),
--- a/layout/generic/nsRubyFrame.cpp
+++ b/layout/generic/nsRubyFrame.cpp
@@ -266,16 +266,33 @@ nsRubyFrame::Reflow(nsPresContext* aPres
   // We never handle overflow in ruby.
   MOZ_ASSERT(!NS_FRAME_OVERFLOW_IS_INCOMPLETE(aStatus));
 
   aDesiredSize.ISize(lineWM) = aReflowState.mLineLayout->EndSpan(this);
   nsLayoutUtils::SetBSizeFromFontMetrics(this, aDesiredSize, aReflowState,
                                          borderPadding, lineWM, frameWM);
 }
 
+#ifdef DEBUG
+static void
+SanityCheckRubyPosition(int8_t aRubyPosition)
+{
+  uint8_t horizontalPosition = aRubyPosition &
+    (NS_STYLE_RUBY_POSITION_LEFT | NS_STYLE_RUBY_POSITION_RIGHT);
+  MOZ_ASSERT(horizontalPosition == NS_STYLE_RUBY_POSITION_LEFT ||
+             horizontalPosition == NS_STYLE_RUBY_POSITION_RIGHT);
+  uint8_t verticalPosition = aRubyPosition &
+    (NS_STYLE_RUBY_POSITION_OVER | NS_STYLE_RUBY_POSITION_UNDER |
+     NS_STYLE_RUBY_POSITION_INTER_CHARACTER);
+  MOZ_ASSERT(verticalPosition == NS_STYLE_RUBY_POSITION_OVER ||
+             verticalPosition == NS_STYLE_RUBY_POSITION_UNDER ||
+             verticalPosition == NS_STYLE_RUBY_POSITION_INTER_CHARACTER);
+}
+#endif
+
 void
 nsRubyFrame::ReflowSegment(nsPresContext* aPresContext,
                            const nsHTMLReflowState& aReflowState,
                            nsRubyBaseContainerFrame* aBaseContainer,
                            nsReflowStatus& aStatus)
 {
   AutoSetTextContainers holder(aBaseContainer);
   WritingMode lineWM = aReflowState.mLineLayout->GetWritingMode();
@@ -351,16 +368,26 @@ nsRubyFrame::ReflowSegment(nsPresContext
       nsIFrame* nextRTC = textContainers[i]->GetNextInFlow();
       if (nextRTC) {
         nextRTC->GetParent()->DeleteNextInFlowChild(nextRTC, true);
       }
     }
   }
 
   nsRect baseRect = aBaseContainer->GetRect();
+  // We need to position our rtc frames on one side or the other of the
+  // base container's rect, using a coordinate space that's relative to
+  // the ruby frame. Right now, the base container's rect's block-axis
+  // position is relative to the block container frame containing the
+  // lines, so we use 0 instead. (i.e. we assume that the base container
+  // is adjacent to the ruby frame's block-start edge.)
+  // XXX We may need to add border/padding here. See bug 1055667.
+  (lineWM.IsVertical() ? baseRect.x : baseRect.y) = 0;
+  // The rect for offsets of text containers.
+  nsRect offsetRect = baseRect;
   for (uint32_t i = 0; i < rtcCount; i++) {
     nsRubyTextContainerFrame* textContainer = textContainers[i];
     nsReflowStatus textReflowStatus;
     nsHTMLReflowMetrics textMetrics(aReflowState);
     nsHTMLReflowState textReflowState(aPresContext, aReflowState,
                                       textContainer, availSize);
     // FIXME We probably shouldn't be using the same nsLineLayout for
     //       the text containers. But it should be fine now as we are
@@ -371,24 +398,47 @@ nsRubyFrame::ReflowSegment(nsPresContext
                           textReflowState, textReflowStatus);
     // Ruby text containers always return NS_FRAME_COMPLETE even when
     // they have continuations, because the breaking has already been
     // handled when reflowing the base containers.
     NS_ASSERTION(textReflowStatus == NS_FRAME_COMPLETE,
                  "Ruby text container must not break itself inside");
     textContainer->SetSize(LogicalSize(lineWM, textMetrics.ISize(lineWM),
                                        textMetrics.BSize(lineWM)));
+
     nscoord x, y;
     nscoord bsize = textMetrics.BSize(lineWM);
+    uint8_t rubyPosition = textContainer->StyleText()->mRubyPosition;
+#ifdef DEBUG
+    SanityCheckRubyPosition(rubyPosition);
+#endif
     if (lineWM.IsVertical()) {
-      x = lineWM.IsVerticalLR() ? -bsize : baseRect.XMost();
-      y = baseRect.Y();
+      // writing-mode is vertical, so bsize is the annotation's *width*
+      if (rubyPosition & NS_STYLE_RUBY_POSITION_LEFT) {
+        x = offsetRect.X() - bsize;
+        offsetRect.SetLeftEdge(x);
+      } else {
+        x = offsetRect.XMost();
+        offsetRect.SetRightEdge(x + bsize);
+      }
+      y = offsetRect.Y();
     } else {
-      x = baseRect.X();
-      y = -bsize;
+      // writing-mode is horizontal, so bsize is the annotation's *height*
+      x = offsetRect.X();
+      if (rubyPosition & NS_STYLE_RUBY_POSITION_OVER) {
+        y = offsetRect.Y() - bsize;
+        offsetRect.SetTopEdge(y);
+      } else if (rubyPosition & NS_STYLE_RUBY_POSITION_UNDER) {
+        y = offsetRect.YMost();
+        offsetRect.SetBottomEdge(y + bsize);
+      } else {
+        // XXX inter-character support in bug 1055672
+        MOZ_ASSERT_UNREACHABLE("Unsupported ruby-position");
+        y = offsetRect.Y();
+      }
     }
     FinishReflowChild(textContainer, aPresContext, textMetrics,
                       &textReflowState, x, y, 0);
   }
 }
 
 nsRubyBaseContainerFrame*
 nsRubyFrame::PullOneSegment(ContinuationTraversingState& aState)
--- a/layout/generic/nsRubyTextContainerFrame.cpp
+++ b/layout/generic/nsRubyTextContainerFrame.cpp
@@ -66,25 +66,19 @@ nsRubyTextContainerFrame::Reflow(nsPresC
                                  nsHTMLReflowMetrics& aDesiredSize,
                                  const nsHTMLReflowState& aReflowState,
                                  nsReflowStatus& aStatus)
 {
   DO_GLOBAL_REFLOW_COUNT("nsRubyTextContainerFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
 
   // All rt children have already been reflowed. All we need to do is
-  // to report complete and return the desired size.
+  // to report complete and return the desired size provided by the
+  // ruby base container.
 
   // Although a ruby text container may have continuations, returning
   // NS_FRAME_COMPLETE here is still safe, since its parent, ruby frame,
   // ignores the status, and continuations of the ruby base container
   // will take care of our continuations.
   aStatus = NS_FRAME_COMPLETE;
   WritingMode lineWM = aReflowState.mLineLayout->GetWritingMode();
-  WritingMode frameWM = aReflowState.GetWritingMode();
-  LogicalMargin borderPadding = aReflowState.ComputedLogicalBorderPadding();
-
-  // ISize is provided by the ruby base container
-  // during reflow of that container.
-  aDesiredSize.ISize(lineWM) = mISize;
-  nsLayoutUtils::SetBSizeFromFontMetrics(this, aDesiredSize, aReflowState,
-                                         borderPadding, lineWM, frameWM);
+  aDesiredSize.SetSize(lineWM, mLineSize);
 }
--- a/layout/generic/nsRubyTextContainerFrame.h
+++ b/layout/generic/nsRubyTextContainerFrame.h
@@ -42,20 +42,21 @@ public:
   virtual nsresult GetFrameName(nsAString& aResult) const MOZ_OVERRIDE;
 #endif
 
 protected:
   friend nsContainerFrame*
     NS_NewRubyTextContainerFrame(nsIPresShell* aPresShell,
                                  nsStyleContext* aContext);
   explicit nsRubyTextContainerFrame(nsStyleContext* aContext)
-    : nsRubyTextContainerFrameSuper(aContext) {}
+    : nsRubyTextContainerFrameSuper(aContext)
+    , mLineSize(mozilla::WritingMode(aContext)) {}
 
   friend class nsRubyBaseContainerFrame;
-  void SetISize(nscoord aISize) { mISize = aISize; }
+  void SetLineSize(const mozilla::LogicalSize& aSize) { mLineSize = aSize; }
 
-  // The intended dimensions of the ruby text container. These are modified
-  // whenever a ruby text box is reflowed and used when the ruby text container
-  // is reflowed.
-  nscoord mISize;
+  // The intended dimensions of the ruby text container. It is set by
+  // the corresponding ruby base container when the segment is reflowed,
+  // and used when the ruby text container is reflowed by its parent.
+  mozilla::LogicalSize mLineSize;
 };
 
 #endif /* nsRubyTextContainerFrame_h___ */
--- a/layout/generic/nsRubyTextFrame.cpp
+++ b/layout/generic/nsRubyTextFrame.cpp
@@ -85,12 +85,15 @@ nsRubyTextFrame::Reflow(nsPresContext* a
   // propagated to the ancestors, then it won't be displayed even if
   // the content is no longer the same, until next reflow triggered by
   // some other change. In general, we always reflow all the frames we
   // created. There might be other problems if we don't do that.
   nsRubyTextFrameSuper::Reflow(aPresContext, aDesiredSize,
                                aReflowState, aStatus);
 
   if (GetStateBits() & NS_RUBY_TEXT_FRAME_AUTOHIDE) {
-    aDesiredSize.ClearSize();
+    // Reset the ISize. The BSize is not changed so that it won't
+    // affect vertical positioning in unexpected way.
+    WritingMode lineWM = aReflowState.mLineLayout->GetWritingMode();
+    aDesiredSize.ISize(lineWM) = 0;
     aDesiredSize.SetOverflowAreasToDesiredBounds();
   }
 }
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -1856,20 +1856,20 @@ BuildTextRunsScanner::BuildTextRunForFra
   }
   userData->mMappedFlowCount = mMappedFlows.Length();
   userData->mLastFlowIndex = 0;
 
   uint32_t currentTransformedTextOffset = 0;
 
   uint32_t nextBreakIndex = 0;
   nsTextFrame* nextBreakBeforeFrame = GetNextBreakBeforeFrame(&nextBreakIndex);
+  bool isSVG = mLineContainer->IsSVGText();
   bool enabledJustification = mLineContainer &&
     (mLineContainer->StyleText()->mTextAlign == NS_STYLE_TEXT_ALIGN_JUSTIFY ||
-     mLineContainer->StyleText()->mTextAlignLast == NS_STYLE_TEXT_ALIGN_JUSTIFY) &&
-    !mLineContainer->IsSVGText();
+     mLineContainer->StyleText()->mTextAlignLast == NS_STYLE_TEXT_ALIGN_JUSTIFY);
 
   // for word-break style
   switch (mLineContainer->StyleText()->mWordBreak) {
     case NS_STYLE_WORDBREAK_BREAK_ALL:
       mLineBreaker.SetWordBreak(nsILineBreaker::kWordBreak_BreakAll);
       break;
     case NS_STYLE_WORDBREAK_KEEP_ALL:
       mLineBreaker.SetWordBreak(nsILineBreaker::kWordBreak_KeepAll);
@@ -1891,17 +1891,18 @@ BuildTextRunsScanner::BuildTextRunForFra
     textStyle = f->StyleText();
     if (NS_STYLE_TEXT_TRANSFORM_NONE != textStyle->mTextTransform) {
       anyTextTransformStyle = true;
     }
     textFlags |= GetSpacingFlags(LetterSpacing(f));
     textFlags |= GetSpacingFlags(WordSpacing(f));
     nsTextFrameUtils::CompressionMode compression =
       CSSWhitespaceToCompressionMode[textStyle->mWhiteSpace];
-    if (enabledJustification && !textStyle->WhiteSpaceIsSignificant()) {
+    if ((enabledJustification || f->StyleContext()->IsDirectlyInsideRuby()) &&
+        !textStyle->WhiteSpaceIsSignificant() && !isSVG) {
       textFlags |= gfxTextRunFactory::TEXT_ENABLE_SPACING;
     }
     fontStyle = f->StyleFont();
     nsIFrame* parent = mLineContainer->GetParent();
     if (NS_MATHML_MATHVARIANT_NONE != fontStyle->mMathVariant) {
       if (NS_MATHML_MATHVARIANT_NORMAL != fontStyle->mMathVariant) {
         anyMathMLStyling = true;
       }
@@ -2755,16 +2756,22 @@ static int32_t FindChar(const nsTextFrag
         return (static_cast<const char*>(p) - str) + aOffset;
     }
   }
   return -1;
 }
 
 static bool IsChineseOrJapanese(nsIFrame* aFrame)
 {
+  if (aFrame->StyleContext()->IsDirectlyInsideRuby()) {
+    // Always treat ruby as CJ language so that those characters can
+    // be expanded properly even when surrounded by other language.
+    return true;
+  }
+
   nsIAtom* language = aFrame->StyleFont()->mLanguage;
   if (!language) {
     return false;
   }
   const char16_t *lang = language->GetUTF16String();
   return (!nsCRT::strncmp(lang, MOZ_UTF16("ja"), 2) ||
           !nsCRT::strncmp(lang, MOZ_UTF16("zh"), 2)) &&
          (language->GetLength() == 2 || lang[2] == '-');
@@ -8497,17 +8504,18 @@ nsTextFrame::ReflowText(nsLineLayout& aL
     }
   } else if (cachedNewlineOffset) {
     mContent->DeleteProperty(nsGkAtoms::newline);
   }
 
   // Compute space and letter counts for justification, if required
   if (!textStyle->WhiteSpaceIsSignificant() &&
       (lineContainer->StyleText()->mTextAlign == NS_STYLE_TEXT_ALIGN_JUSTIFY ||
-       lineContainer->StyleText()->mTextAlignLast == NS_STYLE_TEXT_ALIGN_JUSTIFY) &&
+       lineContainer->StyleText()->mTextAlignLast == NS_STYLE_TEXT_ALIGN_JUSTIFY ||
+       StyleContext()->IsDirectlyInsideRuby()) &&
       !lineContainer->IsSVGText()) {
     AddStateBits(TEXT_JUSTIFICATION_ENABLED);
     provider.ComputeJustification(offset, charsFit);
     aLineLayout.SetJustificationInfo(provider.GetJustificationInfo());
   }
 
   SetLength(contentLength, &aLineLayout, ALLOW_FRAME_CREATION_AND_DESTRUCTION);
 
--- a/layout/reftests/css-ruby/reftest.list
+++ b/layout/reftests/css-ruby/reftest.list
@@ -1,13 +1,13 @@
 default-preferences pref(layout.css.ruby.enabled,true)
 
 == autohiding-1.html autohiding-1-ref.html
 == autohiding-2.html autohiding-2-ref.html
-== autohiding-3.html autohiding-3-ref.html
+fails == autohiding-3.html autohiding-3-ref.html # bug 1107701
 == box-generation-1.html box-generation-1-ref.html
 == box-generation-2.html box-generation-2-ref.html
 == box-generation-3.html box-generation-3-ref.html
 == box-generation-4.html box-generation-4-ref.html
 == box-generation-5.html box-generation-5-ref.html
 == dynamic-insertion-1.html dynamic-insertion-1-ref.html
 == dynamic-insertion-2.html dynamic-insertion-2-ref.html
 == dynamic-insertion-3.html dynamic-insertion-3-ref.html
@@ -17,10 +17,13 @@ fuzzy-if(winWidget,28,1) == dynamic-remo
 == float-handling.html float-handling-ref.html
 == inlinize-blocks-1.html inlinize-blocks-1-ref.html
 == inlinize-blocks-2.html inlinize-blocks-2-ref.html
 == inlinize-blocks-3.html inlinize-blocks-3-ref.html
 == inlinize-blocks-4.html inlinize-blocks-4-ref.html
 == inlinize-blocks-5.html inlinize-blocks-5-ref.html
 == ruby-whitespace-1.html ruby-whitespace-1-ref.html
 == ruby-whitespace-2.html ruby-whitespace-2-ref.html
+== ruby-position-horizontal.html ruby-position-horizontal-ref.html
+pref(layout.css.vertical-text.enabled,true) fails == ruby-position-vertical-lr.html ruby-position-vertical-lr-ref.html # bug 1112474
+pref(layout.css.vertical-text.enabled,true) fails == ruby-position-vertical-rl.html ruby-position-vertical-rl-ref.html # bug 1112474
 != ruby-reflow-1-opaqueruby.html ruby-reflow-1-noruby.html
 == ruby-reflow-1-transparentruby.html ruby-reflow-1-noruby.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-ruby/ruby-position-horizontal-ref.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <title>Bug 1055665 - Test for ruby-position</title>
+  <link rel="stylesheet" href="common.css">
+  <style>
+    body {
+      font-family: monospace;
+      line-height: normal;
+    }
+    .annotation, .annotation > div {
+      position: absolute;
+    }
+  </style>
+</head>
+<body>
+  <div style="height: 8em; line-height: 8em;">
+    <div style="display: inline-block; line-height: normal;">
+      <div class="annotation">
+        <div style="top: -100%;">over##</div>
+        <div style="top: 100%;">under#</div>
+        <div style="top: -200%;">over2#</div>
+        <div style="top: 200%;">under2</div>
+        &nbsp; <!-- to give container a nonzero size for
+                    percent values to resolve against -->
+      </div>
+      base##
+    </div>
+  </div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-ruby/ruby-position-horizontal.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <title>Bug 1055665 - Test for ruby-position</title>
+  <link rel="stylesheet" href="common.css">
+  <style>
+    body {
+      font-family: monospace;
+      /* use a large line-height here to avoid additional leadings */
+      line-height: 8em;
+    }
+    rtc, rt {
+      font-size: 100% !important;
+      line-height: normal !important;
+    }
+  </style>
+</head>
+<body>
+  <ruby>
+    <rb>base##</rb>
+    <rtc style="ruby-position: over left"><rt>over##</rt></rtc>
+    <rtc style="ruby-position: under left"><rt>under#</rt></rtc>
+    <rtc style="ruby-position: over left"><rt>over2#</rt></rtc>
+    <rtc style="ruby-position: under left"><rt>under2</rt></rtc>
+  </ruby>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-ruby/ruby-position-vertical-lr-ref.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <title>Bug 1055665 - Test for ruby-position</title>
+  <link rel="stylesheet" href="common.css">
+  <style>
+    body {
+      font-family: monospace;
+      line-height: normal;
+      writing-mode: vertical-lr;
+    }
+    .annotation, .annotation > div {
+      position: absolute;
+    }
+  </style>
+</head>
+<body>
+  <div style="width: 8em; line-height: 8em;">
+    <div style="display: inline-block; line-height: normal;">
+      <div class="annotation">
+        <div style="right: 100%;">left##</div>
+        <div style="right: -100%;">right#</div>
+        <div style="right: 200%;">left2#</div>
+        <div style="right: -200%;">right2</div>
+        &nbsp; <!-- to give container a nonzero size for
+                    percent values to resolve against -->
+      </div>
+      base##
+    </div>
+  </div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-ruby/ruby-position-vertical-lr.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <title>Bug 1055665 - Test for ruby-position</title>
+  <link rel="stylesheet" href="common.css">
+  <style>
+    body {
+      font-family: monospace;
+      /* use a large line-height here to avoid additional leadings */
+      line-height: 8em;
+      writing-mode: vertical-lr;
+    }
+    rtc, rt {
+      font-size: 100% !important;
+      line-height: normal !important;
+    }
+  </style>
+</head>
+<body>
+  <ruby>
+    <rb>base##</rb>
+    <rtc style="ruby-position: over left"><rt>left##</rt></rtc>
+    <rtc style="ruby-position: over right"><rt>right#</rt></rtc>
+    <rtc style="ruby-position: over left"><rt>left2#</rt></rtc>
+    <rtc style="ruby-position: over right"><rt>right2</rt></rtc>
+  </ruby>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-ruby/ruby-position-vertical-rl-ref.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <title>Bug 1055665 - Test for ruby-position</title>
+  <link rel="stylesheet" href="common.css">
+  <style>
+    body {
+      font-family: monospace;
+      line-height: normal;
+      writing-mode: vertical-rl;
+    }
+    .annotation, .annotation > div {
+      position: absolute;
+    }
+  </style>
+</head>
+<body>
+  <div style="width: 8em; line-height: 8em;">
+    <div style="display: inline-block; line-height: normal;">
+      <div class="annotation">
+        <div style="right: 100%;">left##</div>
+        <div style="right: -100%;">right#</div>
+        <div style="right: 200%;">left2#</div>
+        <div style="right: -200%;">right2</div>
+        &nbsp; <!-- to give container a nonzero size for
+                    percent values to resolve against -->
+      </div>
+      base##
+    </div>
+  </div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-ruby/ruby-position-vertical-rl.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <title>Bug 1055665 - Test for ruby-position</title>
+  <link rel="stylesheet" href="common.css">
+  <style>
+    body {
+      font-family: monospace;
+      /* use a large line-height here to avoid additional leadings */
+      line-height: 8em;
+      writing-mode: vertical-rl;
+    }
+    rtc, rt {
+      font-size: 100% !important;
+      line-height: normal !important;
+    }
+  </style>
+</head>
+<body>
+  <ruby>
+    <rb>base##</rb>
+    <rtc style="ruby-position: over left"><rt>left##</rt></rtc>
+    <rtc style="ruby-position: over right"><rt>right#</rt></rtc>
+    <rtc style="ruby-position: over left"><rt>left2#</rt></rtc>
+    <rtc style="ruby-position: over right"><rt>right2</rt></rtc>
+  </ruby>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/floats/1114329-ref.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <style type="text/css">
+    p {  border: 1px solid black; }
+        .layout-main-page-content { width: 75%; float: right; text-align:right; }
+        .layout-right-column { width: 25%; float: left; }
+    </style>
+  </head>
+  <body>
+    <div>
+      <div class="layout-main-page-content">
+        <img src="foo">
+        <p dir="rtl" id="p_10">0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789</p>
+        <p dir="rtl" id="p_20">0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789</p>
+      </div>
+    </div>
+    <div class="layout-right-column" dir="rtl">
+      hoge hoge hoge hoge hoge hoge hoge hoge hoge hoge
+      <img id="image" style="height: 280px; overflow: hidden;" src="foo" width="100%">
+    </div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/floats/1114329.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <style type="text/css">
+    p {  border: 1px solid black; }
+        .layout-main-page-content { width: 75%; float: right;}
+        .layout-right-column { width: 25%; float: left; }
+    </style>
+    <script type="text/javascript">
+      function do_test() {
+        document.getElementById("image").removeAttribute("hidden");
+        document.documentElement.removeAttribute("class");
+      };
+    </script>
+  </head>
+  <body dir="rtl" onload="do_test()">
+    <div>
+      <div class="layout-main-page-content">
+        <img src="foo">
+        <p dir="rtl" id="p_10">0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789</p>
+        <p dir="rtl" id="p_20">0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789</p>
+      </div>
+    </div>
+    <div class="layout-right-column">
+      hoge hoge hoge hoge hoge hoge hoge hoge hoge hoge
+      <img id="image" hidden="true" style="height: 280px; overflow: hidden;" src="foo" width="100%">
+    </div>
+  </body>
+</html>
--- a/layout/reftests/floats/reftest.list
+++ b/layout/reftests/floats/reftest.list
@@ -13,8 +13,9 @@ fails == zero-height-float.html zero-hei
 fails == 345369-1.html 345369-1-ref.html
 fails == 345369-2.html 345369-2-ref.html
 == 345369-3.html 345369-3-ref.html
 == 345369-4.html 345369-4-ref.html
 == 345369-5.html 345369-5-ref.html
 == 429974-1.html 429974-1-ref.html
 == 546048-1.html 546048-1-ref.html
 == 775350-1.html 775350-1-ref.html
+== 1114329.html 1114329-ref.html
--- a/layout/style/nsCSSKeywordList.h
+++ b/layout/style/nsCSSKeywordList.h
@@ -305,16 +305,17 @@ CSS_KEY(initial, initial)
 CSS_KEY(inline, inline)
 CSS_KEY(inline-axis, inline_axis)
 CSS_KEY(inline-block, inline_block)
 CSS_KEY(inline-flex, inline_flex)
 CSS_KEY(inline-grid, inline_grid)
 CSS_KEY(inline-table, inline_table)
 CSS_KEY(inset, inset)
 CSS_KEY(inside, inside)
+// CSS_KEY(inter-character, inter_character) // TODO see bug 1055672
 CSS_KEY(interpolatematrix, interpolatematrix)
 CSS_KEY(isolate, isolate)
 CSS_KEY(invert, invert)
 CSS_KEY(italic, italic)
 CSS_KEY(japanese-formal, japanese_formal)
 CSS_KEY(japanese-informal, japanese_informal)
 CSS_KEY(jis78, jis78)
 CSS_KEY(jis83, jis83)
@@ -388,16 +389,17 @@ CSS_KEY(nwse-resize, nwse_resize)
 CSS_KEY(oblique, oblique)
 CSS_KEY(oldstyle-nums, oldstyle_nums)
 CSS_KEY(opacity, opacity)
 CSS_KEY(open-quote, open_quote)
 CSS_KEY(ordinal, ordinal)
 CSS_KEY(ornaments, ornaments)
 CSS_KEY(outset, outset)
 CSS_KEY(outside, outside)
+CSS_KEY(over, over)
 CSS_KEY(overlay, overlay)
 CSS_KEY(overline, overline)
 CSS_KEY(padding-box, padding_box)
 CSS_KEY(painted, painted)
 CSS_KEY(pan-x, pan_x)
 CSS_KEY(pan-y, pan_y)
 CSS_KEY(paused, paused)
 CSS_KEY(pc, pc)
@@ -548,16 +550,17 @@ CSS_KEY(translate3d, translate3d)
 CSS_KEY(translatex, translatex)
 CSS_KEY(translatey, translatey)
 CSS_KEY(translatez, translatez)
 CSS_KEY(transparent, transparent) // for nsComputedDOMStyle only
 CSS_KEY(tri-state, tri_state)
 CSS_KEY(true, true)
 CSS_KEY(ultra-condensed, ultra_condensed)
 CSS_KEY(ultra-expanded, ultra_expanded)
+CSS_KEY(under, under)
 CSS_KEY(underline, underline)
 CSS_KEY(unicase, unicase)
 CSS_KEY(unset, unset)
 CSS_KEY(uppercase, uppercase)
 CSS_KEY(upright, upright)
 CSS_KEY(vertical, vertical)
 CSS_KEY(vertical-lr, vertical_lr)
 CSS_KEY(vertical-rl, vertical_rl)
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -790,16 +790,17 @@ protected:
   bool ParseMarks(nsCSSValue& aValue);
   bool ParseClipPath();
   bool ParseTransform(bool aIsPrefixed);
   bool ParseObjectPosition();
   bool ParseOutline();
   bool ParseOverflow();
   bool ParsePadding();
   bool ParseQuotes();
+  bool ParseRubyPosition(nsCSSValue& aValue);
   bool ParseSize();
   bool ParseTextAlign(nsCSSValue& aValue,
                       const KTableValue aTable[]);
   bool ParseTextAlign(nsCSSValue& aValue);
   bool ParseTextAlignLast(nsCSSValue& aValue);
   bool ParseTextDecoration();
   bool ParseTextDecorationLine(nsCSSValue& aValue);
   bool ParseTextCombineUpright(nsCSSValue& aValue);
@@ -10004,16 +10005,18 @@ CSSParserImpl::ParseSingleValueProperty(
       case eCSSProperty_font_weight:
         return ParseFontWeight(aValue);
       case eCSSProperty_image_orientation:
         return ParseImageOrientation(aValue);
       case eCSSProperty_list_style_type:
         return ParseListStyleType(aValue);
       case eCSSProperty_marks:
         return ParseMarks(aValue);
+      case eCSSProperty_ruby_position:
+        return ParseRubyPosition(aValue);
       case eCSSProperty_text_align:
         return ParseTextAlign(aValue);
       case eCSSProperty_text_align_last:
         return ParseTextAlignLast(aValue);
       case eCSSProperty_text_decoration_line:
         return ParseTextDecorationLine(aValue);
       case eCSSProperty_text_combine_upright:
         return ParseTextCombineUpright(aValue);
@@ -13120,16 +13123,44 @@ CSSParserImpl::ParseQuotes()
       quotes->mNext = new nsCSSValuePairList;
       quotes = quotes->mNext;
     }
   }
   AppendValue(eCSSProperty_quotes, value);
   return true;
 }
 
+static const int32_t gRubyPositionMask[] = {
+  // vertical values
+  NS_STYLE_RUBY_POSITION_OVER |
+    NS_STYLE_RUBY_POSITION_UNDER |
+    NS_STYLE_RUBY_POSITION_INTER_CHARACTER,
+  // horizontal values
+  NS_STYLE_RUBY_POSITION_RIGHT |
+    NS_STYLE_RUBY_POSITION_LEFT,
+  // end
+  MASK_END_VALUE
+};
+
+bool
+CSSParserImpl::ParseRubyPosition(nsCSSValue& aValue)
+{
+  if (ParseVariant(aValue, VARIANT_INHERIT, nullptr)) {
+    return true;
+  }
+  if (!ParseBitmaskValues(aValue, nsCSSProps::kRubyPositionKTable,
+                          gRubyPositionMask)) {
+    return false;
+  }
+  auto value = aValue.GetIntValue();
+  // The specified value must include *both* a vertical keyword *and*
+  // a horizontal keyword. We reject it here if either is missing.
+  return (value & gRubyPositionMask[0]) && (value & gRubyPositionMask[1]);
+}
+
 bool
 CSSParserImpl::ParseSize()
 {
   nsCSSValue width, height;
   if (!ParseVariant(width, VARIANT_AHKL, nsCSSProps::kPageSizeKTable)) {
     return false;
   }
   if (width.IsLengthUnit()) {
--- a/layout/style/nsCSSPropList.h
+++ b/layout/style/nsCSSPropList.h
@@ -2971,16 +2971,27 @@ CSS_PROP_POSITION(
         CSS_PROPERTY_STORES_CALC |
         CSS_PROPERTY_UNITLESS_LENGTH_QUIRK |
         CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "",
     VARIANT_AHLP | VARIANT_CALC,
     nullptr,
     offsetof(nsStylePosition, mOffset),
     eStyleAnimType_Sides_Right)
+CSS_PROP_TEXT(
+    ruby-position,
+    ruby_position,
+    RubyPosition,
+    CSS_PROPERTY_PARSE_VALUE |
+        CSS_PROPERTY_VALUE_PARSER_FUNCTION,
+    "layout.css.ruby.enabled",
+    0,
+    kRubyPositionKTable,
+    CSS_PROP_NO_OFFSET,
+    eStyleAnimType_None)
 CSS_PROP_DISPLAY(
     scroll-behavior,
     scroll_behavior,
     ScrollBehavior,
     CSS_PROPERTY_PARSE_VALUE,
     "layout.css.scroll-behavior.property-enabled",
     VARIANT_HK,
     kScrollBehaviorKTable,
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -1620,16 +1620,26 @@ const KTableValue nsCSSProps::kRadialGra
 const KTableValue nsCSSProps::kResizeKTable[] = {
   eCSSKeyword_none,       NS_STYLE_RESIZE_NONE,
   eCSSKeyword_both,       NS_STYLE_RESIZE_BOTH,
   eCSSKeyword_horizontal, NS_STYLE_RESIZE_HORIZONTAL,
   eCSSKeyword_vertical,   NS_STYLE_RESIZE_VERTICAL,
   eCSSKeyword_UNKNOWN,-1
 };
 
+const KTableValue nsCSSProps::kRubyPositionKTable[] = {
+  eCSSKeyword_over, NS_STYLE_RUBY_POSITION_OVER,
+  eCSSKeyword_under, NS_STYLE_RUBY_POSITION_UNDER,
+  // bug 1055672 for 'inter-character' support
+  // eCSSKeyword_inter_character, NS_STYLE_RUBY_POSITION_INTER_CHARACTER,
+  eCSSKeyword_right, NS_STYLE_RUBY_POSITION_RIGHT,
+  eCSSKeyword_left, NS_STYLE_RUBY_POSITION_LEFT,
+  eCSSKeyword_UNKNOWN, -1
+};
+
 const KTableValue nsCSSProps::kScrollBehaviorKTable[] = {
   eCSSKeyword_auto,       NS_STYLE_SCROLL_BEHAVIOR_AUTO,
   eCSSKeyword_smooth,     NS_STYLE_SCROLL_BEHAVIOR_SMOOTH,
   eCSSKeyword_UNKNOWN,-1
 };
 
 const KTableValue nsCSSProps::kStackSizingKTable[] = {
   eCSSKeyword_ignore, NS_STYLE_STACK_SIZING_IGNORE,
--- a/layout/style/nsCSSProps.h
+++ b/layout/style/nsCSSProps.h
@@ -623,16 +623,17 @@ public:
   static const KTableValue kPointerEventsKTable[];
   // Not const because we modify its entries when the pref
   // "layout.css.sticky.enabled" changes:
   static KTableValue kPositionKTable[];
   static const KTableValue kRadialGradientShapeKTable[];
   static const KTableValue kRadialGradientSizeKTable[];
   static const KTableValue kRadialGradientLegacySizeKTable[];
   static const KTableValue kResizeKTable[];
+  static const KTableValue kRubyPositionKTable[];
   static const KTableValue kScrollBehaviorKTable[];
   static const KTableValue kSpeakKTable[];
   static const KTableValue kSpeakHeaderKTable[];
   static const KTableValue kSpeakNumeralKTable[];
   static const KTableValue kSpeakPunctuationKTable[];
   static const KTableValue kSpeechRateKTable[];
   static const KTableValue kStackSizingKTable[];
   static const KTableValue kTableLayoutKTable[];
--- a/layout/style/nsCSSValue.cpp
+++ b/layout/style/nsCSSValue.cpp
@@ -1285,16 +1285,23 @@ nsCSSValue::AppendToString(nsCSSProperty
       break;
 
     case eCSSProperty_clip_path:
       AppendASCIItoUTF16(nsCSSProps::ValueToKeyword(intValue,
                             nsCSSProps::kClipShapeSizingKTable),
                          aResult);
       break;
 
+    case eCSSProperty_ruby_position:
+      nsStyleUtil::AppendBitmaskCSSValue(aProperty, intValue,
+                                         NS_STYLE_RUBY_POSITION_OVER,
+                                         NS_STYLE_RUBY_POSITION_LEFT,
+                                         aResult);
+      break;
+
     default:
       const nsAFlatCString& name = nsCSSProps::LookupPropertyValue(aProperty, intValue);
       AppendASCIItoUTF16(name, aResult);
       break;
     }
   }
   else if (eCSSUnit_EnumColor == unit) {
     // we can lookup the property in the ColorTable and then
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -3072,16 +3072,30 @@ nsComputedDOMStyle::DoGetLineHeight()
     SetValueToCoord(val, StyleText()->mLineHeight, true,
                     nullptr, nsCSSProps::kLineHeightKTable);
   }
 
   return val;
 }
 
 CSSValue*
+nsComputedDOMStyle::DoGetRubyPosition()
+{
+  nsROCSSPrimitiveValue* val = new nsROCSSPrimitiveValue;
+  int32_t intValue = StyleText()->mRubyPosition;
+  nsAutoString valueStr;
+  nsStyleUtil::AppendBitmaskCSSValue(eCSSProperty_ruby_position,
+                                     intValue,
+                                     NS_STYLE_RUBY_POSITION_OVER,
+                                     NS_STYLE_RUBY_POSITION_LEFT, valueStr);
+  val->SetString(valueStr);
+  return val;
+}
+
+CSSValue*
 nsComputedDOMStyle::DoGetVerticalAlign()
 {
   nsROCSSPrimitiveValue *val = new nsROCSSPrimitiveValue;
   SetValueToCoord(val, StyleTextReset()->mVerticalAlign, false,
                   &nsComputedDOMStyle::GetLineHeightCoord,
                   nsCSSProps::kVerticalAlignKTable);
   return val;
 }
--- a/layout/style/nsComputedDOMStyle.h
+++ b/layout/style/nsComputedDOMStyle.h
@@ -369,16 +369,17 @@ private:
   /* List properties */
   mozilla::dom::CSSValue* DoGetListStyleImage();
   mozilla::dom::CSSValue* DoGetListStylePosition();
   mozilla::dom::CSSValue* DoGetListStyleType();
   mozilla::dom::CSSValue* DoGetImageRegion();
 
   /* Text Properties */
   mozilla::dom::CSSValue* DoGetLineHeight();
+  mozilla::dom::CSSValue* DoGetRubyPosition();
   mozilla::dom::CSSValue* DoGetTextAlign();
   mozilla::dom::CSSValue* DoGetTextAlignLast();
   mozilla::dom::CSSValue* DoGetTextCombineUpright();
   mozilla::dom::CSSValue* DoGetTextDecoration();
   mozilla::dom::CSSValue* DoGetTextDecorationColor();
   mozilla::dom::CSSValue* DoGetTextDecorationLine();
   mozilla::dom::CSSValue* DoGetTextDecorationStyle();
   mozilla::dom::CSSValue* DoGetTextIndent();
--- a/layout/style/nsComputedDOMStylePropertyList.h
+++ b/layout/style/nsComputedDOMStylePropertyList.h
@@ -192,16 +192,17 @@ COMPUTED_STYLE_PROP(page_break_before,  
 COMPUTED_STYLE_PROP(page_break_inside,             PageBreakInside)
 COMPUTED_STYLE_PROP(perspective,                   Perspective)
 COMPUTED_STYLE_PROP(perspective_origin,            PerspectiveOrigin)
 COMPUTED_STYLE_PROP(pointer_events,                PointerEvents)
 COMPUTED_STYLE_PROP(position,                      Position)
 COMPUTED_STYLE_PROP(quotes,                        Quotes)
 COMPUTED_STYLE_PROP(resize,                        Resize)
 COMPUTED_STYLE_PROP(right,                         Right)
+COMPUTED_STYLE_PROP(ruby_position,                 RubyPosition)
 COMPUTED_STYLE_PROP(scroll_behavior,               ScrollBehavior)
 //// COMPUTED_STYLE_PROP(size,                     Size)
 COMPUTED_STYLE_PROP(table_layout,                  TableLayout)
 COMPUTED_STYLE_PROP(text_align,                    TextAlign)
 COMPUTED_STYLE_PROP(text_combine_upright,          TextCombineUpright)
 COMPUTED_STYLE_PROP(text_decoration,               TextDecoration)
 COMPUTED_STYLE_PROP(text_decoration_color,         TextDecorationColor)
 COMPUTED_STYLE_PROP(text_decoration_line,          TextDecorationLine)
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -4363,16 +4363,23 @@ nsRuleNode::ComputeTextData(void* aStart
               NS_STYLE_WORDWRAP_NORMAL, 0, 0, 0, 0);
 
   // hyphens: enum, inherit, initial
   SetDiscrete(*aRuleData->ValueForHyphens(), text->mHyphens, canStoreInRuleTree,
               SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT,
               parentText->mHyphens,
               NS_STYLE_HYPHENS_MANUAL, 0, 0, 0, 0);
 
+  // ruby-position: enum, inherit, initial
+  SetDiscrete(*aRuleData->ValueForRubyPosition(),
+              text->mRubyPosition, canStoreInRuleTree,
+              SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT,
+              parentText->mRubyPosition,
+              NS_STYLE_RUBY_POSITION_INITIAL, 0, 0, 0, 0);
+
   // text-size-adjust: none, auto, inherit, initial
   SetDiscrete(*aRuleData->ValueForTextSizeAdjust(), text->mTextSizeAdjust,
               canStoreInRuleTree,
               SETDSC_NONE | SETDSC_AUTO | SETDSC_UNSET_INHERIT,
               parentText->mTextSizeAdjust,
               NS_STYLE_TEXT_SIZE_ADJUST_AUTO, // initial value
               NS_STYLE_TEXT_SIZE_ADJUST_AUTO, // auto value
               NS_STYLE_TEXT_SIZE_ADJUST_NONE, // none value
--- a/layout/style/nsStyleConsts.h
+++ b/layout/style/nsStyleConsts.h
@@ -838,16 +838,25 @@ static inline mozilla::css::Side operato
 #define NS_STYLE_WORDWRAP_NORMAL                0
 #define NS_STYLE_WORDWRAP_BREAK_WORD            1
 
 // See nsStyleText
 #define NS_STYLE_HYPHENS_NONE                   0
 #define NS_STYLE_HYPHENS_MANUAL                 1
 #define NS_STYLE_HYPHENS_AUTO                   2
 
+// ruby-position, see nsStyleText
+#define NS_STYLE_RUBY_POSITION_OVER             0x01
+#define NS_STYLE_RUBY_POSITION_UNDER            0x02
+#define NS_STYLE_RUBY_POSITION_INTER_CHARACTER  0x04 // placeholder, not yet parsed
+#define NS_STYLE_RUBY_POSITION_RIGHT            0x08
+#define NS_STYLE_RUBY_POSITION_LEFT             0x10
+#define NS_STYLE_RUBY_POSITION_INITIAL \
+  (NS_STYLE_RUBY_POSITION_OVER | NS_STYLE_RUBY_POSITION_RIGHT)
+
 // See nsStyleText
 #define NS_STYLE_TEXT_SIZE_ADJUST_NONE          0
 #define NS_STYLE_TEXT_SIZE_ADJUST_AUTO          1
 
 // See nsStyleText
 #define NS_STYLE_TEXT_ORIENTATION_MIXED          0
 #define NS_STYLE_TEXT_ORIENTATION_UPRIGHT        1
 #define NS_STYLE_TEXT_ORIENTATION_SIDEWAYS_RIGHT 2
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -3339,16 +3339,17 @@ nsStyleText::nsStyleText(void)
   mTextAlignLast = NS_STYLE_TEXT_ALIGN_AUTO;
   mTextAlignTrue = false;
   mTextAlignLastTrue = false;
   mTextTransform = NS_STYLE_TEXT_TRANSFORM_NONE;
   mWhiteSpace = NS_STYLE_WHITESPACE_NORMAL;
   mWordBreak = NS_STYLE_WORDBREAK_NORMAL;
   mWordWrap = NS_STYLE_WORDWRAP_NORMAL;
   mHyphens = NS_STYLE_HYPHENS_MANUAL;
+  mRubyPosition = NS_STYLE_RUBY_POSITION_INITIAL;
   mTextSizeAdjust = NS_STYLE_TEXT_SIZE_ADJUST_AUTO;
   mTextCombineUpright = NS_STYLE_TEXT_COMBINE_UPRIGHT_NONE;
   mControlCharacterVisibility = NS_STYLE_CONTROL_CHARACTER_VISIBILITY_HIDDEN;
 
   mLetterSpacing.SetNormalValue();
   mLineHeight.SetNormalValue();
   mTextIndent.SetCoordValue(0);
   mWordSpacing = 0;
@@ -3362,16 +3363,17 @@ nsStyleText::nsStyleText(const nsStyleTe
     mTextAlignLast(aSource.mTextAlignLast),
     mTextAlignTrue(false),
     mTextAlignLastTrue(false),
     mTextTransform(aSource.mTextTransform),
     mWhiteSpace(aSource.mWhiteSpace),
     mWordBreak(aSource.mWordBreak),
     mWordWrap(aSource.mWordWrap),
     mHyphens(aSource.mHyphens),
+    mRubyPosition(aSource.mRubyPosition),
     mTextSizeAdjust(aSource.mTextSizeAdjust),
     mTextCombineUpright(aSource.mTextCombineUpright),
     mControlCharacterVisibility(aSource.mControlCharacterVisibility),
     mTabSize(aSource.mTabSize),
     mWordSpacing(aSource.mWordSpacing),
     mLetterSpacing(aSource.mLetterSpacing),
     mLineHeight(aSource.mLineHeight),
     mTextIndent(aSource.mTextIndent),
@@ -3402,16 +3404,17 @@ nsChangeHint nsStyleText::CalcDifference
       (mTextAlignLast != aOther.mTextAlignLast) ||
       (mTextAlignTrue != aOther.mTextAlignTrue) ||
       (mTextAlignLastTrue != aOther.mTextAlignLastTrue) ||
       (mTextTransform != aOther.mTextTransform) ||
       (mWhiteSpace != aOther.mWhiteSpace) ||
       (mWordBreak != aOther.mWordBreak) ||
       (mWordWrap != aOther.mWordWrap) ||
       (mHyphens != aOther.mHyphens) ||
+      (mRubyPosition != aOther.mRubyPosition) ||
       (mTextSizeAdjust != aOther.mTextSizeAdjust) ||
       (mLetterSpacing != aOther.mLetterSpacing) ||
       (mLineHeight != aOther.mLineHeight) ||
       (mTextIndent != aOther.mTextIndent) ||
       (mWordSpacing != aOther.mWordSpacing) ||
       (mTabSize != aOther.mTabSize))
     return NS_STYLE_HINT_REFLOW;
 
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -1591,16 +1591,17 @@ struct nsStyleText {
   uint8_t mTextAlignLast;               // [inherited] see nsStyleConsts.h
   bool mTextAlignTrue : 1;              // [inherited] see nsStyleConsts.h
   bool mTextAlignLastTrue : 1;          // [inherited] see nsStyleConsts.h
   uint8_t mTextTransform;               // [inherited] see nsStyleConsts.h
   uint8_t mWhiteSpace;                  // [inherited] see nsStyleConsts.h
   uint8_t mWordBreak;                   // [inherited] see nsStyleConsts.h
   uint8_t mWordWrap;                    // [inherited] see nsStyleConsts.h
   uint8_t mHyphens;                     // [inherited] see nsStyleConsts.h
+  uint8_t mRubyPosition;                // [inherited] see nsStyleConsts.h
   uint8_t mTextSizeAdjust;              // [inherited] see nsStyleConsts.h
   uint8_t mTextCombineUpright;          // [inherited] see nsStyleConsts.h
   uint8_t mControlCharacterVisibility;  // [inherited] see nsStyleConsts.h
   int32_t mTabSize;                     // [inherited] see nsStyleConsts.h
 
   nscoord mWordSpacing;                 // [inherited]
   nsStyleCoord  mLetterSpacing;         // [inherited] coord, normal
   nsStyleCoord  mLineHeight;            // [inherited] coord, factor, normal
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -5042,16 +5042,31 @@ if (SpecialPowers.getBoolPref("layout.cs
   // Using unshift to add these values at the beginning.
   // Adding them to the end would trigger bug 1038905. The "unshift" should be
   // changed to a "push" when this bug is resolved.
   gCSSProperties["display"].other_values.unshift("ruby",
                                                  "ruby-base",
                                                  "ruby-base-container",
                                                  "ruby-text",
                                                  "ruby-text-container");
+  gCSSProperties["ruby-position"] = {
+    domProp: "rubyPosition",
+    inherited: true,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "over right", "right over" ],
+    other_values: [
+      "over left", "under right", "under left",
+      "left over", "right under", "left under"
+    ],
+    invalid_values: [
+      "over", "under", "left", "right", "auto", "none", "not_a_position",
+      "over over", "over under", "left left", "left right",
+      "over left over", "right over left", "0", "100px", "50%"
+    ]
+  };
 }
 
 if (SpecialPowers.getBoolPref("layout.css.grid.enabled")) {
   gCSSProperties["display"].other_values.push("grid", "inline-grid");
   gCSSProperties["grid-auto-flow"] = {
     domProp: "gridAutoFlow",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
new file mode 100644
--- /dev/null
+++ b/media/libstagefright/binding/H264.cpp
@@ -0,0 +1,356 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ArrayUtils.h"
+#include "mp4_demuxer/AnnexB.h"
+#include "mp4_demuxer/ByteReader.h"
+#include "mp4_demuxer/ByteWriter.h"
+#include "mp4_demuxer/H264.h"
+#include <media/stagefright/foundation/ABitReader.h>
+
+using namespace mozilla;
+
+namespace mp4_demuxer
+{
+
+class BitReader
+{
+public:
+  explicit BitReader(const ByteBuffer& aBuffer)
+  : mBitReader(aBuffer.Elements(), aBuffer.Length())
+  {
+  }
+
+  uint32_t ReadBits(size_t aNum)
+  {
+    MOZ_ASSERT(mBitReader.numBitsLeft());
+    MOZ_ASSERT(aNum <= 32);
+    if (mBitReader.numBitsLeft() < aNum) {
+      return 0;
+    }
+    return mBitReader.getBits(aNum);
+  }
+
+  uint32_t ReadBit()
+  {
+    return ReadBits(1);
+  }
+
+  // Read unsigned integer Exp-Golomb-coded.
+  uint32_t ReadUE()
+  {
+    uint32_t i = 0;
+
+    while (ReadBit() == 0 && i < 32) {
+      i++;
+    }
+    if (i == 32) {
+      MOZ_ASSERT(false);
+      return 0;
+    }
+    uint32_t r = ReadBits(i);
+    r += (1 << i) - 1;
+    return r;
+  }
+
+  // Read signed integer Exp-Golomb-coded.
+  int32_t ReadSE()
+  {
+    int32_t r = ReadUE();
+    if (r & 1) {
+      return (r+1) / 2;
+    } else {
+      return -r / 2;
+    }
+  }
+
+private:
+  stagefright::ABitReader mBitReader;
+};
+
+SPSData::SPSData()
+{
+  PodZero(this);
+  chroma_format_idc = 1;
+}
+
+/* static */ already_AddRefed<ByteBuffer>
+H264::DecodeNALUnit(const ByteBuffer* aNAL)
+{
+  MOZ_ASSERT(aNAL);
+
+  if (aNAL->Length() < 4) {
+    return nullptr;
+  }
+
+  nsRefPtr<ByteBuffer> rbsp = new ByteBuffer;
+  ByteReader reader(*aNAL);
+  uint8_t nal_unit_type = reader.ReadU8() & 0x1f;
+  uint32_t nalUnitHeaderBytes = 1;
+  if (nal_unit_type == 14 || nal_unit_type == 20 || nal_unit_type == 21) {
+    bool svc_extension_flag = false;
+    bool avc_3d_extension_flag = false;
+    if (nal_unit_type != 21) {
+      svc_extension_flag = reader.PeekU8() & 0x80;
+    } else {
+      avc_3d_extension_flag = reader.PeekU8() & 0x80;
+    }
+    if (svc_extension_flag) {
+      nalUnitHeaderBytes += 3;
+    } else if (avc_3d_extension_flag) {
+      nalUnitHeaderBytes += 2;
+    } else {
+      nalUnitHeaderBytes += 3;
+    }
+  }
+  if (!reader.Read(nalUnitHeaderBytes - 1)) {
+    return nullptr;
+  }
+  uint32_t zeros = 0;
+  while (reader.Remaining()) {
+    uint8_t byte = reader.ReadU8();
+    if (zeros < 2 || byte == 0x03) {
+      rbsp->AppendElement(byte);
+    }
+    if (byte == 0) {
+      zeros++;
+    } else {
+      zeros = 0;
+    }
+  }
+  return rbsp.forget();
+}
+
+/* static */ bool
+H264::DecodeSPS(const ByteBuffer* aSPS, SPSData& aDest)
+{
+  MOZ_ASSERT(aSPS);
+  BitReader br(*aSPS);
+
+  int32_t lastScale;
+  int32_t nextScale;
+  int32_t deltaScale;
+
+  aDest.profile_idc = br.ReadBits(8);
+  aDest.constraint_set0_flag = br.ReadBit();
+  aDest.constraint_set1_flag = br.ReadBit();
+  aDest.constraint_set2_flag = br.ReadBit();
+  aDest.constraint_set3_flag = br.ReadBit();
+  aDest.constraint_set4_flag = br.ReadBit();
+  aDest.constraint_set5_flag = br.ReadBit();
+  br.ReadBits(2); // reserved_zero_2bits
+  aDest.level_idc = br.ReadBits(8);
+  aDest.seq_parameter_set_id = br.ReadUE();
+  if (aDest.profile_idc == 100 || aDest.profile_idc == 110 ||
+      aDest.profile_idc == 122 || aDest.profile_idc == 244 ||
+      aDest.profile_idc == 44 || aDest.profile_idc == 83 ||
+     aDest.profile_idc == 86 || aDest.profile_idc == 118 ||
+      aDest.profile_idc == 128 || aDest.profile_idc == 138 ||
+      aDest.profile_idc == 139 || aDest.profile_idc == 134) {
+    if ((aDest.chroma_format_idc = br.ReadUE()) == 3) {
+      aDest.separate_colour_plane_flag = br.ReadBit();
+    }
+    br.ReadUE();  // bit_depth_luma_minus8
+    br.ReadUE();  // bit_depth_chroma_minus8
+    br.ReadBit(); // qpprime_y_zero_transform_bypass_flag
+    if (br.ReadBit()) { // seq_scaling_matrix_present_flag
+      for (int idx = 0; idx < ((aDest.chroma_format_idc != 3) ? 8 : 12); ++idx) {
+        if (br.ReadBit()) { // Scaling list present
+          lastScale = nextScale = 8;
+          int sl_n = (idx < 6) ? 16 : 64;
+          for (int sl_i = 0; sl_i < sl_n; sl_i++) {
+            if (nextScale) {
+              deltaScale = br.ReadSE();
+              nextScale = (lastScale + deltaScale + 256) % 256;
+            }
+            lastScale = (nextScale == 0) ? lastScale : nextScale;
+          }
+        }
+      }
+    }
+  }
+  aDest.log2_max_frame_num = br.ReadUE() + 4;
+  aDest.pic_order_cnt_type = br.ReadUE();
+  if (aDest.pic_order_cnt_type == 0) {
+    aDest.log2_max_pic_order_cnt_lsb = br.ReadUE() + 4;
+  } else if (aDest.pic_order_cnt_type == 1) {
+    aDest.delta_pic_order_always_zero_flag = br.ReadBit();
+    aDest.offset_for_non_ref_pic = br.ReadSE();
+    aDest.offset_for_top_to_bottom_field = br.ReadSE();
+    uint32_t num_ref_frames_in_pic_order_cnt_cycle = br.ReadUE();
+    for (uint32_t i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++) {
+      br.ReadSE(); // offset_for_ref_frame[i]
+    }
+  }
+  aDest.max_num_ref_frames = br.ReadUE();
+  aDest.gaps_in_frame_num_allowed_flag = br.ReadBit();
+  aDest.pic_width_in_mbs = br.ReadUE() + 1;
+  aDest.pic_height_in_map_units = br.ReadUE() + 1;
+  aDest.frame_mbs_only_flag = br.ReadBit();
+  if (!aDest.frame_mbs_only_flag) {
+    aDest.pic_height_in_map_units *= 2;
+    aDest.mb_adaptive_frame_field_flag = br.ReadBit();
+  }
+  br.ReadBit(); // direct_8x8_inference_flag
+  aDest.frame_cropping_flag = br.ReadBit();
+  if (aDest.frame_cropping_flag) {
+    aDest.frame_crop_left_offset = br.ReadUE();
+    aDest.frame_crop_right_offset = br.ReadUE();
+    aDest.frame_crop_top_offset = br.ReadUE();
+    aDest.frame_crop_bottom_offset = br.ReadUE();
+  }
+
+  // Calculate common values.
+
+  // FFmpeg and VLC ignore the left and top cropping. Do the same here.
+
+  uint8_t ChromaArrayType =
+    aDest.separate_colour_plane_flag ? 0 : aDest.chroma_format_idc;
+  // Calculate width.
+  uint32_t CropUnitX = 1;
+  uint32_t SubWidthC = aDest.chroma_format_idc == 3 ? 1 : 2;
+  if (ChromaArrayType != 0) {
+    CropUnitX = SubWidthC;
+  }
+  uint32_t cropX = CropUnitX * aDest.frame_crop_right_offset;
+  aDest.pic_width = aDest.pic_width_in_mbs * 16 - cropX;
+
+  // Calculate Height
+  uint32_t CropUnitY = 2 - aDest.frame_mbs_only_flag;
+  uint32_t SubHeightC = aDest.chroma_format_idc <= 1 ? 2 : 1;
+  if (ChromaArrayType != 0)
+    CropUnitY *= SubHeightC;
+  uint32_t cropY = CropUnitY * aDest.frame_crop_bottom_offset;
+  aDest.pic_height = aDest.pic_height_in_map_units * 16 - cropY;
+
+  aDest.interlaced = !aDest.frame_mbs_only_flag;
+  return true;
+}
+
+/* static */ void
+H264::vui_parameters(BitReader& aBr, SPSData& aDest)
+{
+  aDest.aspect_ratio_info_present_flag = aBr.ReadBit();
+  if (aDest.aspect_ratio_info_present_flag)
+  {
+    aDest.aspect_ratio_idc = aBr.ReadBits(8);
+
+    if (aDest.aspect_ratio_idc == 255 /* EXTENDED_SAR */) {
+      aDest.sar_width  = aBr.ReadBits(16);
+      aDest.sar_height = aBr.ReadBits(16);
+    }
+  }
+  else {
+    aDest.sar_width = aDest.sar_height = 0;
+  }
+
+  if (aBr.ReadBit()) { //overscan_info_present_flag
+    aDest.overscan_appropriate_flag = aBr.ReadBit();
+  }
+  if (aBr.ReadBit()) { //video_signal_type_present_flag
+    aDest.video_format = aBr.ReadBits(3);
+    aDest.video_full_range_flag = aBr.ReadBit();
+    aDest.colour_description_present_flag = aBr.ReadBit();
+    if (aDest.colour_description_present_flag) {
+      aDest.colour_primaries = aBr.ReadBits(8);
+      aDest.transfer_characteristics = aBr.ReadBits(8);
+      aDest.matrix_coefficients = aBr.ReadBits(8);
+    }
+  }
+  aDest.chroma_loc_info_present_flag = aBr.ReadBit();
+
+  if (aDest.chroma_loc_info_present_flag) {
+    aDest.chroma_sample_loc_type_top_field = aBr.ReadUE();
+    aDest.chroma_sample_loc_type_bottom_field = aBr.ReadUE();
+  }
+
+  if (aBr.ReadBit()) { //timing_info_present_flag
+    aDest.num_units_in_tick = aBr.ReadBits(32);
+    aDest.time_scale = aBr.ReadBits(32);
+    aDest.fixed_frame_rate_flag = aBr.ReadBit();
+  }
+
+  bool hrd_present = false;
+  if (aBr.ReadBit()) { // nal_hrd_parameters_present_flag
+    hrd_parameters(aBr);
+    hrd_present = true;
+  }
+  if (aBr.ReadBit()) { // vcl_hrd_parameters_present_flag
+    hrd_parameters(aBr);
+    hrd_present = true;
+  }
+  if (hrd_present) {
+    aBr.ReadBit(); // low_delay_hrd_flag
+  }
+  aDest.pic_struct_present_flag = aBr.ReadBit();
+  aDest.bitstream_restriction_flag = aBr.ReadBit();
+  if (aDest.bitstream_restriction_flag) {
+    aDest.motion_vectors_over_pic_boundaries_flag = aBr.ReadBit();
+    aDest.max_bytes_per_pic_denom = aBr.ReadUE();
+    aDest.max_bits_per_mb_denom = aBr.ReadUE();
+    aDest.log2_max_mv_length_horizontal = aBr.ReadUE();
+    aDest.log2_max_mv_length_vertical = aBr.ReadUE();
+    aDest.max_num_reorder_frames = aBr.ReadUE();
+    aDest.max_dec_frame_buffering = aBr.ReadUE();
+  }
+}
+
+/* static */ void
+H264::hrd_parameters(BitReader& aBr)
+{
+  uint32_t cpb_cnt_minus1 = aBr.ReadUE();
+  aBr.ReadBits(4); // bit_rate_scale
+  aBr.ReadBits(4); // cpb_size_scale
+  for (uint32_t SchedSelIdx = 0; SchedSelIdx <= cpb_cnt_minus1; SchedSelIdx++) {
+    aBr.ReadUE(); // bit_rate_value_minus1[ SchedSelIdx ]
+    aBr.ReadUE(); // cpb_size_value_minus1[ SchedSelIdx ]
+    aBr.ReadBit(); // cbr_flag[ SchedSelIdx ]
+  }
+  aBr.ReadBits(5); // initial_cpb_removal_delay_length_minus1
+  aBr.ReadBits(5); // cpb_removal_delay_length_minus1
+  aBr.ReadBits(5); // dpb_output_delay_length_minus1
+  aBr.ReadBits(5); // time_offset_length
+}
+
+/* static */ bool
+H264::DecodeSPSFromExtraData(const ByteBuffer* aExtraData, SPSData& aDest)
+{
+  if (!AnnexB::HasSPS(aExtraData)) {
+    return false;
+  }
+  ByteReader reader(*aExtraData);
+
+  if (!reader.Read(5)) {
+    return false;
+  }
+
+  if (!(reader.ReadU8() & 0x1f)) {
+    // No SPS.
+    reader.DiscardRemaining();
+    return false;
+  }
+  uint16_t length = reader.ReadU16();
+
+  if ((reader.PeekU8() & 0x1f) != 7) {
+    // Not a SPS NAL type.
+    reader.DiscardRemaining();
+    return false;
+  }
+
+  const uint8_t* ptr = reader.Read(length);
+  if (!ptr) {
+    return false;
+  }
+
+  nsRefPtr<ByteBuffer> rawNAL = new ByteBuffer;
+  rawNAL->AppendElements(ptr, length);
+
+  nsRefPtr<ByteBuffer> sps = DecodeNALUnit(rawNAL);
+
+  reader.DiscardRemaining();
+
+  return DecodeSPS(sps, aDest);
+}
+
+} // namespace mp4_demuxer
new file mode 100644
--- /dev/null
+++ b/media/libstagefright/binding/include/mp4_demuxer/H264.h
@@ -0,0 +1,361 @@
+/* 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 MP4_DEMUXER_H264_H_
+#define MP4_DEMUXER_H264_H_
+
+#include "mp4_demuxer/DecoderData.h"
+
+namespace mp4_demuxer
+{
+
+class BitReader;
+
+struct SPSData
+{
+  /* Decoded Members */
+  /*
+    pic_width is the decoded width according to:
+    pic_width = ((pic_width_in_mbs_minus1 + 1) * 16)
+                - (frame_crop_left_offset + frame_crop_right_offset) * 2
+   */
+  uint32_t pic_width;
+  /*
+    pic_height is the decoded height according to:
+    pic_height = (2 - frame_mbs_only_flag) * ((pic_height_in_map_units_minus1 + 1) * 16)
+                 - (frame_crop_top_offset + frame_crop_bottom_offset) * 2
+   */
+  uint32_t pic_height;
+
+  bool interlaced;
+
+  /*
+    H264 decoding parameters according to ITU-T H.264 (T-REC-H.264-201402-I/en)
+   http://www.itu.int/rec/T-REC-H.264-201402-I/en
+   */
+
+  bool constraint_set0_flag;
+  bool constraint_set1_flag;
+  bool constraint_set2_flag;
+  bool constraint_set3_flag;
+  bool constraint_set4_flag;
+  bool constraint_set5_flag;
+
+  /*
+    profile_idc and level_idc indicate the profile and level to which the coded
+    video sequence conforms when the SVC sequence parameter set is the active
+    SVC sequence parameter set.
+   */
+  uint8_t profile_idc;
+  uint8_t level_idc;
+
+  /*
+    seq_parameter_set_id identifies the sequence parameter set that is referred
+    to by the picture parameter set. The value of seq_parameter_set_id shall be
+    in the range of 0 to 31, inclusive.
+   */
+  uint8_t seq_parameter_set_id;
+
+  /*
+    When the value of chroma_format_idc is equal to 1, the nominal vertical
+    and horizontal relative locations of luma and chroma samples in frames are
+    shown in Figure 6-1. Alternative chroma sample relative locations may be
+    indicated in video usability information (see Annex E).
+   */
+  uint8_t chroma_format_idc;
+
+  /*
+    If separate_colour_plane_flag is equal to 0, each of the two chroma arrays
+    has the same height and width as the luma array. Otherwise
+    (separate_colour_plane_flag is equal to 1), the three colour planes are
+    separately processed as monochrome sampled pictures.
+   */
+  bool separate_colour_plane_flag;
+
+  /*
+    log2_max_frame_num_minus4 specifies the value of the variable
+    MaxFrameNum that is used in frame_num related derivations as
+    follows:
+
+     MaxFrameNum = 2( log2_max_frame_num_minus4 + 4 ). The value of
+    log2_max_frame_num_minus4 shall be in the range of 0 to 12, inclusive.
+   */
+  uint8_t log2_max_frame_num;
+
+  /*
+    pic_order_cnt_type specifies the method to decode picture order
+    count (as specified in subclause 8.2.1). The value of
+    pic_order_cnt_type shall be in the range of 0 to 2, inclusive.
+   */
+  uint8_t pic_order_cnt_type;
+
+  /*
+    log2_max_pic_order_cnt_lsb_minus4 specifies the value of the
+    variable MaxPicOrderCntLsb that is used in the decoding
+    process for picture order count as specified in subclause
+    8.2.1 as follows:
+
+    MaxPicOrderCntLsb = 2( log2_max_pic_order_cnt_lsb_minus4 + 4 )
+
+    The value of log2_max_pic_order_cnt_lsb_minus4 shall be in
+    the range of 0 to 12, inclusive.
+   */
+  uint8_t log2_max_pic_order_cnt_lsb;
+
+  /*
+    delta_pic_order_always_zero_flag equal to 1 specifies that
+    delta_pic_order_cnt[ 0 ] and delta_pic_order_cnt[ 1 ] are
+    not present in the slice headers of the sequence and shall
+    be inferred to be equal to 0.
+   */
+  bool delta_pic_order_always_zero_flag;
+
+  /*
+    offset_for_non_ref_pic is used to calculate the picture
+    order count of a non-reference picture as specified in
+    8.2.1. The value of offset_for_non_ref_pic shall be in the
+    range of -231 to 231 - 1, inclusive.
+   */
+  int8_t offset_for_non_ref_pic;
+
+  /*
+    offset_for_top_to_bottom_field is used to calculate the
+    picture order count of a bottom field as specified in
+    subclause 8.2.1. The value of offset_for_top_to_bottom_field
+    shall be in the range of -231 to 231 - 1, inclusive.
+   */
+  int8_t offset_for_top_to_bottom_field;
+
+  /*
+    max_num_ref_frames specifies the maximum number of short-term and
+    long-term reference frames, complementary reference field pairs,
+    and non-paired reference fields that may be used by the decoding
+    process for inter prediction of any picture in the
+    sequence. max_num_ref_frames also determines the size of the sliding
+    window operation as specified in subclause 8.2.5.3. The value of
+    max_num_ref_frames shall be in the range of 0 to MaxDpbSize (as
+    specified in subclause A.3.1 or A.3.2), inclusive.
+   */
+  uint32_t max_num_ref_frames;
+
+  /*
+    gaps_in_frame_num_value_allowed_flag specifies the allowed
+    values of frame_num as specified in subclause 7.4.3 and the
+    decoding process in case of an inferred gap between values of
+    frame_num as specified in subclause 8.2.5.2.
+   */
+  bool gaps_in_frame_num_allowed_flag;
+
+  /*
+    pic_width_in_mbs_minus1 plus 1 specifies the width of each
+    decoded picture in units of macroblocks.  16 macroblocks in a row
+   */
+  uint32_t pic_width_in_mbs;
+
+  /*
+    pic_height_in_map_units_minus1 plus 1 specifies the height in
+    slice group map units of a decoded frame or field.  16
+    macroblocks in each column.
+   */
+  uint32_t pic_height_in_map_units;
+
+  /*
+    frame_mbs_only_flag equal to 0 specifies that coded pictures of
+    the coded video sequence may either be coded fields or coded
+    frames. frame_mbs_only_flag equal to 1 specifies that every
+    coded picture of the coded video sequence is a coded frame
+    containing only frame macroblocks.
+   */
+  bool frame_mbs_only_flag;
+
+  /*
+    mb_adaptive_frame_field_flag equal to 0 specifies no
+    switching between frame and field macroblocks within a
+    picture. mb_adaptive_frame_field_flag equal to 1 specifies
+    the possible use of switching between frame and field
+    macroblocks within frames. When mb_adaptive_frame_field_flag
+    is not present, it shall be inferred to be equal to 0.
+   */
+  bool mb_adaptive_frame_field_flag;
+
+  /*
+    frame_cropping_flag equal to 1 specifies that the frame cropping
+    offset parameters follow next in the sequence parameter
+    set. frame_cropping_flag equal to 0 specifies that the frame
+    cropping offset parameters are not present.
+   */
+  bool frame_cropping_flag;
+  uint32_t frame_crop_left_offset;;
+  uint32_t frame_crop_right_offset;
+  uint32_t frame_crop_top_offset;
+  uint32_t frame_crop_bottom_offset;
+
+  // VUI Parameters
+
+  /*
+    vui_parameters_present_flag equal to 1 specifies that the
+    vui_parameters( ) syntax structure as specified in Annex E is
+    present. vui_parameters_present_flag equal to 0 specifies that
+    the vui_parameters( ) syntax structure as specified in Annex E
+    is not present.
+   */
+  bool vui_parameters_present_flag;
+
+  /*
+   aspect_ratio_info_present_flag equal to 1 specifies that
+   aspect_ratio_idc is present. aspect_ratio_info_present_flag
+   equal to 0 specifies that aspect_ratio_idc is not present.
+   */
+  bool aspect_ratio_info_present_flag;
+
+  /*
+    aspect_ratio_idc specifies the value of the sample aspect
+    ratio of the luma samples. Table E-1 shows the meaning of
+    the code. When aspect_ratio_idc indicates Extended_SAR, the
+    sample aspect ratio is represented by sar_width and
+    sar_height. When the aspect_ratio_idc syntax element is not
+    present, aspect_ratio_idc value shall be inferred to be
+    equal to 0.
+   */
+  uint8_t aspect_ratio_idc;
+  uint32_t sar_width;
+  uint32_t sar_height;
+
+  bool overscan_info_present_flag;
+  bool overscan_appropriate_flag;
+
+  uint8_t video_format;
+  bool video_full_range_flag;
+  bool colour_description_present_flag;
+  uint8_t colour_primaries;
+  uint8_t transfer_characteristics;
+  uint8_t matrix_coefficients;
+  bool chroma_loc_info_present_flag;
+  uint32_t chroma_sample_loc_type_top_field;
+  uint32_t chroma_sample_loc_type_bottom_field;
+  uint32_t num_units_in_tick;
+  uint32_t time_scale;
+  bool fixed_frame_rate_flag;
+
+  // Bitstream restriction parameters
+
+  /*
+    pic_struct_present_flag equal to 1 specifies that picture timing SEI
+    messages (clause D.2.2) are present that include the pic_struct syntax
+    element. pic_struct_present_flag equal to 0 specifies that the pic_struct
+    syntax element is not present in picture timing SEI messages.
+    When pic_struct_present_flag is not present, its value shall be inferred to
+    be equal to 0.
+   */
+  bool pic_struct_present_flag;
+
+  /*
+    bitstream_restriction_flag equal to 1, specifies that the following coded
+    video sequence bitstream restriction parameters are present.
+    bitstream_restriction_flag equal to 0, specifies that the following coded
+    video sequence bitstream restriction parameters are not present.
+   */
+  bool bitstream_restriction_flag;
+
+  /*
+    motion_vectors_over_pic_boundaries_flag equal to 0 indicates that no
+    sample outside the picture boundaries and no sample at a fractional
+    sample position for which the sample value is derived using one or more
+    samples outside the picture boundaries is used for inter prediction of any
+    sample. motion_vectors_over_pic_boundaries_flag equal to 1 indicates that
+    one or more samples outside picture boundaries may be used in inter
+    prediction. When the motion_vectors_over_pic_boundaries_flag syntax element
+    is not present, motion_vectors_over_pic_boundaries_flag value shall be
+    inferred to be equal to 1.
+   */
+  bool motion_vectors_over_pic_boundaries_flag;
+
+  /*
+    max_bytes_per_pic_denom indicates a number of bytes not exceeded by the
+    sum of the sizes of the VCL NAL units associated with any coded picture in
+    the coded video sequence.
+   */
+  uint32_t max_bytes_per_pic_denom;
+
+  /*
+    max_bits_per_mb_denom indicates an upper bound for the number of coded bits
+    of macroblock_layer( ) data for any macroblock in any picture of the coded
+    video sequence. The value of max_bits_per_mb_denom shall be in the range
+    of 0 to 16, inclusive.
+   */
+  uint32_t max_bits_per_mb_denom;
+
+  /*
+    log2_max_mv_length_horizontal and log2_max_mv_length_vertical indicate the
+    maximum absolute value of a decoded horizontal and vertical motion vector
+    component, respectively, in 1⁄4 luma sample units, for all pictures in the
+    coded video sequence. A value of n asserts that no value of a motion vector
+    component shall exceed the range from −2n to 2n − 1, inclusive, in units
+    of 1⁄4 luma sample displacement. The value of log2_max_mv_length_horizontal
+    shall be in the range of 0 to 16, inclusive. The value of
+    log2_max_mv_length_vertical shall be in the range of 0 to 16, inclusive.
+    When log2_max_mv_length_horizontal is not present, the values of
+    log2_max_mv_length_horizontal and log2_max_mv_length_vertical shall be
+    inferred to be equal to 16.
+   */
+   uint32_t log2_max_mv_length_horizontal;
+   uint32_t log2_max_mv_length_vertical;
+
+  /*
+    max_num_reorder_frames indicates an upper bound for the number of frames
+    buffers, in the decoded picture buffer (DPB), that are required for storing
+    frames, complementary field pairs, and non-paired fields before output.
+    It is a requirement of bitstream conformance that the maximum number of
+    frames, complementary field pairs, or non-paired fields that precede any
+    frame, complementary field pair, or non-paired field in the coded video
+    sequence in decoding order and follow it in output order shall be less than
+    or equal to max_num_reorder_frames. The value of max_num_reorder_frames
+    shall be in the range of 0 to max_dec_frame_buffering, inclusive.
+    When the max_num_reorder_frames syntax element is not present, the value
+    of max_num_reorder_frames value shall be inferred as follows:
+     – If profile_idc is equal to 44, 86, 100, 110, 122, or 244 and
+       constraint_set3_flag is equal to 1, the value of max_num_reorder_frames
+       shall be inferred to be equal to 0.
+     – Otherwise (profile_idc is not equal to 44, 86, 100, 110, 122, or 244 or
+       constraint_set3_flag is equal to 0), the value of max_num_reorder_frames
+       shall be inferred to be equal to MaxDpbFrames.
+   */
+   uint32_t max_num_reorder_frames;
+
+  /*
+    max_dec_frame_buffering specifies the required size of the HRD decoded
+    picture buffer (DPB) in units of frame buffers. It is a requirement of
+    bitstream conformance that the coded video sequence shall not require a
+    decoded picture buffer with size of more than
+    Max( 1, max_dec_frame_buffering ) frame buffers to enable the output of
+    decoded pictures at the output times specified by dpb_output_delay of the
+    picture timing SEI messages. The value of max_dec_frame_buffering shall be
+    greater than or equal to max_num_ref_frames. An upper bound for the value
+    of max_dec_frame_buffering is specified by the level limits in
+    clauses A.3.1, A.3.2, G.10.2.1, and H.10.2.
+   */
+   uint32_t max_dec_frame_buffering;
+
+  SPSData();
+};
+
+class H264
+{
+public:
+  static bool DecodeSPSFromExtraData(const ByteBuffer* aExtraData, SPSData& aDest);
+  /* Extract RAW BYTE SEQUENCE PAYLOAD from NAL content.
+     Returns nullptr if invalid content. */
+  static already_AddRefed<ByteBuffer> DecodeNALUnit(const ByteBuffer* aNAL);
+  /* Decode SPS NAL RBSP and fill SPSData structure */
+  static bool DecodeSPS(const ByteBuffer* aSPS, SPSData& aDest);
+
+private:
+  static void vui_parameters(BitReader& aBr, SPSData& aDest);
+  // Read HRD parameters, all data is ignored.
+  static void hrd_parameters(BitReader& aBr);
+};
+
+} // namespace mp4_demuxer
+
+#endif // MP4_DEMUXER_H264_H_
--- a/media/libstagefright/moz.build
+++ b/media/libstagefright/moz.build
@@ -50,16 +50,17 @@ if CONFIG['OS_TARGET'] != 'Android':
 EXPORTS.mp4_demuxer += [
     'binding/include/mp4_demuxer/Adts.h',
     'binding/include/mp4_demuxer/AnnexB.h',
     'binding/include/mp4_demuxer/AtomType.h',
     'binding/include/mp4_demuxer/BufferStream.h',
     'binding/include/mp4_demuxer/ByteReader.h',
     'binding/include/mp4_demuxer/ByteWriter.h',
     'binding/include/mp4_demuxer/DecoderData.h',
+    'binding/include/mp4_demuxer/H264.h',
     'binding/include/mp4_demuxer/Interval.h',
     'binding/include/mp4_demuxer/MoofParser.h',
     'binding/include/mp4_demuxer/mp4_demuxer.h',
 ]
 
 SOURCES += [
     'frameworks/av/media/libstagefright/foundation/hexdump.cpp',
     'frameworks/av/media/libstagefright/MetaData.cpp',
@@ -70,16 +71,17 @@ SOURCES += [
 ]
 
 UNIFIED_SOURCES += [
     'binding/Adts.cpp',
     'binding/AnnexB.cpp',
     'binding/Box.cpp',
     'binding/BufferStream.cpp',
     'binding/DecoderData.cpp',
+    'binding/H264.cpp',
     'binding/Index.cpp',
     'binding/MoofParser.cpp',
     'binding/mp4_demuxer.cpp',
     'frameworks/av/media/libstagefright/DataSource.cpp',
     'frameworks/av/media/libstagefright/ESDS.cpp',
     'frameworks/av/media/libstagefright/foundation/AAtomizer.cpp',
     'frameworks/av/media/libstagefright/foundation/ABitReader.cpp',
     'frameworks/av/media/libstagefright/foundation/ABuffer.cpp',
--- a/media/webrtc/trunk/webrtc/modules/video_processing/main/source/content_analysis.cc
+++ b/media/webrtc/trunk/webrtc/modules/video_processing/main/source/content_analysis.cc
@@ -78,17 +78,17 @@ VideoContentMetrics* VPMContentAnalysis:
   }
 #endif
 
   return ContentMetrics();
 }
 
 int32_t VPMContentAnalysis::Release() {
   content_metrics_.reset(NULL);
-  prev_frame_.reset(NULL);
+  prev_frame_.reset();
 
   width_ = 0;
   height_ = 0;
   first_frame_ = true;
 
   return VPM_OK;
 }
 
@@ -107,17 +107,17 @@ int32_t VPMContentAnalysis::Initialize(i
     skip_num_ = 2;
   }
   // use skipNum = 4 for FULLL_HD images
   if ( (height_ >=  1080) && (width_ >= 1920) ) {
     skip_num_ = 4;
   }
 
   content_metrics_.reset(NULL);
-  prev_frame_.reset(NULL);
+  prev_frame_.reset();
 
   // Spatial Metrics don't work on a border of 8. Minimum processing
   // block size is 16 pixels.  So make sure the width and height support this.
   if (width_ <= 32 || height_ <= 32) {
     return VPM_PARAMETER_ERROR;
   }
 
   content_metrics_.reset(new VideoContentMetrics());
--- a/media/webrtc/trunk/webrtc/modules/video_processing/main/source/content_analysis.h
+++ b/media/webrtc/trunk/webrtc/modules/video_processing/main/source/content_analysis.h
@@ -61,17 +61,17 @@ class VPMContentAnalysis {
   int32_t ComputeSpatialMetrics_C();
 
 #if defined(WEBRTC_ARCH_X86_FAMILY)
   int32_t ComputeSpatialMetrics_SSE2();
   int32_t TemporalDiffMetric_SSE2();
 #endif
 
   const uint8_t* orig_frame_;
-  scoped_ptr<uint8_t> prev_frame_;
+  scoped_ptr<uint8_t[]> prev_frame_;
   int width_;
   int height_;
   int skip_num_;
   int border_;
 
   // Content Metrics: Stores the local average of the metrics.
   float motion_magnitude_;   // motion class
   float spatial_pred_err_;   // spatial class
deleted file mode 120000
--- a/media/webrtc/trunk/webrtc/tools/e2e_quality/audio/perf
+++ /dev/null
@@ -1,1 +0,0 @@
-../../../../tools/perf
\ No newline at end of file
--- a/mfbt/Attributes.h
+++ b/mfbt/Attributes.h
@@ -507,21 +507,21 @@
  *   value is allocated on the heap, and will as a result check such allocations
  *   during MOZ_STACK_CLASS and MOZ_NONHEAP_CLASS annotation checking.
  * MOZ_IMPLICIT: Applies to constructors. Implicit conversion constructors
  *   are disallowed by default unless they are marked as MOZ_IMPLICIT. This
  *   attribute must be used for constructors which intend to provide implicit
  *   conversions.
  * MOZ_NO_ARITHMETIC_EXPR_IN_ARGUMENT: Applies to functions. Makes it a compile
  *   time error to pass arithmetic expressions on variables to the function.
- * MOZ_STRONG_REF: Applies to declarations of pointer types.  This attribute
+ * MOZ_OWNING_REF: Applies to declarations of pointer types.  This attribute
  *   tells the compiler that the raw pointer is a strong reference, and that
  *   property is somehow enforced by the code.  This can make the compiler
  *   ignore these pointers when validating the usage of pointers otherwise.
- * MOZ_WEAK_REF: Applies to declarations of pointer types.  This attribute
+ * MOZ_NON_OWNING_REF: Applies to declarations of pointer types.  This attribute
  *   tells the compiler that the raw pointer is a weak reference, and that
  *   property is somehow enforced by the code.  This can make the compiler
  *   ignore these pointers when validating the usage of pointers otherwise.
  */
 #ifdef MOZ_CLANG_PLUGIN
 #  define MOZ_MUST_OVERRIDE __attribute__((annotate("moz_must_override")))
 #  define MOZ_STACK_CLASS __attribute__((annotate("moz_stack_class")))
 #  define MOZ_NONHEAP_CLASS __attribute__((annotate("moz_nonheap_class")))
@@ -530,18 +530,18 @@
      /* in debug builds, these classes do have non-trivial constructors. */
 #    define MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS __attribute__((annotate("moz_global_class")))
 #  else
 #    define MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS __attribute__((annotate("moz_global_class"))) \
             MOZ_TRIVIAL_CTOR_DTOR
 #  endif
 #  define MOZ_IMPLICIT __attribute__((annotate("moz_implicit")))
 #  define MOZ_NO_ARITHMETIC_EXPR_IN_ARGUMENT __attribute__((annotate("moz_no_arith_expr_in_arg")))
-#  define MOZ_STRONG_REF __attribute__((annotate("moz_strong_ref")))
-#  define MOZ_WEAK_REF __attribute__((annotate("moz_weak_ref")))
+#  define MOZ_OWNING_REF __attribute__((annotate("moz_strong_ref")))
+#  define MOZ_NON_OWNING_REF __attribute__((annotate("moz_weak_ref")))
 /*
  * It turns out that clang doesn't like void func() __attribute__ {} without a
  * warning, so use pragmas to disable the warning. This code won't work on GCC
  * anyways, so the warning is safe to ignore.
  */
 #  define MOZ_HEAP_ALLOCATOR \
     _Pragma("clang diagnostic push") \
     _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \
@@ -551,18 +551,18 @@
 #  define MOZ_MUST_OVERRIDE /* nothing */
 #  define MOZ_STACK_CLASS /* nothing */
 #  define MOZ_NONHEAP_CLASS /* nothing */
 #  define MOZ_TRIVIAL_CTOR_DTOR /* nothing */
 #  define MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS /* nothing */
 #  define MOZ_IMPLICIT /* nothing */
 #  define MOZ_NO_ARITHMETIC_EXPR_IN_ARGUMENT /* nothing */
 #  define MOZ_HEAP_ALLOCATOR /* nothing */
-#  define MOZ_STRONG_REF /* nothing */
-#  define MOZ_WEAK_REF /* nothing */
+#  define MOZ_OWNING_REF /* nothing */
+#  define MOZ_NON_OWNING_REF /* nothing */
 #endif /* MOZ_CLANG_PLUGIN */
 
 /*
  * MOZ_THIS_IN_INITIALIZER_LIST is used to avoid a warning when we know that
  * it's safe to use 'this' in an initializer list.
  */
 #ifdef _MSC_VER
 #  define MOZ_THIS_IN_INITIALIZER_LIST() \
--- a/mfbt/IntegerTypeTraits.h
+++ b/mfbt/IntegerTypeTraits.h
@@ -74,16 +74,21 @@ struct StdintTypeForSizeAndSignedness<8,
 
 } // namespace detail
 
 template<size_t Size>
 struct UnsignedStdintTypeForSize
   : detail::StdintTypeForSizeAndSignedness<Size, false>
 {};
 
+template<size_t Size>
+struct SignedStdintTypeForSize
+  : detail::StdintTypeForSizeAndSignedness<Size, true>
+{};
+
 template<typename IntegerType>
 struct PositionOfSignBit
 {
   static_assert(IsIntegral<IntegerType>::value,
                 "PositionOfSignBit is only for integral types");
   // 8 here should be CHAR_BIT from limits.h, but the world has moved on.
   static const size_t value = 8 * sizeof(IntegerType) - 1;
 };
--- a/mfbt/RefPtr.h
+++ b/mfbt/RefPtr.h
@@ -280,17 +280,17 @@ public:
 
 private:
   void assign(T* aVal)
   {
     unref(mPtr);
     mPtr = aVal;
   }
 
-  T* mPtr;
+  T* MOZ_OWNING_REF mPtr;
 
   static MOZ_ALWAYS_INLINE T* ref(T* aVal)
   {
     if (aVal) {
       aVal->AddRef();
     }
     return aVal;
   }
@@ -331,17 +331,17 @@ public:
     T* tmp = mPtr;
     mPtr = nullptr;
     return tmp;
   }
 
 private:
   TemporaryRef(T* aVal, const DontRef&) : mPtr(aVal) {}
 
-  mutable T* mPtr;
+  mutable T* MOZ_OWNING_REF mPtr;
 
   TemporaryRef() MOZ_DELETE;
   void operator=(const TemporaryRef&) MOZ_DELETE;
 };
 
 /**
  * OutParamRef is a wrapper that tracks a refcounted pointer passed as
  * an outparam argument to a function.  OutParamRef implements COM T**
--- a/mfbt/WeakPtr.h
+++ b/mfbt/WeakPtr.h
@@ -64,16 +64,17 @@
  * http://src.chromium.org/svn/trunk/src/base/memory/weak_ptr.h
  */
 
 #ifndef mozilla_WeakPtr_h
 #define mozilla_WeakPtr_h
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
 #include "mozilla/NullPtr.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/TypeTraits.h"
 
 #include <string.h>
 
 namespace mozilla {
 
@@ -117,17 +118,17 @@ public:
 #undef snprintf
 #endif
 
 private:
   friend class mozilla::SupportsWeakPtr<T>;
 
   void detach() { mPtr = nullptr; }
 
-  T* mPtr;
+  T* MOZ_NON_OWNING_REF mPtr;
 };
 
 } // namespace detail
 
 template <typename T>
 class SupportsWeakPtr
 {
 protected:
--- a/netwerk/base/public/security-prefs.js
+++ b/netwerk/base/public/security-prefs.js
@@ -20,17 +20,16 @@ pref("security.ssl.enable_alpn", true);
 pref("security.ssl3.ecdhe_rsa_aes_128_gcm_sha256", true);
 pref("security.ssl3.ecdhe_ecdsa_aes_128_gcm_sha256", true);
 pref("security.ssl3.ecdhe_rsa_aes_128_sha", true);
 pref("security.ssl3.ecdhe_ecdsa_aes_128_sha", true);
 pref("security.ssl3.ecdhe_rsa_aes_256_sha", true);
 pref("security.ssl3.ecdhe_ecdsa_aes_256_sha", true);
 pref("security.ssl3.dhe_rsa_aes_128_sha", true);
 pref("security.ssl3.dhe_rsa_aes_256_sha", true);
-pref("security.ssl3.dhe_dss_aes_128_sha", false);
 pref("security.ssl3.ecdhe_rsa_rc4_128_sha", true);
 pref("security.ssl3.ecdhe_ecdsa_rc4_128_sha", true);
 pref("security.ssl3.rsa_aes_128_sha", true);
 pref("security.ssl3.rsa_aes_256_sha", true);
 pref("security.ssl3.rsa_des_ede3_sha", true);
 pref("security.ssl3.rsa_rc4_128_sha", true);
 pref("security.ssl3.rsa_rc4_128_md5", true);
 
--- a/python/mozbuild/mozbuild/backend/common.py
+++ b/python/mozbuild/mozbuild/backend/common.py
@@ -1,26 +1,28 @@
 # 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/.
 
 from __future__ import unicode_literals
 
+import itertools
 import json
 import os
 import re
 
 import mozpack.path as mozpath
 
 from .base import BuildBackend
 
 from ..frontend.data import (
     ConfigFileSubstitution,
     ExampleWebIDLInterface,
     HeaderFileSubstitution,
+    IPDLFile,
     GeneratedEventWebIDLFile,
     GeneratedWebIDLFile,
     PreprocessedTestWebIDLFile,
     PreprocessedWebIDLFile,
     TestManifest,
     TestWebIDLFile,
     XPIDLFile,
     WebIDLFile,
@@ -167,16 +169,17 @@ class TestManager(object):
 class CommonBackend(BuildBackend):
     """Holds logic common to all build backends."""
 
     def _init(self):
         self._idl_manager = XPIDLManager(self.environment)
         self._test_manager = TestManager(self.environment)
         self._webidls = WebIDLCollection()
         self._configs = set()
+        self._ipdl_sources = set()
 
     def consume_object(self, obj):
         self._configs.add(obj.config)
 
         if isinstance(obj, TestManifest):
             for test in obj.tests:
                 self._test_manager.add(test, flavor=obj.flavor,
                     topsrcdir=obj.topsrcdir)
@@ -220,27 +223,48 @@ class CommonBackend(BuildBackend):
 
         elif isinstance(obj, PreprocessedWebIDLFile):
             self._webidls.preprocessed_sources.add(mozpath.join(
                 obj.srcdir, obj.basename))
 
         elif isinstance(obj, ExampleWebIDLInterface):
             self._webidls.example_interfaces.add(obj.name)
 
+        elif isinstance(obj, IPDLFile):
+            self._ipdl_sources.add(mozpath.join(obj.srcdir, obj.basename))
+
         else:
             return
 
         obj.ack()
 
     def consume_finished(self):
         if len(self._idl_manager.idls):
             self._handle_idl_manager(self._idl_manager)
 
         self._handle_webidl_collection(self._webidls)
 
+        sorted_ipdl_sources = list(sorted(self._ipdl_sources))
+
+        def files_from(ipdl):
+            base = mozpath.basename(ipdl)
+            root, ext = mozpath.splitext(base)
+
+            # Both .ipdl and .ipdlh become .cpp files
+            files = ['%s.cpp' % root]
+            if ext == '.ipdl':
+                # .ipdl also becomes Child/Parent.cpp files
+                files.extend(['%sChild.cpp' % root,
+                              '%sParent.cpp' % root])
+            return files
+
+        ipdl_cppsrcs = list(itertools.chain(*[files_from(p) for p in sorted_ipdl_sources]))
+
+        self._handle_ipdl_sources(sorted_ipdl_sources, ipdl_cppsrcs)
+
         for config in self._configs:
             self.backend_input_files.add(config.source)
 
         # Write out a machine-readable file describing every test.
         path = mozpath.join(self.environment.topobjdir, 'all-tests.json')
         with self._write_file(path) as fh:
             s = json.dumps(self._test_manager.tests_by_path)
             fh.write(s)
--- a/python/mozbuild/mozbuild/backend/recursivemake.py
+++ b/python/mozbuild/mozbuild/backend/recursivemake.py
@@ -40,17 +40,16 @@ from ..frontend.data import (
     FinalTargetFiles,
     GeneratedInclude,
     GeneratedSources,
     HostLibrary,
     HostProgram,
     HostSimpleProgram,
     HostSources,
     InstallationTarget,
-    IPDLFile,
     JARManifest,
     JavaJarData,
     JavaScriptModules,
     Library,
     LocalInclude,
     PerSourceFlag,
     Program,
     Resources,
@@ -272,17 +271,16 @@ class RecursiveMakeBackend(CommonBackend
     However, as long as there are Makefile.in files in the tree, we are tied to
     recursive make and thus will need this backend.
     """
 
     def _init(self):
         CommonBackend._init(self)
 
         self._backend_files = {}
-        self._ipdl_sources = set()
 
         def detailed(summary):
             s = '{:d} total backend files; ' \
                 '{:d} created; {:d} updated; {:d} unchanged; ' \
                 '{:d} deleted; {:d} -> {:d} Makefile'.format(
                 summary.created_count + summary.updated_count +
                 summary.unchanged_count,
                 summary.created_count,
@@ -446,19 +444,16 @@ class RecursiveMakeBackend(CommonBackend
             self._process_test_harness_files(obj, backend_file)
 
         elif isinstance(obj, Resources):
             self._process_resources(obj, obj.resources, backend_file)
 
         elif isinstance(obj, JARManifest):
             backend_file.write('JAR_MANIFEST := %s\n' % obj.path)
 
-        elif isinstance(obj, IPDLFile):
-            self._ipdl_sources.add(mozpath.join(obj.srcdir, obj.basename))
-
         elif isinstance(obj, Program):
             self._process_program(obj.program, backend_file)
             self._process_linked_libraries(obj, backend_file)
 
         elif isinstance(obj, HostProgram):
             self._process_host_program(obj.program, backend_file)
             self._process_linked_libraries(obj, backend_file)
 
@@ -745,46 +740,16 @@ class RecursiveMakeBackend(CommonBackend
                             continue
                         if t == b'tools' and not re.search('(?:^|\s)tools.*::', content, re.M):
                             continue
                         if objdir == self.environment.topobjdir:
                             continue
                         self._no_skip['tools'].add(mozpath.relpath(objdir,
                             self.environment.topobjdir))
 
-        # Write out a master list of all IPDL source files.
-        ipdl_dir = mozpath.join(self.environment.topobjdir, 'ipc', 'ipdl')
-        mk = Makefile()
-
-        sorted_ipdl_sources = list(sorted(self._ipdl_sources))
-        mk.add_statement('ALL_IPDLSRCS := %s' % ' '.join(sorted_ipdl_sources))
-
-        def files_from(ipdl):
-            base = mozpath.basename(ipdl)
-            root, ext = mozpath.splitext(base)
-
-            # Both .ipdl and .ipdlh become .cpp files
-            files = ['%s.cpp' % root]
-            if ext == '.ipdl':
-                # .ipdl also becomes Child/Parent.cpp files
-                files.extend(['%sChild.cpp' % root,
-                              '%sParent.cpp' % root])
-            return files
-
-        ipdl_cppsrcs = list(itertools.chain(*[files_from(p) for p in sorted_ipdl_sources]))
-        self._add_unified_build_rules(mk, ipdl_cppsrcs, ipdl_dir,
-                                      unified_prefix='UnifiedProtocols',
-                                      unified_files_makefile_variable='CPPSRCS')
-
-        mk.add_statement('IPDLDIRS := %s' % ' '.join(sorted(set(mozpath.dirname(p)
-            for p in self._ipdl_sources))))
-
-        with self._write_file(mozpath.join(ipdl_dir, 'ipdlsrcs.mk')) as ipdls:
-            mk.dump(ipdls, removal_guard=False)
-
         self._fill_root_mk()
 
         # Write out a dependency file used to determine whether a config.status
         # re-run is needed.
         inputs = sorted(p.replace(os.sep, '/') for p in self.backend_input_files)
 
         # We need to use $(DEPTH) so the target here matches what's in
         # rules.mk. If they are different, the dependencies don't get pulled in
@@ -1327,16 +1292,33 @@ INSTALL_TARGETS += %(prefix)s
             # that if the Makefile.in disappears, this will force
             # moz.build traversal. This means that when we remove empty
             # Makefile.in files, the old file will get replaced with
             # the autogenerated one automatically.
             self.backend_input_files.add(obj.input_path)
 
         self.summary.makefile_out_count += 1
 
+    def _handle_ipdl_sources(self, sorted_ipdl_sources, ipdl_cppsrcs):
+        # Write out a master list of all IPDL source files.
+        ipdl_dir = mozpath.join(self.environment.topobjdir, 'ipc', 'ipdl')
+        mk = Makefile()
+
+        mk.add_statement('ALL_IPDLSRCS := %s' % ' '.join(sorted_ipdl_sources))
+
+        self._add_unified_build_rules(mk, ipdl_cppsrcs, ipdl_dir,
+                                      unified_prefix='UnifiedProtocols',
+                                      unified_files_makefile_variable='CPPSRCS')
+
+        mk.add_statement('IPDLDIRS := %s' % ' '.join(sorted(set(mozpath.dirname(p)
+            for p in self._ipdl_sources))))
+
+        with self._write_file(mozpath.join(ipdl_dir, 'ipdlsrcs.mk')) as ipdls:
+            mk.dump(ipdls, removal_guard=False)
+
     def _handle_webidl_collection(self, webidls):
         if not webidls.all_stems():
             return
 
         bindings_dir = mozpath.join(self.environment.topobjdir, 'dom',
             'bindings')
 
         all_inputs = set(webidls.all_static_sources())
--- a/python/mozbuild/mozbuild/backend/visualstudio.py
+++ b/python/mozbuild/mozbuild/backend/visualstudio.py
@@ -96,26 +96,26 @@ class VisualStudioBackend(CommonBackend)
         obj.ack()
 
         reldir = getattr(obj, 'relativedir', None)
 
         if hasattr(obj, 'config') and reldir not in self._paths_to_configs:
             self._paths_to_configs[reldir] = obj.config
 
         if isinstance(obj, Sources):
-            self._add_sources(self, reldir, obj)
+            self._add_sources(reldir, obj)
 
         elif isinstance(obj, HostSources):
-            self._add_sources(self, reldir, obj)
+            self._add_sources(reldir, obj)
 
         elif isinstance(obj, GeneratedSources):
-            self._add_sources(self, reldir, obj)
+            self._add_sources(reldir, obj)
 
         elif isinstance(obj, UnifiedSources):
-            self._addr_sources(self, reldir, obj)
+            self._add_sources(reldir, obj)
 
         elif isinstance(obj, Library):
             self._libs_to_paths[obj.basename] = reldir
 
         elif isinstance(obj, Defines):
             self._paths_to_defines.setdefault(reldir, {}).update(obj.defines)
 
         elif isinstance(obj, LocalInclude):
--- a/security/manager/ssl/src/SSLServerCertVerification.cpp
+++ b/security/manager/ssl/src/SSLServerCertVerification.cpp
@@ -1104,17 +1104,17 @@ AuthCertificate(CertVerifier& certVerifi
 
   // We want to remember the CA certs in the temp db, so that the application can find the
   // complete chain at any time it might need it.
   // But we keep only those CA certs in the temp db, that we didn't already know.
 
   RefPtr<nsSSLStatus> status(infoObject->SSLStatus());
   RefPtr<nsNSSCertificate> nsc;
 
-  if (!status || !status->mServerCert) {
+  if (!status || !status->HasServerCert()) {
     if( rv == SECSuccess ){
       nsc = nsNSSCertificate::Create(cert, &evOidPolicy);
     }
     else {
       nsc = nsNSSCertificate::Create(cert);
     }
   }
 
@@ -1136,20 +1136,27 @@ AuthCertificate(CertVerifier& certVerifi
                                                                   nullptr, rv);
     }
     else {
       // Certificate verification failed, update the status' bits.
       RememberCertErrorsTable::GetInstance().LookupCertErrorBits(
         infoObject, status);
     }
 
-    if (status && !status->mServerCert) {
-      status->mServerCert = nsc;
+    if (status && !status->HasServerCert()) {
+      nsNSSCertificate::EVStatus evStatus;
+      if (evOidPolicy == SEC_OID_UNKNOWN || rv != SECSuccess) {
+        evStatus = nsNSSCertificate::ev_status_invalid;
+      } else {
+        evStatus = nsNSSCertificate::ev_status_valid;
+      }
+
+      status->SetServerCert(nsc, evStatus);
       PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
-             ("AuthCertificate setting NEW cert %p\n", status->mServerCert.get()));
+             ("AuthCertificate setting NEW cert %p\n", nsc.get()));
     }
   }
 
   if (rv != SECSuccess) {
     // Certificate validation failed; store the peer certificate chain on
     // infoObject so it can be used for error reporting. Note: infoObject
     // indirectly takes ownership of peerCertChain.
     infoObject->SetFailedCertChain(peerCertChain);
--- a/security/manager/ssl/src/TransportSecurityInfo.cpp
+++ b/security/manager/ssl/src/TransportSecurityInfo.cpp
@@ -237,21 +237,21 @@ TransportSecurityInfo::formatErrorMessag
   if (errorCode == 0) {
     result.Truncate();
     return NS_OK;
   }
 
   nsresult rv;
   NS_ConvertASCIItoUTF16 hostNameU(mHostName);
   NS_ASSERTION(errorMessageType != OverridableCertErrorMessage || 
-                (mSSLStatus && mSSLStatus->mServerCert &&
+                (mSSLStatus && mSSLStatus->HasServerCert() &&
                  mSSLStatus->mHaveCertErrorBits),
                 "GetErrorLogMessage called for cert error without cert");
   if (errorMessageType == OverridableCertErrorMessage && 
-      mSSLStatus && mSSLStatus->mServerCert) {
+      mSSLStatus && mSSLStatus->HasServerCert()) {
     rv = formatOverridableCertErrorMessage(*mSSLStatus, errorCode,
                                            mHostName, mPort,
                                            suppressPort443,
                                            wantsHtml,
                                            result);
   } else {
     rv = formatPlainErrorMessage(mHostName, mPort, 
                                  errorCode,
@@ -291,18 +291,18 @@ TransportSecurityInfo::GetInterface(cons
   }
   return rv;
 }
 
 // This is a new magic value. However, it re-uses the first 4 bytes
 // of the previous value. This is so when older versions attempt to
 // read a newer serialized TransportSecurityInfo, they will actually
 // fail and return NS_ERROR_FAILURE instead of silently failing.
-#define TRANSPORTSECURITYINFOMAGIC { 0xa9863a23, 0x2429, 0x4866, \
-  { 0x92, 0x89, 0x45, 0x51, 0xc2, 0x01, 0xca, 0xf2 } }
+#define TRANSPORTSECURITYINFOMAGIC { 0xa9863a23, 0x328f, 0x45ab, \
+  { 0xa8, 0xa4, 0x35, 0x18, 0x80, 0x04, 0x77, 0x8d } }
 static NS_DEFINE_CID(kTransportSecurityInfoMagic, TRANSPORTSECURITYINFOMAGIC);
 
 NS_IMETHODIMP
 TransportSecurityInfo::Write(nsIObjectOutputStream* stream)
 {
   nsresult rv = stream->WriteID(kTransportSecurityInfoMagic);
   if (NS_FAILED(rv)) {
     return rv;
@@ -1084,17 +1084,17 @@ void
 TransportSecurityInfo::SetStatusErrorBits(nsIX509Cert & cert,
                                           uint32_t collected_errors)
 {
   MutexAutoLock lock(mMutex);
 
   if (!mSSLStatus)
     mSSLStatus = new nsSSLStatus();
 
-  mSSLStatus->mServerCert = &cert;
+  mSSLStatus->SetServerCert(&cert, nsNSSCertificate::ev_status_invalid);
 
   mSSLStatus->mHaveCertErrorBits = true;
   mSSLStatus->mIsDomainMismatch = 
     collected_errors & nsICertOverrideService::ERROR_MISMATCH;
   mSSLStatus->mIsNotValidAtThisTime = 
     collected_errors & nsICertOverrideService::ERROR_TIME;
   mSSLStatus->mIsUntrusted = 
     collected_errors & nsICertOverrideService::ERROR_UNTRUSTED;
--- a/security/manager/ssl/src/nsNSSCallbacks.cpp
+++ b/security/manager/ssl/src/nsNSSCallbacks.cpp
@@ -1283,25 +1283,25 @@ void HandshakeCallback(PRFileDesc* fd, v
     if (NS_FAILED(rv)) {
       equals_previous = false;
     }
   }
 
   if (equals_previous) {
     PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
             ("HandshakeCallback using PREV cert %p\n", prevcert.get()));
-    status->mServerCert = prevcert;
+    status->SetServerCert(prevcert, nsNSSCertificate::ev_status_unknown);
   }
   else {
-    if (status->mServerCert) {
+    if (status->HasServerCert()) {
       PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
-              ("HandshakeCallback KEEPING cert %p\n", status->mServerCert.get()));
+              ("HandshakeCallback KEEPING existing cert\n"));
     }
     else {
       PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
               ("HandshakeCallback using NEW cert %p\n", nssc.get()));
-      status->mServerCert = nssc;
+      status->SetServerCert(nssc, nsNSSCertificate::ev_status_unknown);
     }
   }
 
   infoObject->NoteTimeUntilReady();
   infoObject->SetHandshakeCompleted();
 }
--- a/security/manager/ssl/src/nsNSSCertificate.h
+++ b/security/manager/ssl/src/nsNSSCertificate.h
@@ -44,35 +44,39 @@ public:
   nsNSSCertificate();
   nsresult FormatUIStrings(const nsAutoString& nickname,
                            nsAutoString& nickWithSerial,
                            nsAutoString& details);
   static nsNSSCertificate* Create(CERTCertificate*cert = nullptr,
                                   SECOidTag* evOidPolicy = nullptr);
   static nsNSSCertificate* ConstructFromDER(char* certDER, int derLen);
 
+  enum EVStatus {
+    ev_status_invalid = 0,
+    ev_status_valid = 1,
+    ev_status_unknown = 2
+  };
+
 private:
   virtual ~nsNSSCertificate();
 
   mozilla::ScopedCERTCertificate mCert;
   bool             mPermDelete;
   uint32_t         mCertType;
   nsresult CreateASN1Struct(nsIASN1Object** aRetVal);
   nsresult CreateTBSCertificateASN1Struct(nsIASN1Sequence** retSequence,
                                           nsINSSComponent* nssComponent);
   nsresult GetSortableDate(PRTime aTime, nsAString& _aSortableDate);
   virtual void virtualDestroyNSSReference();
   void destructorSafeDestroyNSSReference();
   bool InitFromDER(char* certDER, int derLen);  // return false on failure
 
   nsresult GetCertificateHash(nsAString& aFingerprint, SECOidTag aHashAlg);
 
-  enum {
-    ev_status_invalid = 0, ev_status_valid = 1, ev_status_unknown = 2
-  } mCachedEVStatus;
+  EVStatus mCachedEVStatus;
   SECOidTag mCachedEVOidTag;
   nsresult hasValidEVOidTag(SECOidTag& resultOidTag, bool& validEV);
   nsresult getValidEVOidTag(SECOidTag& resultOidTag, bool& validEV);
 };
 
 namespace mozilla {
 
 SECStatus ConstructCERTCertListFromReversedDERArray(
--- a/security/manager/ssl/src/nsNSSComponent.cpp
+++ b/security/manager/ssl/src/nsNSSComponent.cpp
@@ -645,19 +645,16 @@ static const CipherPref sCipherPrefs[] =
    TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, true },
 
  { "security.ssl3.dhe_rsa_aes_128_sha",
    TLS_DHE_RSA_WITH_AES_128_CBC_SHA, true },
 
  { "security.ssl3.dhe_rsa_aes_256_sha",
    TLS_DHE_RSA_WITH_AES_256_CBC_SHA, true },
 
- { "security.ssl3.dhe_dss_aes_128_sha",
-   TLS_DHE_DSS_WITH_AES_128_CBC_SHA, false }, // deprecated (DSS)
-
  { "security.ssl3.ecdhe_rsa_rc4_128_sha",
    TLS_ECDHE_RSA_WITH_RC4_128_SHA, true, true }, // deprecated (RC4)
  { "security.ssl3.ecdhe_ecdsa_rc4_128_sha",
    TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, true, true }, // deprecated (RC4)
 
  { "security.ssl3.rsa_aes_128_sha",
    TLS_RSA_WITH_AES_128_CBC_SHA, true }, // deprecated (RSA key exchange)
  { "security.ssl3.rsa_aes_256_sha",
--- a/security/manager/ssl/src/nsNSSIOLayer.cpp
+++ b/security/manager/ssl/src/nsNSSIOLayer.cpp
@@ -393,18 +393,19 @@ nsNSSSocketInfo::IsAcceptableForHost(con
   // need to be considered. They are joinable.
   if (hostname.Equals(GetHostName())) {
     *_retval = true;
     return NS_OK;
   }
 
   // Before checking the server certificate we need to make sure the
   // handshake has completed.
-  if (!mHandshakeCompleted || !SSLStatus() || !SSLStatus()->mServerCert)
+  if (!mHandshakeCompleted || !SSLStatus() || !SSLStatus()->HasServerCert()) {
     return NS_OK;
+  }
 
   // If the cert has error bits (e.g. it is untrusted) then do not join.
   // The value of mHaveCertErrorBits is only reliable because we know that
   // the handshake completed.
   if (SSLStatus()->mHaveCertErrorBits)
     return NS_OK;
 
   // If the connection is using client certificates then do not join
@@ -413,17 +414,20 @@ nsNSSSocketInfo::IsAcceptableForHost(con
   if (mSentClientCert)
     return NS_OK;
 
   // Ensure that the server certificate covers the hostname that would
   // like to join this connection
 
   ScopedCERTCertificate nssCert;
 
-  nsCOMPtr<nsIX509Cert> cert(SSLStatus()->mServerCert);
+  nsCOMPtr<nsIX509Cert> cert;
+  if (NS_FAILED(SSLStatus()->GetServerCert(getter_AddRefs(cert)))) {
+    return NS_OK;
+  }
   if (cert) {
     nssCert = cert->GetCert();
   }
 
   if (!nssCert) {
     return NS_OK;
   }
 
--- a/security/manager/ssl/src/nsSSLStatus.cpp
+++ b/security/manager/ssl/src/nsSSLStatus.cpp
@@ -116,38 +116,30 @@ nsSSLStatus::GetIsUntrusted(bool* aIsUnt
 }
 
 NS_IMETHODIMP
 nsSSLStatus::GetIsExtendedValidation(bool* aIsEV)
 {
   NS_ENSURE_ARG_POINTER(aIsEV);
   *aIsEV = false;
 
-#ifdef MOZ_NO_EV_CERTS
-  return NS_OK;
-#else
-  nsCOMPtr<nsIX509Cert> cert = mServerCert;
-  // mServerCert should never be null when this method is called because
-  // nsSSLStatus objects always have mServerCert set right after they are
-  // constructed and before they are returned. GetIsExtendedValidation should
-  // only be called in the chrome process (in e10s), and mServerCert will always
-  // implement nsIIdentityInfo in the chrome process.
-  nsCOMPtr<nsIIdentityInfo> idinfo = do_QueryInterface(cert);
-  if (!idinfo) {
-    NS_ERROR("nsSSLStatus has null mServerCert or was called in the content "
-             "process");
-    return NS_ERROR_UNEXPECTED;
-  }
-
   // Never allow bad certs for EV, regardless of overrides.
   if (mHaveCertErrorBits) {
     return NS_OK;
   }
 
-  return idinfo->GetIsExtendedValidation(aIsEV);
+  if (mHasIsEVStatus) {
+    *aIsEV = mIsEV;
+    return NS_OK;
+  }
+
+#ifdef MOZ_NO_EV_CERTS
+  return NS_OK;
+#else
+  return NS_ERROR_NOT_AVAILABLE;
 #endif
 }
 
 NS_IMETHODIMP
 nsSSLStatus::Read(nsIObjectInputStream* aStream)
 {
   nsCOMPtr<nsISupports> cert;
   nsresult rv = aStream->ReadObject(true, getter_AddRefs(cert));
@@ -164,17 +156,21 @@ nsSSLStatus::Read(nsIObjectInputStream* 
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = aStream->ReadBoolean(&mIsDomainMismatch);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = aStream->ReadBoolean(&mIsNotValidAtThisTime);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = aStream->ReadBoolean(&mIsUntrusted);
   NS_ENSURE_SUCCESS(rv, rv);
+  rv = aStream->ReadBoolean(&mIsEV);
+  NS_ENSURE_SUCCESS(rv, rv);
 
+  rv = aStream->ReadBoolean(&mHasIsEVStatus);
+  NS_ENSURE_SUCCESS(rv, rv);
   rv = aStream->ReadBoolean(&mHaveCipherSuiteAndProtocol);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = aStream->ReadBoolean(&mHaveCertErrorBits);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
@@ -192,17 +188,21 @@ nsSSLStatus::Write(nsIObjectOutputStream
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = aStream->WriteBoolean(mIsDomainMismatch);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = aStream->WriteBoolean(mIsNotValidAtThisTime);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = aStream->WriteBoolean(mIsUntrusted);
   NS_ENSURE_SUCCESS(rv, rv);
+  rv = aStream->WriteBoolean(mIsEV);
+  NS_ENSURE_SUCCESS(rv, rv);
 
+  rv = aStream->WriteBoolean(mHasIsEVStatus);
+  NS_ENSURE_SUCCESS(rv, rv);
   rv = aStream->WriteBoolean(mHaveCipherSuiteAndProtocol);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = aStream->WriteBoolean(mHaveCertErrorBits);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
@@ -269,18 +269,43 @@ nsSSLStatus::GetClassIDNoAlloc(nsCID* aC
 }
 
 nsSSLStatus::nsSSLStatus()
 : mCipherSuite(0)
 , mProtocolVersion(0)
 , mIsDomainMismatch(false)
 , mIsNotValidAtThisTime(false)
 , mIsUntrusted(false)
+, mIsEV(false)
+, mHasIsEVStatus(false)
 , mHaveCipherSuiteAndProtocol(false)
 , mHaveCertErrorBits(false)
 {
 }
 
 NS_IMPL_ISUPPORTS(nsSSLStatus, nsISSLStatus, nsISerializable, nsIClassInfo)
 
 nsSSLStatus::~nsSSLStatus()
 {
 }
+
+void
+nsSSLStatus::SetServerCert(nsIX509Cert* aServerCert, nsNSSCertificate::EVStatus aEVStatus)
+{
+  mServerCert = aServerCert;
+
+  if (aEVStatus != nsNSSCertificate::ev_status_unknown) {
+    mIsEV = (aEVStatus == nsNSSCertificate::ev_status_valid);
+    mHasIsEVStatus = true;
+    return;
+  }
+
+#ifndef MOZ_NO_EV_CERTS
+  nsCOMPtr<nsIIdentityInfo> idinfo = do_QueryInterface(mServerCert);
+  if (idinfo) {
+    nsresult rv = idinfo->GetIsExtendedValidation(&mIsEV);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return;
+    }
+    mHasIsEVStatus = true;
+  }
+#endif
+}
--- a/security/manager/ssl/src/nsSSLStatus.h
+++ b/security/manager/ssl/src/nsSSLStatus.h
@@ -8,46 +8,56 @@
 #define _NSSSLSTATUS_H
 
 #include "nsISSLStatus.h"
 #include "nsCOMPtr.h"
 #include "nsXPIDLString.h"
 #include "nsIX509Cert.h"
 #include "nsISerializable.h"
 #include "nsIClassInfo.h"
+#include "nsNSSCertificate.h" // For EVStatus
 
 class nsSSLStatus MOZ_FINAL
   : public nsISSLStatus
   , public nsISerializable
   , public nsIClassInfo
 {
 protected:
   virtual ~nsSSLStatus();
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSISSLSTATUS
   NS_DECL_NSISERIALIZABLE
   NS_DECL_NSICLASSINFO
 
   nsSSLStatus();
 
+  void SetServerCert(nsIX509Cert* aServerCert, nsNSSCertificate::EVStatus aEVStatus);
+
+  bool HasServerCert() {
+    return mServerCert != nullptr;
+  }
+
   /* public for initilization in this file */
-  nsCOMPtr<nsIX509Cert> mServerCert;
-
   uint16_t mCipherSuite;
   uint16_t mProtocolVersion;
 
   bool mIsDomainMismatch;
   bool mIsNotValidAtThisTime;
   bool mIsUntrusted;
+  bool mIsEV;
 
+  bool mHasIsEVStatus;
   bool mHaveCipherSuiteAndProtocol;
 
   /* mHaveCertErrrorBits is relied on to determine whether or not a SPDY
      connection is eligible for joining in nsNSSSocketInfo::JoinConnection() */
   bool mHaveCertErrorBits;
+
+private:
+  nsCOMPtr<nsIX509Cert> mServerCert;
 };
 
 #define NS_SSLSTATUS_CID \
-{ 0x61f69c85, 0x0fed, 0x44fb, \
-  { 0x89, 0x8f, 0xa4, 0xb1, 0x3c, 0x33, 0x3c, 0x8d } }
+{ 0xe2f14826, 0x9e70, 0x4647, \
+  { 0xb2, 0x3f, 0x10, 0x10, 0xf5, 0x12, 0x46, 0x28 } }
 
 #endif
--- a/security/pkix/include/pkix/ScopedPtr.h
+++ b/security/pkix/include/pkix/ScopedPtr.h
@@ -26,17 +26,17 @@
 #define mozilla_pkix__ScopedPtr_h
 
 #include "pkix/nullptr.h"
 
 namespace mozilla { namespace pkix {
 
 // Similar to boost::scoped_ptr and std::unique_ptr. Does not support copying
 // or assignment.
-template <typename T, void (*Destroyer)(T*)>
+template <typename T, void (&Destroyer)(T*)>
 class ScopedPtr
 {
 public:
   explicit ScopedPtr(T* value = nullptr) : mValue(value) { }
   ~ScopedPtr()
   {
     if (mValue) {
       Destroyer(mValue);
@@ -68,38 +68,38 @@ public:
 
 protected:
   T* mValue;
 
   ScopedPtr(const ScopedPtr&) /* = delete */;
   void operator=(const ScopedPtr&) /* = delete */;
 };
 
-template <typename T, void(*Destroyer)(T*)>
+template <typename T, void(&Destroyer)(T*)>
 inline bool
 operator==(T* a, const ScopedPtr<T, Destroyer>& b)
 {
   return a == b.get();
 }
 
-template <typename T, void(*Destroyer)(T*)>
+template <typename T, void(&Destroyer)(T*)>
 inline bool
 operator==(const ScopedPtr<T, Destroyer>& a, T* b)
 {
   return a.get() == b;
 }
 
-template <typename T, void(*Destroyer)(T*)>
+template <typename T, void(&Destroyer)(T*)>
 inline bool
 operator!=(T* a, const ScopedPtr<T, Destroyer>& b)
 {
   return a != b.get();
 }
 
-template <typename T, void(*Destroyer)(T*)>
+template <typename T, void(&Destroyer)(T*)>
 inline bool
 operator!=(const ScopedPtr<T, Destroyer>& a, T* b)
 {
   return a.get() != b;
 }
 
 } } // namespace mozilla::pkix
 
--- a/security/pkix/include/pkix/bind.h
+++ b/security/pkix/include/pkix/bind.h
@@ -69,152 +69,161 @@ template <typename V>       V&  ref(V& v
 template <typename V> const V& cref(const V& v) { return v; }
 
 namespace internal {
 
 template <typename R, typename P1, typename B1>
 class Bind1
 {
 public:
-  typedef R (*F)(P1 &, B1 &);
-  Bind1(F f, B1 & b1) : f(f), b1(b1) { }
-  R operator()(P1 & p1) const { return f(p1, b1); }
+  typedef R (&F)(P1&, B1&);
+  Bind1(F f, B1& b1) : f(f), b1(b1) { }
+  R operator()(P1& p1) const { return f(p1, b1); }
 private:
-  const F f;
+  F f;
   B1& b1;
   void operator=(const Bind1&) /*= delete*/;
 };
 
 template <typename R, typename P1, typename B1, typename B2>
 class Bind2
 {
 public:
-  typedef R (*F)(P1&, B1&, B2&);
+  typedef R (&F)(P1&, B1&, B2&);
   Bind2(F f, B1& b1, B2& b2) : f(f), b1(b1), b2(b2) { }
   R operator()(P1& p1) const { return f(p1, b1, b2); }
 private:
-  const F f;
+  F f;
   B1& b1;
   B2& b2;
   void operator=(const Bind2&) /*= delete*/;
 };
 
 template <typename R, typename P1, typename B1, typename B2, typename B3>
 class Bind3
 {
 public:
-  typedef R (*F)(P1&, B1, B2, B3&);
+  typedef R (&F)(P1&, B1, B2, B3&);
   Bind3(F f, B1& b1, B2& b2, B3& b3)
     : f(f), b1(b1), b2(b2), b3(b3) { }
   R operator()(P1& p1) const { return f(p1, b1, b2, b3); }
 private:
-  const F f;
+  F f;
   B1& b1;
   B2& b2;
   B3& b3;
   void operator=(const Bind3&) /*= delete*/;
 };
 
 template <typename R, typename P1, typename B1, typename B2, typename B3,
           typename B4>
 class Bind4
 {
 public:
-  typedef R (*F)(P1&, B1, B2, B3&, B4&);
+  typedef R (&F)(P1&, B1, B2, B3&, B4&);
   Bind4(F f, B1& b1, B2& b2, B3& b3, B4& b4)
     : f(f), b1(b1), b2(b2), b3(b3), b4(b4) { }
   R operator()(P1& p1) const { return f(p1, b1, b2, b3, b4); }
 private:
-  const F f;
+  F f;
   B1& b1;
   B2& b2;
   B3& b3;
   B4& b4;
   void operator=(const Bind4&) /*= delete*/;
 };
 
 template <typename R, typename C1, typename P1, typename P2, typename P3,
           typename P4>
 class BindToMemberFunction4
 {
 public:
+  // XXX: C++ doesn't have reference-to-member function, only
+  // pointer-to-member function, so we can't enforce the non-nullness of f
+  // using the type system.
   typedef R (C1::*F)(P1&, P2&, P3, P4&);
-  BindToMemberFunction4(F f, C1* that) : f(f), that(that) { }
-  R operator()(P1& p1, P2& p2, P3 p3, P4& p4) const { return (that->*f)(p1, p2, p3, p4); }
+  BindToMemberFunction4(F f, C1& that) : f(f), that(that) { }
+  R operator()(P1& p1, P2& p2, P3 p3, P4& p4) const
+  {
+    return (that.*f)(p1, p2, p3, p4);
+  }
 private:
   const F f;
-  C1* const that;
+  C1& that;
   void operator=(const BindToMemberFunction4&) /*= delete*/;
 };
 
 template <typename R, typename P1, typename B1, typename B2, typename B3,
           typename B4, typename B5>
 class Bind5
 {
 public:
-  typedef R (*F)(P1&, B1, B2, B3, B4, B5);
+  typedef R (&F)(P1&, B1, B2, B3, B4, B5);
   Bind5(F f, B1 b1, B2 b2, B3 b3, B4 b4, B5 b5)
     : f(f), b1(b1), b2(b2), b3(b3), b4(b4), b5(b5) { }
   R operator()(P1& p1) const { return f(p1, b1, b2, b3, b4, b5); }
 private:
-  const F f;
+  F f;
   B1 b1;
   B2 b2;
   B3 b3;
   B4 b4;
   B5 b5;
   void operator=(const Bind5&) /*= delete*/;
 };
 
 } // namespace internal
 
 template <typename R, typename P1, typename B1>
 inline internal::Bind1<R, P1, B1>
-bind(R (*f)(P1&, B1&), Placeholder1&, B1& b1)
+bind(R (&f)(P1&, B1&), Placeholder1&, B1& b1)
 {
   return internal::Bind1<R, P1, B1>(f, b1);
 }
 
 template <typename R, typename P1, typename B1, typename B2>
 inline internal::Bind2<R, P1, B1, B2>
-bind(R (*f)(P1&, B1&, B2&), Placeholder1&, B1& b1, B2& b2)
+bind(R (&f)(P1&, B1&, B2&), Placeholder1&, B1& b1, B2& b2)
 {
   return internal::Bind2<R, P1, B1, B2>(f, b1, b2);
 }
 
 template <typename R, typename P1, typename B1, typename B2, typename B3>
 inline internal::Bind3<R, P1, const B1, const B2, B3>
-bind(R (*f)(P1&, B1, B2, B3&), Placeholder1&, const B1& b1, const B2& b2,
+bind(R (&f)(P1&, B1, B2, B3&), Placeholder1&, const B1& b1, const B2& b2,
      B3& b3)
 {
   return internal::Bind3<R, P1, const B1, const B2, B3>(f, b1, b2, b3);
 }
 
 template <typename R, typename P1, typename B1, typename B2, typename B3,
           typename B4>
 inline internal::Bind4<R, P1, const B1, const B2, B3, B4>
-bind(R (*f)(P1&, B1, B2, B3&, B4&), Placeholder1&, const B1& b1, const B2& b2,
+bind(R (&f)(P1&, B1, B2, B3&, B4&), Placeholder1&, const B1& b1, const B2& b2,
      B3& b3, B4& b4)
 {
   return internal::Bind4<R, P1, const B1, const B2, B3, B4>(f, b1, b2, b3, b4);
 }
 
+// XXX: C++ doesn't have reference-to-member function, only
+// pointer-to-member function, so we can't enforce the non-nullness of f
+// using the type system.
 template <typename R, typename C1, typename P1, typename P2, typename P3,
           typename P4>
 inline internal::BindToMemberFunction4<R, C1, P1, P2, P3, P4>
-bind(R (C1::*f)(P1&, P2&, P3, P4&), C1* that, Placeholder1&, Placeholder2&,
-     Placeholder3, Placeholder4&)
+bind(R (C1::*f)(P1&, P2&, P3, P4&), C1& that, Placeholder1&,
+     Placeholder2&, Placeholder3, Placeholder4&)
 {
   return internal::BindToMemberFunction4<R, C1, P1, P2, P3, P4>(f, that);
 }
 
 template <typename R, typename P1, typename B1, typename B2, typename B3,
           typename B4, typename B5>
 inline internal::Bind5<R, P1, B1, B2, B3, B4, B5&>
-bind(R (*f)(P1&, B1, B2, B3, B4, B5&), Placeholder1&, B1 b1, B2 b2, B3 b3,
+bind(R (&f)(P1&, B1, B2, B3, B4, B5&), Placeholder1&, B1 b1, B2 b2, B3 b3,
      B4 b4, B5& b5)
 {
   return internal::Bind5<R, P1, B1, B2, B3, B4, B5&>(f, b1, b2, b3, b4, b5);
 }
 
 #endif
 
 } } // namespace mozilla::pkix
--- a/security/pkix/lib/pkixcert.cpp
+++ b/security/pkix/lib/pkixcert.cpp
@@ -136,17 +136,17 @@ BackCert::Init()
   if (tbsCertificate.Peek(CSC | 2)) {
     rv = der::ExpectTagAndSkipValue(tbsCertificate, CSC | 2);
     if (rv != Success) {
       return rv;
     }
   }
 
   rv = der::OptionalExtensions(tbsCertificate, CSC | 3,
-                               bind(&BackCert::RememberExtension, this, _1,
+                               bind(&BackCert::RememberExtension, *this, _1,
                                     _2, _3, _4));
   if (rv != Success) {
     return rv;
   }
 
   // The Netscape Certificate Type extension is an obsolete
   // Netscape-proprietary mechanism that we ignore in favor of the standard
   // extensions. However, some CAs have issued certificates with the Netscape
--- a/security/pkix/test/gtest/pkixbuild_tests.cpp
+++ b/security/pkix/test/gtest/pkixbuild_tests.cpp
@@ -17,35 +17,31 @@
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
+#include <map>
 #include "cert.h"
-#include "nss.h"
 #include "pkix/pkix.h"
-#include "pkix/pkixnss.h"
 #include "pkixgtest.h"
 #include "pkixtestutil.h"
 
 using namespace mozilla::pkix;
 using namespace mozilla::pkix::test;
 
-typedef ScopedPtr<CERTCertificate, CERT_DestroyCertificate>
-          ScopedCERTCertificate;
-typedef ScopedPtr<CERTCertList, CERT_DestroyCertList> ScopedCERTCertList;
-
 static ByteString
 CreateCert(const char* issuerCN,
            const char* subjectCN,
            EndEntityOrCA endEntityOrCA,
-           /*out*/ ScopedCERTCertificate* subjectCert = nullptr)
+           /*optional modified*/ std::map<ByteString, ByteString>*
+             subjectDERToCertDER = nullptr)
 {
   static long serialNumberValue = 0;
   ++serialNumberValue;
   ByteString serialNumber(CreateEncodedSerialNumber(serialNumberValue));
   EXPECT_FALSE(ENCODING_FAILED(serialNumber));
 
   ByteString issuerDER(CNToDERName(issuerCN));
   ByteString subjectDER(CNToDERName(subjectCN));
@@ -60,101 +56,83 @@ CreateCert(const char* issuerCN,
 
   ScopedTestKeyPair reusedKey(CloneReusedKeyPair());
   ByteString certDER(CreateEncodedCertificate(
                        v3, sha256WithRSAEncryption, serialNumber, issuerDER,
                        oneDayBeforeNow, oneDayAfterNow, subjectDER,
                        *reusedKey, extensions, *reusedKey,
                        sha256WithRSAEncryption));
   EXPECT_FALSE(ENCODING_FAILED(certDER));
-  if (subjectCert) {
-    SECItem certDERItem = {
-      siBuffer,
-      const_cast<uint8_t*>(certDER.data()),
-      static_cast<unsigned int>(certDER.length())
-    };
-    *subjectCert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
-                                           &certDERItem, nullptr, false, true);
-    EXPECT_TRUE(*subjectCert);
+
+  if (subjectDERToCertDER) {
+    (*subjectDERToCertDER)[subjectDER] = certDER;
   }
+
   return certDER;
 }
 
 class TestTrustDomain : public TrustDomain
 {
 public:
   // The "cert chain tail" is a longish chain of certificates that is used by
   // all of the tests here. We share this chain across all the tests in order
   // to speed up the tests (generating keypairs for the certs is very slow).
   bool SetUpCertChainTail()
   {
     static char const* const names[] = {
         "CA1 (Root)", "CA2", "CA3", "CA4", "CA5", "CA6", "CA7"
     };
 
-    static_assert(MOZILLA_PKIX_ARRAY_LENGTH(names) ==
-                    MOZILLA_PKIX_ARRAY_LENGTH(certChainTail),
-                  "mismatch in sizes of names and certChainTail arrays");
-
     for (size_t i = 0; i < MOZILLA_PKIX_ARRAY_LENGTH(names); ++i) {
       const char* issuerName = i == 0 ? names[0] : names[i-1];
-      (void) CreateCert(issuerName, names[i], EndEntityOrCA::MustBeCA,
-                        &certChainTail[i]);
+      CreateCACert(issuerName, names[i]);
+      if (i == 0) {
+        rootCACertDER = leafCACertDER;
+      }
     }
 
     return true;
   }
 
+  void CreateCACert(const char* issuerName, const char* subjectName)
+  {
+    leafCACertDER = CreateCert(issuerName, subjectName,
+                               EndEntityOrCA::MustBeCA, &subjectDERToCertDER);
+    assert(!ENCODING_FAILED(leafCACertDER));
+  }
+
+  ByteString GetLeafCACertDER() const { return leafCACertDER; }
+
 private:
   virtual Result GetCertTrust(EndEntityOrCA, const CertPolicyId&,
                               Input candidateCert,
                               /*out*/ TrustLevel& trustLevel)
   {
-    Input rootDER;
-    Result rv = rootDER.Init(certChainTail[0]->derCert.data,
-                             certChainTail[0]->derCert.len);
-    EXPECT_EQ(Success, rv);
-    if (InputsAreEqual(candidateCert, rootDER)) {
-      trustLevel = TrustLevel::TrustAnchor;
-    } else {
-      trustLevel = TrustLevel::InheritsTrust;
-    }
+    trustLevel = InputEqualsByteString(candidateCert, rootCACertDER)
+               ? TrustLevel::TrustAnchor
+               : TrustLevel::InheritsTrust;
     return Success;
   }
 
   virtual Result FindIssuer(Input encodedIssuerName,
                             IssuerChecker& checker, Time time)
   {
-    SECItem encodedIssuerNameSECItem =
-      UnsafeMapInputToSECItem(encodedIssuerName);
-    ScopedCERTCertList
-      candidates(CERT_CreateSubjectCertList(nullptr, CERT_GetDefaultCertDB(),
-                                            &encodedIssuerNameSECItem, 0,
-                                            false));
-    if (candidates) {
-      for (CERTCertListNode* n = CERT_LIST_HEAD(candidates);
-           !CERT_LIST_END(n, candidates); n = CERT_LIST_NEXT(n)) {
-        bool keepGoing;
-        Input derCert;
-        Result rv = derCert.Init(n->cert->derCert.data, n->cert->derCert.len);
-        EXPECT_EQ(Success, rv);
-        if (rv != Success) {
-          return rv;
-        }
-        rv = checker.Check(derCert, nullptr/*additionalNameConstraints*/,
-                           keepGoing);
-        if (rv != Success) {
-          return rv;
-        }
-        if (!keepGoing) {
-          break;
-        }
-      }
+    ByteString subjectDER(InputToByteString(encodedIssuerName));
+    ByteString certDER(subjectDERToCertDER[subjectDER]);
+    Input derCert;
+    Result rv = derCert.Init(certDER.data(), certDER.length());
+    if (rv != Success) {
+      return rv;
     }
-
+    bool keepGoing;
+    rv = checker.Check(derCert, nullptr/*additionalNameConstraints*/,
+                       keepGoing);
+    if (rv != Success) {
+      return rv;
+    }
     return Success;
   }
 
   virtual Result CheckRevocation(EndEntityOrCA, const CertID&, Time,
                                  /*optional*/ const Input*,
                                  /*optional*/ const Input*)
   {
     return Success;
@@ -163,83 +141,69 @@ private:
   virtual Result IsChainValid(const DERArray&, Time)
   {
     return Success;
   }
 
   virtual Result VerifySignedData(const SignedDataWithSignature& signedData,
                                   Input subjectPublicKeyInfo)
   {
-    return ::mozilla::pkix::VerifySignedData(signedData, subjectPublicKeyInfo,
-                                             MINIMUM_TEST_KEY_BITS, nullptr);
+    return TestVerifySignedData(signedData, subjectPublicKeyInfo);
   }
 
   virtual Result DigestBuf(Input item, /*out*/ uint8_t *digestBuf,
                            size_t digestBufLen)
   {
     ADD_FAILURE();
     return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 
   virtual Result CheckPublicKey(Input subjectPublicKeyInfo)
   {
     return TestCheckPublicKey(subjectPublicKeyInfo);
   }
 
-  // We hold references to CERTCertificates in the cert chain tail so that we
-  // CERT_CreateSubjectCertList can find them.
-  ScopedCERTCertificate certChainTail[7];
-
-public:
-  CERTCertificate* GetLeafCACert() const
-  {
-    return certChainTail[MOZILLA_PKIX_ARRAY_LENGTH(certChainTail) - 1].get();
-  }
+  std::map<ByteString, ByteString> subjectDERToCertDER;
+  ByteString leafCACertDER;
+  ByteString rootCACertDER;
 };
 
 class pkixbuild : public ::testing::Test
 {
 public:
   static void SetUpTestCase()
   {
-    // XXX(Bug 1070444): We have to initialize NSS explicitly for these tests,
-    // unlike other tests, because we're using NSS directly.
-    if (NSS_NoDB_Init(nullptr) != SECSuccess) {
-      abort();
-    }
-
     if (!trustDomain.SetUpCertChainTail()) {
       abort();
     }
   }
 
 protected:
 
   static TestTrustDomain trustDomain;
 };
 
 /*static*/ TestTrustDomain pkixbuild::trustDomain;
 
 TEST_F(pkixbuild, MaxAcceptableCertChainLength)
 {
   {
+    ByteString leafCACert(trustDomain.GetLeafCACertDER());
     Input certDER;
-    ASSERT_EQ(Success, certDER.Init(trustDomain.GetLeafCACert()->derCert.data,
-                                    trustDomain.GetLeafCACert()->derCert.len));
+    ASSERT_EQ(Success, certDER.Init(leafCACert.data(), leafCACert.length()));
     ASSERT_EQ(Success,
               BuildCertChain(trustDomain, certDER, Now(),
                              EndEntityOrCA::MustBeCA,
                              KeyUsage::noParticularKeyUsageRequired,
                              KeyPurposeId::id_kp_serverAuth,
                              CertPolicyId::anyPolicy,
                              nullptr/*stapledOCSPResponse*/));
   }
 
   {
-    ScopedCERTCertificate cert;
     ByteString certDER(CreateCert("CA7", "Direct End-Entity",
                                   EndEntityOrCA::MustBeEndEntity));
     ASSERT_FALSE(ENCODING_FAILED(certDER));
     Input certDERInput;
     ASSERT_EQ(Success, certDERInput.Init(certDER.data(), certDER.length()));
     ASSERT_EQ(Success,
               BuildCertChain(trustDomain, certDERInput, Now(),
                              EndEntityOrCA::MustBeEndEntity,
@@ -249,24 +213,20 @@ TEST_F(pkixbuild, MaxAcceptableCertChain
                              nullptr/*stapledOCSPResponse*/));
   }
 }
 
 TEST_F(pkixbuild, BeyondMaxAcceptableCertChainLength)
 {
   static char const* const caCertName = "CA Too Far";
 
-  // We need a CERTCertificate for caCert so that the trustdomain's FindIssuer
-  // method can find it through the NSS cert DB.
-  ScopedCERTCertificate caCert;
+  trustDomain.CreateCACert("CA7", caCertName);
 
   {
-    ByteString certDER(CreateCert("CA7", caCertName, EndEntityOrCA::MustBeCA,
-                                  &caCert));
-    ASSERT_FALSE(ENCODING_FAILED(certDER));
+    ByteString certDER(trustDomain.GetLeafCACertDER());
     Input certDERInput;
     ASSERT_EQ(Success, certDERInput.Init(certDER.data(), certDER.length()));
     ASSERT_EQ(Result::ERROR_UNKNOWN_ISSUER,
               BuildCertChain(trustDomain, certDERInput, Now(),
                              EndEntityOrCA::MustBeCA,
                              KeyUsage::noParticularKeyUsageRequired,
                              KeyPurposeId::id_kp_serverAuth,
                              CertPolicyId::anyPolicy,
@@ -346,18 +306,17 @@ public:
   virtual Result IsChainValid(const DERArray&, Time)
   {
     return Success;
   }
 
   virtual Result VerifySignedData(const SignedDataWithSignature& signedData,
                                   Input subjectPublicKeyInfo)
   {
-    return ::mozilla::pkix::VerifySignedData(signedData, subjectPublicKeyInfo,
-                                             MINIMUM_TEST_KEY_BITS, nullptr);
+    return TestVerifySignedData(signedData, subjectPublicKeyInfo);
   }
 
   virtual Result DigestBuf(Input, /*out*/uint8_t*, size_t)
   {
     ADD_FAILURE();
     return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 
--- a/security/pkix/test/gtest/pkixcert_signature_algorithm_tests.cpp
+++ b/security/pkix/test/gtest/pkixcert_signature_algorithm_tests.cpp
@@ -1,15 +1,14 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 #include "pkix/pkix.h"
-#include "pkix/pkixnss.h"
 #include "pkixgtest.h"
 #include "pkixtestutil.h"
 
 using namespace mozilla::pkix;
 using namespace mozilla::pkix::test;
 
 static ByteString
 CreateCert(const char* issuerCN,
@@ -105,18 +104,17 @@ private:
   {
     return Success;
   }
 
   virtual Result VerifySignedData(const SignedDataWithSignature& signedData,
                                   Input subjectPublicKeyInfo)
   {
     EXPECT_NE(SignatureAlgorithm::unsupported_algorithm, signedData.algorithm);
-    return ::mozilla::pkix::VerifySignedData(signedData, subjectPublicKeyInfo,
-                                             MINIMUM_TEST_KEY_BITS, nullptr);
+    return TestVerifySignedData(signedData, subjectPublicKeyInfo);
   }
 
   virtual Result DigestBuf(Input, uint8_t*, size_t)
   {
     ADD_FAILURE();
     return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 
--- a/security/pkix/test/lib/pkixtestutil.cpp
+++ b/security/pkix/test/lib/pkixtestutil.cpp
@@ -78,16 +78,30 @@ InputEqualsByteString(Input input, const
     // Init can only fail if it is given a bad pointer or if the input is too
     // long, which won't ever happen. Plus, if it does, it is ok to call abort
     // since this is only test code.
     abort();
   }
   return InputsAreEqual(input, bsInput);
 }
 
+ByteString
+InputToByteString(Input input)
+{
+  ByteString result;
+  Reader reader(input);
+  for (;;) {
+    uint8_t b;
+    if (reader.Read(b) != Success) {
+      return result;
+    }
+    result.push_back(b);
+  }
+}
+
 Result
 TamperOnce(/*in/out*/ ByteString& item, const ByteString& from,
            const ByteString& to)
 {
   if (from.length() < 8) {
     return Result::FATAL_ERROR_INVALID_ARGS;
   }
   if (from.length() != to.length()) {
--- a/security/pkix/test/lib/pkixtestutil.h
+++ b/security/pkix/test/lib/pkixtestutil.h
@@ -62,16 +62,17 @@ public:
   template <size_t N>
   explicit TestInput(const char (&valueString)[N])
     : Input(reinterpret_cast<const uint8_t(&)[N-1]>(valueString))
   {
   }
 };
 
 bool InputEqualsByteString(Input input, const ByteString& bs);
+ByteString InputToByteString(Input input);
 
 // python DottedOIDToCode.py --tlv id-kp-OCSPSigning 1.3.6.1.5.5.7.3.9
 static const uint8_t tlv_id_kp_OCSPSigning[] = {
   0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x09
 };
 
 // python DottedOIDToCode.py --tlv id-kp-serverAuth 1.3.6.1.5.5.7.3.1
 static const uint8_t tlv_id_kp_serverAuth[] = {
--- a/services/healthreport/healthreporter.jsm
+++ b/services/healthreport/healthreporter.jsm
@@ -333,20 +333,22 @@ function AbstractHealthReporter(branch, 
 
   this._lastDailyDate = null;
 
   // Yes, this will probably run concurrently with remaining constructor work.
   let hasFirstRun = this._prefs.get("service.firstRun", false);
   this._initHistogram = hasFirstRun ? TELEMETRY_INIT : TELEMETRY_INIT_FIRSTRUN;
   this._dbOpenHistogram = hasFirstRun ? TELEMETRY_DB_OPEN : TELEMETRY_DB_OPEN_FIRSTRUN;
 
-  // This is set to the name for the provider that we are currently initializing
-  // or shutting down, if any. This is used for AsyncShutdownTimeout diagnostics.
+  // This is set to the name for the provider that we are currently initializing,
+  // shutting down or collecting data from, if any.
+  // This is used for AsyncShutdownTimeout diagnostics.
   this._currentProviderInShutdown = null;
   this._currentProviderInInit = null;
+  this._currentProviderInCollect = null;
 }
 
 AbstractHealthReporter.prototype = Object.freeze({
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
 
   /**
    * Whether the service is fully initialized and running.
    *
@@ -410,16 +412,17 @@ AbstractHealthReporter.prototype = Objec
             initializeHadError: this._initializeHadError,
             providerManagerInProgress: this._providerManagerInProgress,
             storageInProgress: this._storageInProgress,
             hasProviderManager: !!this._providerManager,
             hasStorage: !!this._storage,
             shutdownComplete: this._shutdownComplete,
             currentProviderInShutdown: this._currentProviderInShutdown,
             currentProviderInInit: this._currentProviderInInit,
+            currentProviderInCollect: this._currentProviderInCollect,
           }));
 
       try {
         this._storageInProgress = true;
         TelemetryStopwatch.start(this._dbOpenHistogram, this);
         let storage = yield Metrics.Storage(this._dbName);
         TelemetryStopwatch.finish(this._dbOpenHistogram, this);
         yield this._onStorageCreated();
@@ -788,17 +791,18 @@ AbstractHealthReporter.prototype = Objec
       return Promise.reject(new Error("Not initialized."));
     }
 
     return Task.spawn(function doCollection() {
       yield this._providerManager.ensurePullOnlyProvidersRegistered();
 
       try {
         TelemetryStopwatch.start(TELEMETRY_COLLECT_CONSTANT, this);
-        yield this._providerManager.collectConstantData();
+        yield this._providerManager.collectConstantData(name => this._currentProviderInCollect = name);
+        this._currentProviderInCollect = null;
         TelemetryStopwatch.finish(TELEMETRY_COLLECT_CONSTANT, this);
       } catch (ex) {
         TelemetryStopwatch.cancel(TELEMETRY_COLLECT_CONSTANT, this);
         this._log.warn("Error collecting constant data: " +
                        CommonUtils.exceptionStr(ex));
       }
 
       // Daily data is collected if it hasn't yet been collected this
@@ -808,17 +812,18 @@ AbstractHealthReporter.prototype = Objec
       // makes no guarantees about limits. The alternative would involve
       // recording state. The simpler implementation prevails for now.
       if (!this._lastDailyDate ||
           Date.now() - this._lastDailyDate > MILLISECONDS_PER_DAY) {
 
         try {
           TelemetryStopwatch.start(TELEMETRY_COLLECT_DAILY, this);
           this._lastDailyDate = new Date();
-          yield this._providerManager.collectDailyData();
+          yield this._providerManager.collectDailyData(name => this._currentProviderInCollect = name);
+          this._currentProviderInCollect = null;
           TelemetryStopwatch.finish(TELEMETRY_COLLECT_DAILY, this);
         } catch (ex) {
           TelemetryStopwatch.cancel(TELEMETRY_COLLECT_DAILY, this);
           this._log.warn("Error collecting daily data from providers: " +
                          CommonUtils.exceptionStr(ex));
         }
       }
 
--- a/services/metrics/providermanager.jsm
+++ b/services/metrics/providermanager.jsm
@@ -431,18 +431,21 @@ this.ProviderManager.prototype = Object.
 
   /**
    * Collects all constant measurements from all providers.
    *
    * Returns a Promise that will be fulfilled once all data providers have
    * provided their constant data. A side-effect of this promise fulfillment
    * is that the manager is populated with the obtained collection results.
    * The resolved value to the promise is this `ProviderManager` instance.
+   *
+   * @param providerDiagnostic
+   *        (function) Optional, called with the name of the provider currently being initialized.
    */
-  collectConstantData: function () {
+  collectConstantData: function (providerDiagnostic=null) {
     let entries = [];
 
     for (let [name, entry] of this._providers) {
       if (entry.constantsCollected) {
         this._log.trace("Provider has already provided constant data: " +
                         name);
         continue;
       }
@@ -450,28 +453,30 @@ this.ProviderManager.prototype = Object.
       entries.push(entry);
     }
 
     let onCollect = function (entry, result) {
       entry.constantsCollected = true;
     };
 
     return this._callCollectOnProviders(entries, "collectConstantData",
-                                        onCollect);
+                                        onCollect, providerDiagnostic);
   },
 
   /**
    * Calls collectDailyData on all providers.
    */
-  collectDailyData: function () {
+  collectDailyData: function (providerDiagnostic=null) {
     return this._callCollectOnProviders(this._providers.values(),
-                                        "collectDailyData");
+                                        "collectDailyData",
+                                        null,
+                                        providerDiagnostic);
   },
 
-  _callCollectOnProviders: function (entries, fnProperty, onCollect=null) {
+  _callCollectOnProviders: function (entries, fnProperty, onCollect=null, providerDiagnostic=null) {
     let promises = [];
 
     for (let entry of entries) {
       let provider = entry.provider;
       let collectPromise;
       try {
         collectPromise = provider[fnProperty].call(provider);
       } catch (ex) {
@@ -497,31 +502,35 @@ this.ProviderManager.prototype = Object.
         }
 
         return CommonUtils.laterTickResolvingPromise(result);
       });
 
       promises.push([provider.name, promise]);
     }
 
-    return this._handleCollectionPromises(promises);
+    return this._handleCollectionPromises(promises, providerDiagnostic);
   },
 
   /**
    * Handles promises returned by the collect* functions.
    *
    * This consumes the data resolved by the promises and returns a new promise
    * that will be resolved once all promises have been resolved.
    *
    * The promise is resolved even if one of the underlying collection
    * promises is rejected.
    */
-  _handleCollectionPromises: function (promises) {
+  _handleCollectionPromises: function (promises, providerDiagnostic=null) {
     return Task.spawn(function waitForPromises() {
       for (let [name, promise] of promises) {
+        if (providerDiagnostic) {
+          providerDiagnostic(name);
+        }
+
         try {
           yield promise;
           this._log.debug("Provider collected successfully: " + name);
         } catch (ex) {
           this._recordProviderError(name, "Failed to collect", ex);
         }
       }
 
--- a/testing/config/mozharness/mac_config.py
+++ b/testing/config/mozharness/mac_config.py
@@ -4,83 +4,90 @@
 
 config = {
     "suite_definitions": {
         "cppunittest": {
             "options": [
                 "--symbols-path=%(symbols_path)s",
                 "--xre-path=%(abs_app_dir)s"
             ],
+            "mac_res_subdir": "MacOS",
             "run_filename": "runcppunittests.py",
             "testsdir": "cppunittests"
         },
         "jittest": {
             "options": [
                 "tests/bin/js",
                 "--no-slow",
                 "--no-progress",
                 "--tinderbox",
                 "--tbpl"
             ],
+            "mac_res_subdir": "MacOS",
             "run_filename": "jit_test.py",
             "testsdir": "jit-test/jit-test"
         },
         "mochitest": {
             "options": [
                 "--appname=%(binary_path)s",
                 "--utility-path=tests/bin",
                 "--extra-profile-file=tests/bin/plugins",
                 "--symbols-path=%(symbols_path)s",
                 "--certificate-path=tests/certs",
                 "--autorun",
                 "--close-when-done",
                 "--console-level=INFO",
                 "--quiet",
                 "--log-raw=%(raw_log_file)s"
             ],
+            "mac_res_subdir": "MacOS",
             "run_filename": "runtests.py",
             "testsdir": "mochitest"
         },
         "mozbase": {
             "options": [
                 "-b",
                 "%(binary_path)s"
             ],
+            "mac_res_subdir": "MacOS",
             "run_filename": "test.py",
             "testsdir": "mozbase"
         },
         "reftest": {
             "options": [
                 "--appname=%(binary_path)s",
                 "--utility-path=tests/bin",
                 "--extra-profile-file=tests/bin/plugins",
                 "--symbols-path=%(symbols_path)s"
             ],
+            "mac_res_subdir": "MacOS",
             "run_filename": "runreftest.py",
             "testsdir": "reftest"
         },
         "webapprt": {
             "options": [
                 "--app=%(app_path)s",
                 "--utility-path=tests/bin",
                 "--extra-profile-file=tests/bin/plugins",
                 "--symbols-path=%(symbols_path)s",
                 "--certificate-path=tests/certs",
                 "--autorun",
                 "--close-when-done",
                 "--console-level=INFO",
                 "--testing-modules-dir=tests/modules",
                 "--quiet"
             ],
+            "mac_res_subdir": "MacOS",
             "run_filename": "runtests.py",
             "testsdir": "mochitest"
         },
         "xpcshell": {
             "options": [
                 "--symbols-path=%(symbols_path)s",
                 "--test-plugin-path=%(test_plugin_path)s",
                 "--log-raw=%(raw_log_file)s"
             ],
+            "mac_res_subdir": "MacOS",
             "run_filename": "runxpcshelltests.py",
             "testsdir": "xpcshell"
         }
     }
-}
\ No newline at end of file
+}
--- a/testing/mochitest/b2g_start_script.js
+++ b/testing/mochitest/b2g_start_script.js
@@ -1,16 +1,17 @@
 /* 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/. */
 
 let outOfProcess = __marionetteParams[0]
 let mochitestUrl = __marionetteParams[1]
 let onDevice = __marionetteParams[2]
 let wifiSettings = __marionetteParams[3]
+let chrome = __marionetteParams[4]
 let prefs = Components.classes["@mozilla.org/preferences-service;1"].
                             getService(Components.interfaces.nsIPrefBranch)
 let settings = window.navigator.mozSettings;
 let cm = Components.classes["@mozilla.org/categorymanager;1"].
                     getService(Components.interfaces.nsICategoryManager);
 
 if (wifiSettings)
   wifiSettings = JSON.parse(wifiSettings);
@@ -81,16 +82,21 @@ if (outOfProcess) {
       removeEventListener("DOMWindowCreated", listener, false);
       var window = e.target.defaultView;
       window.wrappedJSObject.SpecialPowers.addPermission("allowXULXBL", true, window.document);
     });
   }
   mm.loadFrameScript("data:,(" + encodeURI(contentScript.toSource()) + ")();", true);
 }
 
+if (chrome) {
+  let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader);
+  loader.loadSubScript("chrome://mochikit/content/browser-test.js");
+  b2gStart();
+}
 
 if (onDevice) {
   var cpuLock = Cc["@mozilla.org/power/powermanagerservice;1"]
                       .getService(Ci.nsIPowerManagerService)
                       .newWakeLock("cpu");
 
   let manager = navigator.mozWifiManager;
   let con = manager.connection;
@@ -128,10 +134,12 @@ if (onDevice) {
     req.onsuccess = function () {
       dump("----------------------enabling wifi------------------\n");
       var req1 = settings.createLock().set({
           'wifi.enabled': true,
           'wifi.suspended': false});
     };
   }
 } else {
-  container.src = mochitestUrl;
+  if (!chrome) {
+    container.src = mochitestUrl;
+  }
 }
--- a/testing/mochitest/browser-test.js
+++ b/testing/mochitest/browser-test.js
@@ -1,16 +1,20 @@
 /* -*- js-indent-level: 2; tab-width: 2; indent-tabs-mode: nil -*- */
 // Test timeout (seconds)
 var gTimeoutSeconds = 45;
 var gConfig;
 
 if (Cc === undefined) {
   var Cc = Components.classes;
+}
+if (Ci === undefined) {
   var Ci = Components.interfaces;
+}
+if (Cu === undefined) {
   var Cu = Components.utils;
 }
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Services",
   "resource://gre/modules/Services.jsm");
@@ -27,16 +31,25 @@ const SIMPLETEST_OVERRIDES =
 window.addEventListener("load", function testOnLoad() {
   window.removeEventListener("load", testOnLoad);
   window.addEventListener("MozAfterPaint", function testOnMozAfterPaint() {
     window.removeEventListener("MozAfterPaint", testOnMozAfterPaint);
     setTimeout(testInit, 0);
   });
 });
 
+function b2gStart() {
+  let homescreen = document.getElementById('systemapp');
+  var webNav = homescreen.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                                   .getInterface(Ci.nsIWebNavigation);
+  var url = "chrome://mochikit/content/harness.xul?manifestFile=tests.json";
+
+  webNav.loadURI(url, null, null, null, null);
+}
+
 function testInit() {
   gConfig = readConfig();
   if (gConfig.testRoot == "browser" ||
       gConfig.testRoot == "metro" ||
       gConfig.testRoot == "webapprtChrome") {
     // Make sure to launch the test harness for the first opened window only
     var prefs = Services.prefs;
     if (prefs.prefHasUserValue("testing.browserTestHarness.running"))
--- a/testing/mochitest/mach_commands.py
+++ b/testing/mochitest/mach_commands.py
@@ -106,17 +106,17 @@ class MochitestRunner(MozbuildObject):
             sys.path.append(build_path)
 
         self.tests_dir = os.path.join(self.topobjdir, '_tests')
         self.mochitest_dir = os.path.join(self.tests_dir, 'testing', 'mochitest')
         self.bin_dir = os.path.join(self.topobjdir, 'dist', 'bin')
 
     def run_b2g_test(self, test_paths=None, b2g_home=None, xre_path=None,
                      total_chunks=None, this_chunk=None, no_window=None,
-                     repeat=0, run_until_failure=False, **kwargs):
+                     repeat=0, run_until_failure=False, chrome=False, **kwargs):
         """Runs a b2g mochitest.
 
         test_paths is an enumerable of paths to tests. It can be a relative path
         from the top source directory, an absolute filename, or a directory
         containing test files.
         """
         # Need to call relpath before os.chdir() below.
         test_path = ''
@@ -141,17 +141,20 @@ class MochitestRunner(MozbuildObject):
 
             import mochitest
             from mochitest_options import B2GOptions
 
         parser = B2GOptions()
         options = parser.parse_args([])[0]
 
         if test_path:
-            test_root_file = mozpack.path.join(self.mochitest_dir, 'tests', test_path)
+            if chrome:
+                test_root_file = mozpack.path.join(self.mochitest_dir, 'chrome', test_path)
+            else:
+                test_root_file = mozpack.path.join(self.mochitest_dir, 'tests', test_path)
             if not os.path.exists(test_root_file):
                 print('Specified test path does not exist: %s' % test_root_file)
                 return 1
             options.testPath = test_path
 
         for k, v in kwargs.iteritems():
             setattr(options, k, v)
         options.noWindow = no_window
@@ -179,16 +182,17 @@ class MochitestRunner(MozbuildObject):
             # TODO Find adb automatically if it isn't on the path
             print(ADB_NOT_FOUND % ('mochitest-remote', b2g_home))
             return 1
 
         options.b2gPath = b2g_home
         options.logdir = self.mochitest_dir
         options.httpdPath = self.mochitest_dir
         options.xrePath = xre_path
+        options.chrome = chrome
         return mochitest.run_remote_mochitests(parser, options)
 
     def run_desktop_test(self, context, suite=None, test_paths=None, debugger=None,
         debugger_args=None, slowscript=False, screenshot_on_fail = False, shuffle=False, closure_behaviour='auto',
         rerun_failures=False, no_autorun=False, repeat=0, run_until_failure=False,
         slow=False, chunk_by_dir=0, total_chunks=None, this_chunk=None, extraPrefs=[],
         jsdebugger=False, debug_on_failure=False, start_at=None, end_at=None,
         e10s=False, strict_content_sandbox=False, dmd=False, dump_output_directory=None,
@@ -836,16 +840,23 @@ class B2GCommands(MachCommandBase):
 
         driver = self._spawn(BuildDriver)
         driver.install_tests(remove=False)
 
         mochitest = self._spawn(MochitestRunner)
         return mochitest.run_b2g_test(b2g_home=self.b2g_home,
                 xre_path=self.xre_path, test_paths=test_paths, **kwargs)
 
+    @Command('mochitest-chrome-remote', category='testing',
+        description='Run a remote mochitest-chrome.',
+        conditions=[conditions.is_b2g, is_emulator])
+    @B2GCommand
+    def run_mochitest_chrome_remote(self, test_paths, **kwargs):
+        return self.run_mochitest_remote(test_paths, chrome=True, **kwargs)
+
     @Command('mochitest-b2g-desktop', category='testing',
         conditions=[conditions.is_b2g_desktop],
         description='Run a b2g desktop mochitest (same as mochitest-plain but for b2g desktop).')
     @B2GCommand
     def run_mochitest_b2g_desktop(self, test_paths, **kwargs):
         kwargs['profile'] = kwargs.get('profile') or os.environ.get('GAIA_PROFILE')
         if not kwargs['profile'] or not os.path.isdir(kwargs['profile']):
             print(GAIA_PROFILE_NOT_FOUND % 'mochitest-b2g-desktop')
--- a/testing/mochitest/runtests.py
+++ b/testing/mochitest/runtests.py
@@ -781,16 +781,28 @@ class MochitestUtilsMixin(object):
       mfile.write(chrome)
 
   def getChromeTestDir(self, options):
     dir = os.path.join(os.path.abspath("."), SCRIPT_DIR) + "/"
     if mozinfo.isWin:
       dir = "file:///" + dir.replace("\\", "/")
     return dir
 
+  def writeChromeManifest(self, options):
+    manifest = os.path.join(options.profilePath, "tests.manifest")
+    with open(manifest, "w") as manifestFile:
+      # Register chrome directory.
+      chrometestDir = self.getChromeTestDir(options)
+      manifestFile.write("content mochitests %s contentaccessible=yes\n" % chrometestDir)
+
+      if options.testingModulesDir is not None:
+        manifestFile.write("resource testing-common file:///%s\n" %
+          options.testingModulesDir)
+    return manifest
+
   def addChromeToProfile(self, options):
     "Adds MochiKit chrome tests to the profile."
 
     # Create (empty) chrome directory.
     chromedir = os.path.join(options.profilePath, "chrome")
     os.mkdir(chromedir)
 
     # Write userChrome.css.
@@ -802,25 +814,17 @@ toolbarpalette {
 }
 toolbar#nav-bar {
   background-image: none !important;
 }
 """
     with open(os.path.join(options.profilePath, "userChrome.css"), "a") as chromeFile:
       chromeFile.write(chrome)
 
-    manifest = os.path.join(options.profilePath, "tests.manifest")
-    with open(manifest, "w") as manifestFile:
-      # Register chrome directory.
-      chrometestDir = self.getChromeTestDir(options)
-      manifestFile.write("content mochitests %s contentaccessible=yes\n" % chrometestDir)
-
-      if options.testingModulesDir is not None:
-        manifestFile.write("resource testing-common file:///%s\n" %
-          options.testingModulesDir)
+    manifest = self.writeChromeManifest(options)
 
     # Call installChromeJar().
     if not os.path.isdir(os.path.join(SCRIPT_DIR, self.jarDir)):
       self.log.error("TEST-UNEXPECTED-FAIL | invalid setup: missing mochikit extension")
       return None
 
     # Support Firefox (browser), B2G (shell), SeaMonkey (navigator), and Webapp
     # Runtime (webapp).
--- a/testing/mochitest/runtestsb2g.py
+++ b/testing/mochitest/runtestsb2g.py
@@ -35,16 +35,17 @@ class B2GMochitest(MochitestUtilsMixin):
         self.marionette_args = marionette_args
         self.out_of_process = out_of_process
         self.locations_file = locations
         self.preferences = []
         self.webapps = None
         self.test_script = os.path.join(here, 'b2g_start_script.js')
         self.test_script_args = [self.out_of_process]
         self.product = 'b2g'
+        self.remote_chrome_test_dir = None
 
         if profile_data_dir:
             self.preferences = [os.path.join(profile_data_dir, f)
                                  for f in os.listdir(profile_data_dir) if f.startswith('pref')]
             self.webapps = [os.path.join(profile_data_dir, f)
                              for f in os.listdir(profile_data_dir) if f.startswith('webapp')]
 
         # mozinfo is populated by the parent class
@@ -147,16 +148,20 @@ class B2GMochitest(MochitestUtilsMixin):
             self.runner = self.marionette.runner
             self.app_ctx = self.runner.app_ctx
 
             self.remote_log = posixpath.join(self.app_ctx.remote_test_root,
                                              'log', 'mochitest.log')
             if not self.app_ctx.dm.dirExists(posixpath.dirname(self.remote_log)):
                 self.app_ctx.dm.mkDirs(self.remote_log)
 
+            if options.chrome:
+                # Update chrome manifest file in profile with correct path.
+                self.writeChromeManifest(options)
+
             self.leak_report_file = posixpath.join(self.app_ctx.remote_test_root,
                                                    'log', 'runtests_leaks.log')
 
             # We don't want to copy the host env onto the device, so pass in an
             # empty env.
             self.browserEnv = self.buildBrowserEnv(options, env={})
 
             # B2G emulator debug tests still make external connections, so don't
@@ -164,16 +169,17 @@ class B2GMochitest(MochitestUtilsMixin):
             if mozinfo.info['debug'] and 'MOZ_DISABLE_NONLOCAL_CONNECTIONS' in self.browserEnv:
                 del self.browserEnv['MOZ_DISABLE_NONLOCAL_CONNECTIONS']
             self.runner.env.update(self.browserEnv)
 
             self.startServers(options, None)
             self.buildURLOptions(options, {'MOZ_HIDE_RESULTS_TABLE': '1'})
             self.test_script_args.append(not options.emulator)
             self.test_script_args.append(options.wifi)
+            self.test_script_args.append(options.chrome)
 
 
             self.runner.start(outputTimeout=timeout)
 
             self.marionette.wait_for_port()
             self.marionette.start_session()
             self.marionette.set_context(self.marionette.CONTEXT_CHROME)
 
@@ -181,16 +187,25 @@ class B2GMochitest(MochitestUtilsMixin):
             # will be 'offline' when the mochitests start.  Presumably, the network
             # won't be offline on a real device, so we only do this for emulators.
             self.marionette.execute_script("""
                 Components.utils.import("resource://gre/modules/Services.jsm");
                 Services.io.manageOfflineStatus = false;
                 Services.io.offline = false;
                 """)
 
+            if options.chrome:
+                self.app_ctx.dm.removeDir(self.remote_chrome_test_dir)
+                self.app_ctx.dm.mkDir(self.remote_chrome_test_dir)
+                local = super(B2GMochitest, self).getChromeTestDir(options)
+                local = os.path.join(local, "chrome")
+                remote = self.remote_chrome_test_dir
+                self.log.info("pushing %s to %s on device..." % (local, remote))
+                self.app_ctx.dm.pushDir(local, remote)
+
             if os.path.isfile(self.test_script):
                 with open(self.test_script, 'r') as script:
                     self.marionette.execute_script(script.read(),
                                                    script_args=self.test_script_args)
             else:
                 self.marionette.execute_script(self.test_script,
                                                script_args=self.test_script_args)
             status = self.runner.wait()
@@ -221,16 +236,28 @@ class B2GMochitest(MochitestUtilsMixin):
         if manifest is not None:
             self.cleanup(manifest, options)
         return status
 
     def getGMPPluginPath(self, options):
         # TODO: bug 1043403
         return None
 
+    def getChromeTestDir(self, options):
+        # The chrome test directory returned here is the remote location
+        # of chrome test files. A reference to this directory is requested
+        # when building the profile locally, before self.app_ctx is defined.
+        # To get around this, return a dummy directory until self.app_ctx
+        # is defined; the correct directory will be returned later, over-
+        # writing the dummy.
+        if hasattr(self, 'app_ctx'):
+            self.remote_chrome_test_dir = posixpath.join(self.app_ctx.remote_test_root, 'chrome');
+            return self.remote_chrome_test_dir
+        return 'dummy-chrome-test-dir'
+
 
 class B2GDeviceMochitest(B2GMochitest, Mochitest):
     remote_log = None
 
     def __init__(self, marionette_args, logger_options, profile_data_dir,
                  local_binary_dir, remote_test_root=None, remote_log_file=None):
         B2GMochitest.__init__(self, marionette_args, logger_options, out_of_process=True, profile_data_dir=profile_data_dir)
         self.local_log = None
--- a/testing/mochitest/tests/SimpleTest/SimpleTest.js
+++ b/testing/mochitest/tests/SimpleTest/SimpleTest.js
@@ -815,17 +815,17 @@ SimpleTest.waitForClipboard = function(a
             // Don't show the success message when waiting for preExpectedVal
             if (preExpectedVal)
                 preExpectedVal = null;
             else
                 SimpleTest.ok(true, "Clipboard has the correct value");
             reset();
             successFn();
         } else {
-            setTimeout(function() { return wait(validatorFn, successFn, failureFn, flavor); }, 100);
+            SimpleTest._originalSetTimeout.apply(window, [function() { return wait(validatorFn, successFn, failureFn, flavor); }, 100]);
         }
     }
 
     // First we wait for a known value different from the expected one.
     var preExpectedVal = SimpleTest._waitForClipboardMonotonicCounter +
                          "-waitForClipboard-known-value";
     SpecialPowers.clipboardCopyString(preExpectedVal);
     wait(function(aData) { return aData  == preExpectedVal; },
--- a/toolkit/modules/RemoteWebProgress.jsm
+++ b/toolkit/modules/RemoteWebProgress.jsm
@@ -99,21 +99,16 @@ RemoteWebProgressManager.prototype = {
     if (aStatus) {
       let helper = Cc["@mozilla.org/network/serialization-helper;1"]
                     .getService(Components.interfaces.nsISerializationHelper);
 
       deserialized = helper.deserializeObject(aStatus)
       deserialized.QueryInterface(Ci.nsISSLStatus);
     }
 
-    // We must check the Extended Validation (EV) state here, on the chrome
-    // process, because NSS is needed for that determination.
-    if (deserialized && deserialized.isExtendedValidation)
-      aState |= Ci.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL;
-
     return [deserialized, aState];
   },
 
   setCurrentURI: function (aURI) {
     // This function is simpler than nsDocShell::SetCurrentURI since
     // it doesn't have to deal with child docshells.
     let webNavigation = this._browser.webNavigation;
     webNavigation._currentURI = aURI;
--- a/toolkit/xre/test/win/TestDllInterceptor.cpp
+++ b/toolkit/xre/test/win/TestDllInterceptor.cpp
@@ -152,16 +152,17 @@ int main()
       TestHook("ntdll.dll", "NtQueryFullAttributesFile") &&
       // Bug 733892: toolkit/crashreporter/nsExceptionHandler.cpp
       TestHook("kernel32.dll", "SetUnhandledExceptionFilter") &&
 #ifdef _M_IX86
       // Bug 670967: xpcom/base/AvailableMemoryTracker.cpp
       TestHook("kernel32.dll", "VirtualAlloc") &&
       TestHook("kernel32.dll", "MapViewOfFile") &&
       TestHook("gdi32.dll", "CreateDIBSection") &&
+      TestHook("kernel32.dll", "CreateFileW") &&
 #endif
       TestDetour("ntdll.dll", "LdrLoadDll")) {
     printf("TEST-PASS | WindowsDllInterceptor | all checks passed\n");
     return 0;
   }
 
   return 1;
 }
--- a/xpcom/base/AlreadyAddRefed.h
+++ b/xpcom/base/AlreadyAddRefed.h
@@ -132,12 +132,12 @@ struct already_AddRefed
   already_AddRefed<U> downcast()
   {
     U* tmp = static_cast<U*>(mRawPtr);
     mRawPtr = nullptr;
     return already_AddRefed<U>(tmp);
   }
 
 private:
-  T* mRawPtr;
+  T* MOZ_OWNING_REF mRawPtr;
 };
 
 #endif // AlreadyAddRefed_h
--- a/xpcom/base/StaticPtr.h
+++ b/xpcom/base/StaticPtr.h
@@ -136,17 +136,17 @@ private:
   {
     T* oldPtr = mRawPtr;
     mRawPtr = aNewPtr;
     if (oldPtr) {
       oldPtr->Release();
     }
   }
 
-  T* mRawPtr;
+  T* MOZ_OWNING_REF mRawPtr;
 };
 
 namespace StaticPtr_internal {
 class Zero;
 } // namespace StaticPtr_internal
 
 #define REFLEXIVE_EQUALITY_OPERATORS(type1, type2, eq_fn, ...) \
   template<__VA_ARGS__>                                        \
--- a/xpcom/base/nsAutoPtr.h
+++ b/xpcom/base/nsAutoPtr.h
@@ -55,21 +55,21 @@ private:
     }
 
     operator T*() const
     {
       return mPtr;
     }
 
   private:
-    T* mPtr;
+    T* MOZ_NON_OWNING_REF mPtr;
   };
 
 private:
-  T* mRawPtr;
+  T* MOZ_OWNING_REF mRawPtr;
 
 public:
   typedef T element_type;
 
   ~nsAutoPtr()
   {
     delete mRawPtr;
   }
@@ -426,17 +426,17 @@ private:
   assign(T* aNewPtr)
   {
     T* oldPtr = mRawPtr;
     mRawPtr = aNewPtr;
     delete [] oldPtr;
   }
 
 private:
-  T* mRawPtr;
+  T* MOZ_OWNING_REF mRawPtr;
 
 public:
   typedef T element_type;
 
   ~nsAutoArrayPtr()
   {
     delete [] mRawPtr;
   }
@@ -773,17 +773,17 @@ public:
   virtual nsresult NS_FASTCALL operator()(const nsIID& aIID,
                                           void** aResult) const
   {
     nsresult status = mRawPtr ? mRawPtr->QueryInterface(aIID, aResult)
                               : NS_ERROR_NULL_POINTER;
     return status;
   }
 private:
-  T* mRawPtr;
+  T* MOZ_NON_OWNING_REF mRawPtr;
 };
 
 template<class T>
 class nsQueryObjectWithError : public nsCOMPtr_helper
 {
 public:
   nsQueryObjectWithError(T* aRawPtr, nsresult* aErrorPtr)
     : mRawPtr(aRawPtr), mErrorPtr(aErrorPtr)
@@ -796,17 +796,17 @@ public:
     nsresult status = mRawPtr ? mRawPtr->QueryInterface(aIID, aResult)
                               : NS_ERROR_NULL_POINTER;
     if (mErrorPtr) {
       *mErrorPtr = status;
     }
     return status;
   }
 private:
-  T* mRawPtr;
+  T* MOZ_NON_OWNING_REF mRawPtr;
   nsresult* mErrorPtr;
 };
 
 template<class T>
 inline nsQueryObject<T>
 do_QueryObject(T* aRawPtr)
 {
   return nsQueryObject<T>(aRawPtr);
--- a/xpcom/base/nsRefPtr.h
+++ b/xpcom/base/nsRefPtr.h
@@ -43,17 +43,17 @@ private:
     T* oldPtr = mRawPtr;
     mRawPtr = aNewPtr;
     if (oldPtr) {
       oldPtr->Release();
     }
   }
 
 private:
-  T* mRawPtr;
+  T* MOZ_OWNING_REF mRawPtr;
 
 public:
   typedef T element_type;
 
   ~nsRefPtr()
   {
     if (mRawPtr) {
       mRawPtr->Release();
--- a/xpcom/build/nsWindowsDllInterceptor.h
+++ b/xpcom/build/nsWindowsDllInterceptor.h
@@ -392,16 +392,19 @@ protected:
         nBytes++;
       } else if (origBytes[nBytes] == 0x6A) {
         // PUSH imm8
         nBytes += 2;
       } else if (origBytes[nBytes] == 0xe9) {
         pJmp32 = nBytes;
         // jmp 32bit offset
         nBytes += 5;
+      } else if (origBytes[nBytes] == 0xff && origBytes[nBytes + 1] == 0x25) {
+        // jmp [disp32]
+        nBytes += 6;
       } else {
         //printf ("Unknown x86 instruction byte 0x%02x, aborting trampoline\n", origBytes[nBytes]);
         return;
       }
     }
 #elif defined(_M_X64)
     byteptr_t directJmpAddr;
 
--- a/xpcom/build/nsXREAppData.h
+++ b/xpcom/build/nsXREAppData.h
@@ -2,16 +2,17 @@
 /* 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 nsXREAppData_h
 #define nsXREAppData_h
 
 #include <stdint.h>
+#include "mozilla/Attributes.h"
 
 class nsIFile;
 
 /**
  * Application-specific data needed to start the apprunner.
  *
  * @note When this structure is allocated and manipulated by XRE_CreateAppData,
  *       string fields will be allocated with NS_Alloc, and interface pointers
@@ -25,17 +26,17 @@ struct nsXREAppData
    * is maintained.
    */
   uint32_t size;
 
   /**
    * The directory of the application to be run. May be null if the
    * xulrunner and the app are installed into the same directory.
    */
-  nsIFile* directory;
+  nsIFile* MOZ_NON_OWNING_REF directory;
 
   /**
    * The name of the application vendor. This must be ASCII, and is normally
    * mixed-case, e.g. "Mozilla". Optional (may be null), but highly
    * recommended. Must not be the empty string.
    */
   const char* vendor;
 
@@ -87,17 +88,17 @@ struct nsXREAppData
    * Combination of NS_XRE_ prefixed flags (defined below).
    */
   uint32_t flags;
 
   /**
    * The location of the XRE. XRE_main may not be able to figure this out
    * programatically.
    */
-  nsIFile* xreDirectory;
+  nsIFile* MOZ_NON_OWNING_REF xreDirectory;
 
   /**
    * The minimum/maximum compatible XRE version.
    */
   const char* minVersion;
   const char* maxVersion;
 
   /**
--- a/xpcom/glue/nsCOMPtr.h
+++ b/xpcom/glue/nsCOMPtr.h
@@ -162,32 +162,32 @@ class MOZ_STACK_CLASS nsQueryInterface M
 {
 public:
   explicit
   nsQueryInterface(nsISupports* aRawPtr) : mRawPtr(aRawPtr) {}
 
   nsresult NS_FASTCALL operator()(const nsIID& aIID, void**) const;
 
 private:
-  nsISupports* mRawPtr;
+  nsISupports* MOZ_OWNING_REF mRawPtr;
 };
 
 class nsQueryInterfaceWithError
 {
 public:
   nsQueryInterfaceWithError(nsISupports* aRawPtr, nsresult* aError)
     : mRawPtr(aRawPtr)
     , mErrorPtr(aError)
   {
   }
 
   nsresult NS_FASTCALL operator()(const nsIID& aIID, void**) const;
 
 private:
-  nsISupports* mRawPtr;
+  nsISupports* MOZ_OWNING_REF mRawPtr;
   nsresult* mErrorPtr;
 };
 
 inline nsQueryInterface
 do_QueryInterface(nsISupports* aRawPtr)
 {
   return nsQueryInterface(aRawPtr);
 }
@@ -314,17 +314,17 @@ public:
   assign_from_gs_contractid_with_error(const nsGetServiceByContractIDWithError&,
                                        const nsIID&);
   void NS_FASTCALL
   assign_from_helper(const nsCOMPtr_helper&, const nsIID&);
   void** NS_FASTCALL
   begin_assignment();
 
 protected:
-  NS_MAY_ALIAS_PTR(nsISupports) mRawPtr;
+  NS_MAY_ALIAS_PTR(nsISupports) MOZ_OWNING_REF mRawPtr;
 
   void assign_assuming_AddRef(nsISupports* aNewPtr)
   {
     // |AddRef()|ing the new value (before entering this function) before
     // |Release()|ing the old lets us safely ignore the self-assignment case.
     // We must, however, be careful only to |Release()| _after_ doing the
     // assignment, in case the |Release()| leads to our _own_ destruction,
     // which would, in turn, cause an incorrect second |Release()| of our old
@@ -373,17 +373,17 @@ private:
     NSCAP_LOG_ASSIGNMENT(this, aNewPtr);
     NSCAP_LOG_RELEASE(this, oldPtr);
     if (oldPtr) {
       NSCAP_RELEASE(this, oldPtr);
     }
   }
 
 private:
-  T* mRawPtr;
+  T* MOZ_OWNING_REF mRawPtr;
 #endif
 
 public:
   typedef T element_type;
 
 #ifndef NSCAP_FEATURE_USE_BASE
   ~nsCOMPtr()
   {
--- a/xpcom/glue/nsComponentManagerUtils.h
+++ b/xpcom/glue/nsComponentManagerUtils.h
@@ -36,17 +36,17 @@ public:
     , mErrorPtr(aErrorPtr)
   {
   }
 
   virtual nsresult NS_FASTCALL operator()(const nsIID&, void**) const;
 
 private:
   const nsCID&    mCID;
-  nsISupports*    mOuter;
+  nsISupports* MOZ_NON_OWNING_REF mOuter;
   nsresult*       mErrorPtr;
 };
 
 class nsCreateInstanceByContractID : public nsCOMPtr_helper
 {
 public:
   nsCreateInstanceByContractID(const char* aContractID, nsISupports* aOuter,
                                nsresult* aErrorPtr)
@@ -55,17 +55,17 @@ public:
     , mErrorPtr(aErrorPtr)
   {
   }
 
   virtual nsresult NS_FASTCALL operator()(const nsIID&, void**) const;
 
 private:
   const char*   mContractID;
-  nsISupports*  mOuter;
+  nsISupports* MOZ_NON_OWNING_REF mOuter;
   nsresult*     mErrorPtr;
 };
 
 class nsCreateInstanceFromFactory : public nsCOMPtr_helper
 {
 public:
   nsCreateInstanceFromFactory(nsIFactory* aFactory, nsISupports* aOuter,
                               nsresult* aErrorPtr)
@@ -73,18 +73,18 @@ public:
     , mOuter(aOuter)
     , mErrorPtr(aErrorPtr)
   {
   }
 
   virtual nsresult NS_FASTCALL operator()(const nsIID&, void**) const;
 
 private:
-  nsIFactory*   mFactory;
-  nsISupports*  mOuter;
+  nsIFactory* MOZ_NON_OWNING_REF mFactory;
+  nsISupports* MOZ_NON_OWNING_REF mOuter;
   nsresult*     mErrorPtr;
 };
 
 
 inline const nsCreateInstanceByCID
 do_CreateInstance(const nsCID& aCID, nsresult* aError = 0)
 {
   return nsCreateInstanceByCID(aCID, 0, aError);
--- a/xpcom/glue/nsEnumeratorUtils.cpp
+++ b/xpcom/glue/nsEnumeratorUtils.cpp
@@ -106,30 +106,28 @@ public:
   NS_IMETHOD GetNext(nsISupports** aResult);
 
   explicit nsSingletonEnumerator(nsISupports* aValue);
 
 private:
   ~nsSingletonEnumerator();
 
 protected:
-  nsISupports* mValue;
+  nsCOMPtr<nsISupports> mValue;
   bool mConsumed;
 };
 
 nsSingletonEnumerator::nsSingletonEnumerator(nsISupports* aValue)
   : mValue(aValue)
 {
-  NS_IF_ADDREF(mValue);
   mConsumed = (mValue ? false : true);
 }
 
 nsSingletonEnumerator::~nsSingletonEnumerator()
 {
-  NS_IF_RELEASE(mValue);
 }
 
 NS_IMPL_ISUPPORTS(nsSingletonEnumerator, nsISimpleEnumerator)
 
 NS_IMETHODIMP
 nsSingletonEnumerator::HasMoreElements(bool* aResult)
 {
   NS_PRECONDITION(aResult != 0, "null ptr");
--- a/xpcom/glue/nsHashKeys.h
+++ b/xpcom/glue/nsHashKeys.h
@@ -380,17 +380,17 @@ public:
   static KeyTypePointer KeyToPointer(KeyType aKey) { return aKey; }
   static PLDHashNumber HashKey(KeyTypePointer aKey)
   {
     return NS_PTR_TO_UINT32(aKey) >> 2;
   }
   enum { ALLOW_MEMMOVE = true };
 
 protected:
-  T* mKey;
+  T* MOZ_NON_OWNING_REF mKey;
 };
 
 /**
  * hashkey wrapper using T* KeyType that sets key to nullptr upon
  * destruction. Relevant only in cases where a memory pointer-scanner
  * like valgrind might get confused about stale references.
  *
  * @see nsTHashtable::EntryType for specification
--- a/xpcom/glue/nsIInterfaceRequestorUtils.h
+++ b/xpcom/glue/nsIInterfaceRequestorUtils.h
@@ -29,17 +29,17 @@ public:
     : mSource(aSource)
     , mErrorPtr(aError)
   {
   }
 
   virtual nsresult NS_FASTCALL operator()(const nsIID&, void**) const;
 
 private:
-  nsISupports* mSource;
+  nsISupports* MOZ_NON_OWNING_REF mSource;
   nsresult* mErrorPtr;
 };
 
 inline const nsGetInterface
 do_GetInterface(nsISupports* aSource, nsresult* aError = 0)
 {
   return nsGetInterface(aSource, aError);
 }
--- a/xpcom/glue/nsProxyRelease.cpp
+++ b/xpcom/glue/nsProxyRelease.cpp
@@ -15,17 +15,17 @@ public:
 
   NS_IMETHOD Run()
   {
     mDoomed->Release();
     return NS_OK;
   }
 
 private:
-  nsISupports* mDoomed;
+  nsISupports* MOZ_OWNING_REF mDoomed;
 };
 
 nsresult
 NS_ProxyRelease(nsIEventTarget* aTarget, nsISupports* aDoomed,
                 bool aAlwaysProxy)
 {
   nsresult rv;
 
--- a/xpcom/glue/nsThreadUtils.h
+++ b/xpcom/glue/nsThreadUtils.h
@@ -266,45 +266,43 @@ public:
 
   // Make sure this return type is safe.
   typedef typename ReturnTypeEnforcer<ReturnType>::ReturnTypeIsSafe check;
 };
 
 template<class ClassType, typename Arg, bool Owning>
 struct nsRunnableMethodReceiver
 {
-  ClassType* mObj;
+  nsRefPtr<ClassType> mObj;
   Arg mArg;
   nsRunnableMethodReceiver(ClassType* aObj, Arg aArg)
     : mObj(aObj)
     , mArg(aArg)
   {
-    NS_IF_ADDREF(mObj);
   }
   ~nsRunnableMethodReceiver() { Revoke(); }
-  void Revoke() { NS_IF_RELEASE(mObj); }
+  void Revoke() { mObj = nullptr; }
 };
 
 template<class ClassType, bool Owning>
 struct nsRunnableMethodReceiver<ClassType, void, Owning>
 {
-  ClassType* mObj;
+  nsRefPtr<ClassType> mObj;
   explicit nsRunnableMethodReceiver(ClassType* aObj)
     : mObj(aObj)
   {
-    NS_IF_ADDREF(mObj);
   }
   ~nsRunnableMethodReceiver() { Revoke(); }
-  void Revoke() { NS_IF_RELEASE(mObj); }
+  void Revoke() { mObj = nullptr; }
 };
 
 template<class ClassType>
 struct nsRunnableMethodReceiver<ClassType, void, false>
 {
-  ClassType* mObj;
+  ClassType* MOZ_NON_OWNING_REF mObj;
   explicit nsRunnableMethodReceiver(ClassType* aObj) : mObj(aObj) {}
   void Revoke() { mObj = nullptr; }
 };
 
 template<typename Method, bool Owning> struct nsRunnableMethodTraits;
 
 template<class C, typename R, typename A, bool Owning>
 struct nsRunnableMethodTraits<R(C::*)(A), Owning>
--- a/xpcom/glue/nsWeakReference.cpp
+++ b/xpcom/glue/nsWeakReference.cpp
@@ -40,17 +40,17 @@ private:
 
   void
   NoticeReferentDestruction()
   // ...called (only) by an |nsSupportsWeakReference| from _its_ dtor.
   {
     mReferent = 0;
   }
 
-  nsSupportsWeakReference*  mReferent;
+  nsSupportsWeakReference* MOZ_NON_OWNING_REF mReferent;
 };
 
 nsresult
 nsQueryReferent::operator()(const nsIID& aIID, void** aAnswer) const
 {
   nsresult status;
   if (mWeakPtr) {
     if (NS_FAILED(status = mWeakPtr->QueryReferent(aIID, aAnswer))) {
--- a/xpcom/glue/nsWeakReference.h
+++ b/xpcom/glue/nsWeakReference.h
@@ -26,17 +26,17 @@ protected:
   inline ~nsSupportsWeakReference();
 
 private:
   friend class nsWeakReference;
 
   // Called (only) by an |nsWeakReference| from _its_ dtor.
   void NoticeProxyDestruction() { mProxy = 0; }
 
-  nsWeakReference* mProxy;
+  nsWeakReference* MOZ_NON_OWNING_REF mProxy;
 
 protected:
 
   void ClearWeakReferences();
   bool HasWeakReferences() const { return mProxy != 0; }
 };
 
 inline