Merge inbound to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Tue, 27 Dec 2016 00:00:22 -0500
changeset 374613 3119a9a0b5dee60ac77b7596ae5dbe0658f598ad
parent 374588 4a1f36ebd21cbe090cd5d0ce6458af3d883a868a (current diff)
parent 374612 4398ee2eea0ceea1ed6c5ada0e7eee969ac7ab10 (diff)
child 374614 18dca3e07dcf9381f78a42af19315b0a0292f706
child 374629 948d0a1ea5f85fc5cae41bc7632519a005b21cfb
child 374655 c6a5d15b94a1900f87644e3c5ca969ed8f4a16c3
child 390982 8000547183674ec092445b20f05105b6ea17aaab
push id1419
push userjlund@mozilla.com
push dateMon, 10 Apr 2017 20:44:07 +0000
treeherdermozilla-release@5e6801b73ef6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone53.0a1
first release with
nightly linux32
3119a9a0b5de / 53.0a1 / 20161227030213 / files
nightly linux64
3119a9a0b5de / 53.0a1 / 20161227030213 / files
nightly mac
3119a9a0b5de / 53.0a1 / 20161227030213 / files
nightly win32
3119a9a0b5de / 53.0a1 / 20161227030213 / files
nightly win64
3119a9a0b5de / 53.0a1 / 20161227030213 / 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 inbound to m-c. a=merge
intl/locale/DateTimeFormat.h
intl/locale/DateTimeFormatICU.cpp
intl/locale/DateTimeFormatUnix.cpp
--- a/devtools/client/themes/markup.css
+++ b/devtools/client/themes/markup.css
@@ -207,17 +207,17 @@ ul.children + .tag-line::before {
 .expandable.collapsed .close::before {
   /* Display an ellipsis character in collapsed nodes that can be expanded. */
   content: "\2026";
   display: inline-block;
   width: 12px;
   height: 8px;
   margin: 0 2px;
   line-height: 3px;
-  color: var(--theme-body-color-inactive);;
+  color: var(--theme-body-color-inactive);
   border-radius: 3px;
   border-style: solid;
   border-width: 1px;
   text-align: center;
   vertical-align: middle;
 }
 
 /* Hide HTML void elements (img, hr, br, …) closing tag when the element is not
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -126,16 +126,18 @@
 #include "nsIDOMHTMLFormElement.h"
 #include "nsIRequest.h"
 #include "nsHostObjectProtocolHandler.h"
 
 #include "nsCharsetSource.h"
 #include "nsIParser.h"
 #include "nsIContentSink.h"
 
+#include "nsDateTimeFormatCID.h"
+#include "nsIDateTimeFormat.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/InternalMutationEvent.h"
 #include "nsDOMCID.h"
 
 #include "jsapi.h"
 #include "nsIXPConnect.h"
 #include "xpcpublic.h"
--- a/dom/canvas/TexUnpackBlob.cpp
+++ b/dom/canvas/TexUnpackBlob.cpp
@@ -14,16 +14,75 @@
 #include "WebGLBuffer.h"
 #include "WebGLContext.h"
 #include "WebGLTexelConversions.h"
 #include "WebGLTexture.h"
 
 namespace mozilla {
 namespace webgl {
 
+static bool
+IsPIValidForDOM(const webgl::PackingInfo& pi)
+{
+    // https://www.khronos.org/registry/webgl/specs/latest/2.0/#TEXTURE_TYPES_FORMATS_FROM_DOM_ELEMENTS_TABLE
+
+    // Just check for invalid individual formats and types, not combinations.
+    switch (pi.format) {
+    case LOCAL_GL_RGB:
+    case LOCAL_GL_RGBA:
+    case LOCAL_GL_LUMINANCE_ALPHA:
+    case LOCAL_GL_LUMINANCE:
+    case LOCAL_GL_ALPHA:
+    case LOCAL_GL_RED:
+    case LOCAL_GL_RED_INTEGER:
+    case LOCAL_GL_RG:
+    case LOCAL_GL_RG_INTEGER:
+    case LOCAL_GL_RGB_INTEGER:
+    case LOCAL_GL_RGBA_INTEGER:
+        break;
+
+    case LOCAL_GL_SRGB:
+    case LOCAL_GL_SRGB_ALPHA:
+        // Allowed in WebGL1+EXT_srgb
+        break;
+
+    default:
+        return false;
+    }
+
+    switch (pi.type) {
+    case LOCAL_GL_UNSIGNED_BYTE:
+    case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
+    case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
+    case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
+    case LOCAL_GL_HALF_FLOAT:
+    case LOCAL_GL_HALF_FLOAT_OES:
+    case LOCAL_GL_FLOAT:
+    case LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV:
+        break;
+
+    default:
+        return false;
+    }
+
+    return true;
+}
+
+static bool
+ValidatePIForDOM(WebGLContext* webgl, const char* funcName,
+                 const webgl::PackingInfo& pi)
+{
+    if (!IsPIValidForDOM(pi)) {
+        webgl->ErrorInvalidOperation("%s: Format or type is invalid for DOM sources.",
+                                     funcName);
+        return false;
+    }
+    return true;
+}
+
 static WebGLTexelFormat
 FormatForPackingInfo(const PackingInfo& pi)
 {
     switch (pi.type) {
     case LOCAL_GL_UNSIGNED_BYTE:
         switch (pi.format) {
         case LOCAL_GL_RED:
         case LOCAL_GL_LUMINANCE:
@@ -506,16 +565,19 @@ TexUnpackImage::TexUnpackImage(const Web
 
 TexUnpackImage::~TexUnpackImage()
 { }
 
 bool
 TexUnpackImage::Validate(WebGLContext* webgl, const char* funcName,
                          const webgl::PackingInfo& pi)
 {
+    if (!ValidatePIForDOM(webgl, funcName, pi))
+        return false;
+
     const auto& fullRows = mImage->GetSize().height;
     return ValidateUnpackPixels(webgl, funcName, fullRows, 0, this);
 }
 
 bool
 TexUnpackImage::TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName,
                               WebGLTexture* tex, TexImageTarget target, GLint level,
                               const webgl::DriverUnpackInfo* dui, GLint xOffset,
@@ -669,16 +731,19 @@ GetFormatForSurf(gfx::SourceSurface* sur
 }
 
 //////////
 
 bool
 TexUnpackSurface::Validate(WebGLContext* webgl, const char* funcName,
                            const webgl::PackingInfo& pi)
 {
+    if (!ValidatePIForDOM(webgl, funcName, pi))
+        return false;
+
     const auto& fullRows = mSurf->GetSize().height;
     return ValidateUnpackPixels(webgl, funcName, fullRows, 0, this);
 }
 
 bool
 TexUnpackSurface::TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName,
                                 WebGLTexture* tex, TexImageTarget target, GLint level,
                                 const webgl::DriverUnpackInfo* dstDUI, GLint xOffset,
--- a/dom/canvas/WebGL2Context.h
+++ b/dom/canvas/WebGL2Context.h
@@ -271,17 +271,26 @@ public:
     void VertexAttribDivisor(GLuint index, GLuint divisor);
     void DrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei instanceCount);
     void DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, GLintptr offset, GLsizei instanceCount);
     */
 
     void DrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count,
                            GLenum type, WebGLintptr byteOffset)
     {
-        DrawElements(mode, count, type, byteOffset);
+        const char funcName[] = "drawRangeElements";
+        if (IsContextLost())
+            return;
+
+        if (end < start) {
+            ErrorInvalidValue("%s: end must be >= start.", funcName);
+            return;
+        }
+
+        DrawElements(mode, count, type, byteOffset, funcName);
     }
 
     // ------------------------------------------------------------------------
     // Multiple Render Targets - WebGL2ContextMRTs.cpp
     /* Implemented in WebGLContext
     void DrawBuffers(const dom::Sequence<GLenum>& buffers);
     */
 
--- a/dom/canvas/WebGL2ContextFramebuffers.cpp
+++ b/dom/canvas/WebGL2ContextFramebuffers.cpp
@@ -51,16 +51,20 @@ WebGL2Context::BlitFramebuffer(GLint src
     if (drawFB &&
         !drawFB->ValidateAndInitAttachments("blitFramebuffer's DRAW_FRAMEBUFFER"))
     {
         return;
     }
 
     ////
 
+    if (!mBoundReadFramebuffer) {
+        ClearBackbufferIfNeeded();
+    }
+
     WebGLFramebuffer::BlitFramebuffer(this,
                                       readFB, srcX0, srcY0, srcX1, srcY1,
                                       drawFB, dstX0, dstY0, dstX1, dstY1,
                                       mask, filter);
 }
 
 void
 WebGL2Context::FramebufferTextureLayer(GLenum target, GLenum attachment,
@@ -123,33 +127,37 @@ ValidateBackbufferAttachmentEnum(WebGLCo
         return false;
     }
 }
 
 static bool
 ValidateFramebufferAttachmentEnum(WebGLContext* webgl, const char* funcName,
                                   GLenum attachment)
 {
-    if (attachment >= LOCAL_GL_COLOR_ATTACHMENT0 &&
-        attachment <= webgl->LastColorAttachmentEnum())
-    {
-        return true;
-    }
-
     switch (attachment) {
     case LOCAL_GL_DEPTH_ATTACHMENT:
     case LOCAL_GL_STENCIL_ATTACHMENT:
     case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT:
         return true;
+    }
 
-    default:
+    if (attachment < LOCAL_GL_COLOR_ATTACHMENT0) {
         webgl->ErrorInvalidEnum("%s: attachment: invalid enum value 0x%x.",
                                 funcName, attachment);
         return false;
     }
+
+    if (attachment > webgl->LastColorAttachmentEnum()) {
+        // That these errors have different types is ridiculous.
+        webgl->ErrorInvalidOperation("%s: Too-large LOCAL_GL_COLOR_ATTACHMENTn.",
+                                     funcName);
+        return false;
+    }
+
+    return true;
 }
 
 bool
 WebGLContext::ValidateInvalidateFramebuffer(const char* funcName, GLenum target,
                                             const dom::Sequence<GLenum>& attachments,
                                             ErrorResult* const out_rv,
                                             std::vector<GLenum>* const scopedVector,
                                             GLsizei* const out_glNumAttachments,
@@ -216,16 +224,26 @@ WebGLContext::ValidateInvalidateFramebuf
                     MOZ_CRASH();
                 }
             }
             *out_glNumAttachments = scopedVector->size();
             *out_glAttachments = scopedVector->data();
         }
     }
 
+    ////
+
+    if (!fb) {
+        ClearBackbufferIfNeeded();
+
+        // Don't do more validation after these.
+        Invalidate();
+        mShouldPresent = true;
+    }
+
     return true;
 }
 
 void
 WebGL2Context::InvalidateFramebuffer(GLenum target,
                                      const dom::Sequence<GLenum>& attachments,
                                      ErrorResult& rv)
 {
@@ -256,31 +274,31 @@ WebGL2Context::InvalidateFramebuffer(GLe
 
 void
 WebGL2Context::InvalidateSubFramebuffer(GLenum target, const dom::Sequence<GLenum>& attachments,
                                         GLint x, GLint y, GLsizei width, GLsizei height,
                                         ErrorResult& rv)
 {
     const char funcName[] = "invalidateSubFramebuffer";
 
+    if (!ValidateNonNegative(funcName, "width", width) ||
+        !ValidateNonNegative(funcName, "height", height))
+    {
+        return;
+    }
+
     std::vector<GLenum> scopedVector;
     GLsizei glNumAttachments;
     const GLenum* glAttachments;
     if (!ValidateInvalidateFramebuffer(funcName, target, attachments, &rv, &scopedVector,
                                        &glNumAttachments, &glAttachments))
     {
         return;
     }
 
-    if (!ValidateNonNegative(funcName, "width", width) ||
-        !ValidateNonNegative(funcName, "height", height))
-    {
-        return;
-    }
-
     ////
 
     // Some drivers (like OSX 10.9 GL) just don't support invalidate_framebuffer.
     const bool useFBInvalidation = (mAllowFBInvalidation &&
                                     gl->IsSupported(gl::GLFeature::invalidate_framebuffer));
     if (useFBInvalidation) {
         gl->fInvalidateSubFramebuffer(target, glNumAttachments, glAttachments, x, y,
                                       width, height);
--- a/dom/canvas/WebGL2ContextMRTs.cpp
+++ b/dom/canvas/WebGL2ContextMRTs.cpp
@@ -78,40 +78,46 @@ WebGL2Context::ValidateClearBuffer(const
 void
 WebGL2Context::ClearBufferfv(GLenum buffer, GLint drawBuffer, const Float32Arr& src,
                              GLuint srcElemOffset)
 {
     const char funcName[] = "clearBufferfv";
     if (!ValidateClearBuffer(funcName, buffer, drawBuffer, src.elemCount, srcElemOffset))
         return;
 
+    ScopedDrawCallWrapper wrapper(*this);
+
     const auto ptr = src.elemBytes + srcElemOffset;
     gl->fClearBufferfv(buffer, drawBuffer, ptr);
 }
 
 void
 WebGL2Context::ClearBufferiv(GLenum buffer, GLint drawBuffer, const Int32Arr& src,
                              GLuint srcElemOffset)
 {
     const char funcName[] = "clearBufferiv";
     if (!ValidateClearBuffer(funcName, buffer, drawBuffer, src.elemCount, srcElemOffset))
         return;
 
+    ScopedDrawCallWrapper wrapper(*this);
+
     const auto ptr = src.elemBytes + srcElemOffset;
     gl->fClearBufferiv(buffer, drawBuffer, ptr);
 }
 
 void
 WebGL2Context::ClearBufferuiv(GLenum buffer, GLint drawBuffer, const Uint32Arr& src,
-                             GLuint srcElemOffset)
+                              GLuint srcElemOffset)
 {
     const char funcName[] = "clearBufferuiv";
     if (!ValidateClearBuffer(funcName, buffer, drawBuffer, src.elemCount, srcElemOffset))
         return;
 
+    ScopedDrawCallWrapper wrapper(*this);
+
     const auto ptr = src.elemBytes + srcElemOffset;
     gl->fClearBufferuiv(buffer, drawBuffer, ptr);
 }
 
 ////
 
 void
 WebGL2Context::ClearBufferfi(GLenum buffer, GLint drawBuffer, GLfloat depth,
@@ -119,12 +125,14 @@ WebGL2Context::ClearBufferfi(GLenum buff
 {
     const char funcName[] = "clearBufferfi";
     if (!ValidateClearBuffer(funcName, LOCAL_GL_DEPTH, drawBuffer, 1, 0))
         return;
 
     if (buffer != LOCAL_GL_DEPTH_STENCIL)
         return ErrorInvalidEnumInfo(funcName, buffer);
 
+    ScopedDrawCallWrapper wrapper(*this);
+
     gl->fClearBufferfi(buffer, drawBuffer, depth, stencil);
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGL2ContextSamplers.cpp
+++ b/dom/canvas/WebGL2ContextSamplers.cpp
@@ -67,39 +67,39 @@ WebGL2Context::BindSampler(GLuint unit, 
     gl->MakeCurrent();
     gl->fBindSampler(unit, sampler ? sampler->mGLName : 0);
 
     InvalidateResolveCacheForTextureWithTexUnit(unit);
     mBoundSamplers[unit] = sampler;
 }
 
 void
-WebGL2Context::SamplerParameteri(WebGLSampler& sampler, GLenum pname, GLint paramInt)
+WebGL2Context::SamplerParameteri(WebGLSampler& sampler, GLenum pname, GLint param)
 {
     const char funcName[] = "samplerParameteri";
     if (IsContextLost())
         return;
 
     if (!ValidateObject(funcName, sampler))
         return;
 
-    sampler.SamplerParameter(funcName, pname, paramInt);
+    sampler.SamplerParameter(funcName, pname, FloatOrInt(param));
 }
 
 void
-WebGL2Context::SamplerParameterf(WebGLSampler& sampler, GLenum pname, GLfloat paramFloat)
+WebGL2Context::SamplerParameterf(WebGLSampler& sampler, GLenum pname, GLfloat param)
 {
     const char funcName[] = "samplerParameterf";
     if (IsContextLost())
         return;
 
     if (!ValidateObject(funcName, sampler))
         return;
 
-    sampler.SamplerParameter(funcName, pname, WebGLIntOrFloat(paramFloat).AsInt());
+    sampler.SamplerParameter(funcName, pname, FloatOrInt(param));
 }
 
 void
 WebGL2Context::GetSamplerParameter(JSContext*, const WebGLSampler& sampler, GLenum pname,
                                    JS::MutableHandleValue retval)
 {
     const char funcName[] = "getSamplerParameter";
     retval.setNull();
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -2033,55 +2033,64 @@ WebGLContext::ValidateCurFBForRead(const
     }
 
     return mBoundReadFramebuffer->ValidateForRead(funcName, out_format, out_width,
                                                   out_height);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
-WebGLContext::ScopedMaskWorkaround::ScopedMaskWorkaround(WebGLContext& webgl)
+WebGLContext::ScopedDrawCallWrapper::ScopedDrawCallWrapper(WebGLContext& webgl)
     : mWebGL(webgl)
     , mFakeNoAlpha(ShouldFakeNoAlpha(webgl))
     , mFakeNoDepth(ShouldFakeNoDepth(webgl))
     , mFakeNoStencil(ShouldFakeNoStencil(webgl))
 {
+    if (!mWebGL.mBoundDrawFramebuffer) {
+        mWebGL.ClearBackbufferIfNeeded();
+    }
+
     if (mFakeNoAlpha) {
         mWebGL.gl->fColorMask(mWebGL.mColorWriteMask[0],
                               mWebGL.mColorWriteMask[1],
                               mWebGL.mColorWriteMask[2],
                               false);
     }
     if (mFakeNoDepth) {
         mWebGL.gl->fDisable(LOCAL_GL_DEPTH_TEST);
     }
     if (mFakeNoStencil) {
         mWebGL.gl->fDisable(LOCAL_GL_STENCIL_TEST);
     }
 }
 
-WebGLContext::ScopedMaskWorkaround::~ScopedMaskWorkaround()
+WebGLContext::ScopedDrawCallWrapper::~ScopedDrawCallWrapper()
 {
     if (mFakeNoAlpha) {
         mWebGL.gl->fColorMask(mWebGL.mColorWriteMask[0],
                               mWebGL.mColorWriteMask[1],
                               mWebGL.mColorWriteMask[2],
                               mWebGL.mColorWriteMask[3]);
     }
     if (mFakeNoDepth) {
         mWebGL.gl->fEnable(LOCAL_GL_DEPTH_TEST);
     }
     if (mFakeNoStencil) {
         MOZ_ASSERT(mWebGL.mStencilTestEnabled);
         mWebGL.gl->fEnable(LOCAL_GL_STENCIL_TEST);
     }
+
+    if (!mWebGL.mBoundDrawFramebuffer) {
+        mWebGL.Invalidate();
+        mWebGL.mShouldPresent = true;
+    }
 }
 
 /*static*/ bool
-WebGLContext::ScopedMaskWorkaround::HasDepthButNoStencil(const WebGLFramebuffer* fb)
+WebGLContext::ScopedDrawCallWrapper::HasDepthButNoStencil(const WebGLFramebuffer* fb)
 {
     const auto& depth = fb->DepthAttachment();
     const auto& stencil = fb->StencilAttachment();
     return depth.IsDefined() && !stencil.IsDefined();
 }
 
 ////////////////////////////////////////
 
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -190,16 +190,42 @@ struct IndexedBufferBinding
     uint64_t mRangeStart;
     uint64_t mRangeSize;
 
     IndexedBufferBinding();
 
     uint64_t ByteCount() const;
 };
 
+////
+
+struct FloatOrInt final // For TexParameter[fi] and friends.
+{
+    const bool isFloat;
+    const GLfloat f;
+    const GLint i;
+
+    explicit FloatOrInt(GLint x)
+        : isFloat(false)
+        , f(x)
+        , i(x)
+    { }
+
+    explicit FloatOrInt(GLfloat x)
+        : isFloat(true)
+        , f(x)
+        , i(roundf(x))
+    { }
+
+    FloatOrInt& operator =(const FloatOrInt& x) {
+        memcpy(this, &x, sizeof(x));
+        return *this;
+    }
+};
+
 ////////////////////////////////////
 
 struct TexImageSource
 {
     const dom::ArrayBufferView* mView;
     GLuint mViewElemOffset;
     GLuint mViewElemLengthOverride;
 
@@ -628,16 +654,22 @@ public:
     bool IsVertexArray(const WebGLVertexArray* vao);
     void LineWidth(GLfloat width);
     void LinkProgram(WebGLProgram& prog);
     void PixelStorei(GLenum pname, GLint param);
     void PolygonOffset(GLfloat factor, GLfloat units);
 
     already_AddRefed<layers::SharedSurfaceTextureClient> GetVRFrame();
     bool StartVRPresentation();
+
+    ////
+
+    webgl::PackingInfo
+    ValidImplementationColorReadPI(const webgl::FormatUsageInfo* usage) const;
+
 protected:
     bool ReadPixels_SharedPrecheck(ErrorResult* const out_error);
     void ReadPixelsImpl(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format,
                         GLenum type, void* data, uint32_t dataLen);
     bool DoReadPixelsAndConvert(const webgl::FormatInfo* srcFormat, GLint x, GLint y,
                                 GLsizei width, GLsizei height, GLenum format,
                                 GLenum destType, void* dest, uint32_t dataLen,
                                 uint32_t rowStride);
@@ -656,16 +688,18 @@ public:
 
     void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format,
                     GLenum type, WebGLsizeiptr offset, ErrorResult& out_error);
 
     void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format,
                     GLenum type, const dom::ArrayBufferView& dstData, GLuint dstOffset,
                     ErrorResult& out_error);
 
+    ////
+
     void RenderbufferStorage(GLenum target, GLenum internalFormat,
                              GLsizei width, GLsizei height);
 protected:
     void RenderbufferStorage_base(const char* funcName, GLenum target,
                                   GLsizei samples, GLenum internalformat,
                                   GLsizei width, GLsizei height);
 public:
     void SampleCoverage(GLclampf value, WebGLboolean invert);
@@ -997,27 +1031,26 @@ public:
                          JS::MutableHandle<JS::Value> retval)
     {
         retval.set(GetTexParameter(texTarget, pname));
     }
 
     bool IsTexture(WebGLTexture* tex);
 
     void TexParameterf(GLenum texTarget, GLenum pname, GLfloat param) {
-        TexParameter_base(texTarget, pname, nullptr, &param);
+        TexParameter_base(texTarget, pname, FloatOrInt(param));
     }
 
     void TexParameteri(GLenum texTarget, GLenum pname, GLint param) {
-        TexParameter_base(texTarget, pname, &param, nullptr);
+        TexParameter_base(texTarget, pname, FloatOrInt(param));
     }
 
 protected:
     JS::Value GetTexParameter(GLenum texTarget, GLenum pname);
-    void TexParameter_base(GLenum texTarget, GLenum pname, GLint* maybeIntParam,
-                           GLfloat* maybeFloatParam);
+    void TexParameter_base(GLenum texTarget, GLenum pname, const FloatOrInt& param);
 
     virtual bool IsTexParamValid(GLenum pname) const;
 
     ////////////////////////////////////
 
 public:
     template<typename T>
     void CompressedTexImage2D(GLenum target, GLint level, GLenum internalFormat,
@@ -1230,17 +1263,17 @@ protected:
 // Vertices Feature (WebGLContextVertices.cpp)
     GLenum mPrimRestartTypeBytes;
 
 public:
     void DrawArrays(GLenum mode, GLint first, GLsizei count);
     void DrawArraysInstanced(GLenum mode, GLint first, GLsizei count,
                              GLsizei primcount);
     void DrawElements(GLenum mode, GLsizei count, GLenum type,
-                      WebGLintptr byteOffset);
+                      WebGLintptr byteOffset, const char* funcName = nullptr);
     void DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type,
                                WebGLintptr byteOffset, GLsizei primcount);
 
     void EnableVertexAttribArray(GLuint index);
     void DisableVertexAttribArray(GLuint index);
 
     JS::Value GetVertexAttrib(JSContext* cx, GLuint index, GLenum pname,
                               ErrorResult& rv);
@@ -1896,17 +1929,17 @@ protected:
     bool mNeedsFakeNoDepth;
     bool mNeedsFakeNoStencil;
     bool mNeedsEmulatedLoneDepthStencil;
 
     const bool mAllowFBInvalidation;
 
     bool Has64BitTimestamps() const;
 
-    struct ScopedMaskWorkaround {
+    struct ScopedDrawCallWrapper final {
         WebGLContext& mWebGL;
         const bool mFakeNoAlpha;
         const bool mFakeNoDepth;
         const bool mFakeNoStencil;
 
         static bool ShouldFakeNoAlpha(WebGLContext& webgl) {
             // We should only be doing this if we're about to draw to the backbuffer, but
             // the backbuffer needs to have this fake-no-alpha workaround.
@@ -1945,19 +1978,19 @@ protected:
                 HasDepthButNoStencil(webgl.mBoundDrawFramebuffer))
             {
                 return true;
             }
 
             return false;
         }
 
-        explicit ScopedMaskWorkaround(WebGLContext& webgl);
+        explicit ScopedDrawCallWrapper(WebGLContext& webgl);
 
-        ~ScopedMaskWorkaround();
+        ~ScopedDrawCallWrapper();
     };
 
     void LoseOldestWebGLContextIfLimitExceeded();
     void UpdateLastUseIndex();
 
     template <typename WebGLObjectType>
     JS::Value WebGLObjectAsJSValue(JSContext* cx, const WebGLObjectType*,
                                    ErrorResult& rv) const;
--- a/dom/canvas/WebGLContextBuffers.cpp
+++ b/dom/canvas/WebGLContextBuffers.cpp
@@ -98,17 +98,17 @@ WebGLContext::ValidateIndexedBufferSlot(
         break;
 
     default:
         ErrorInvalidEnum("%s: Bad `target`: 0x%04x", funcName, target);
         return nullptr;
     }
 
     if (index >= bindings->size()) {
-        ErrorInvalidOperation("%s: `index` >= %s.", funcName, maxIndexEnum);
+        ErrorInvalidValue("%s: `index` >= %s.", funcName, maxIndexEnum);
         return nullptr;
     }
 
     return &(*bindings)[index];
 }
 
 ////////////////////////////////////////
 
@@ -243,16 +243,21 @@ WebGLContext::BindBufferRange(GLenum tar
                                       &indexedBinding))
     {
         return;
     }
 
     if (buffer && !buffer->ValidateCanBindToTarget(funcName, target))
         return;
 
+    if (buffer && !size) {
+        ErrorInvalidValue("%s: size must be non-zero for non-null buffer.", funcName);
+        return;
+    }
+
     ////
 
     gl->MakeCurrent();
 
     switch (target) {
     case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
         if (offset % 4 != 0 || size % 4 != 0) {
             ErrorInvalidValue("%s: For %s, `offset` and `size` must be multiples of 4.",
--- a/dom/canvas/WebGLContextDraw.cpp
+++ b/dom/canvas/WebGLContextDraw.cpp
@@ -509,17 +509,17 @@ WebGLContext::DrawArrays(GLenum mode, GL
         return;
 
     const ScopedDrawWithTransformFeedback scopedTF(this, funcName, mode, vertCount,
                                                    instanceCount, &error);
     if (error)
         return;
 
     {
-        ScopedMaskWorkaround autoMask(*this);
+        ScopedDrawCallWrapper wrapper(*this);
         gl->fDrawArrays(mode, first, vertCount);
     }
 
     Draw_cleanup(funcName);
     scopedTF.Advance();
 }
 
 void
@@ -548,17 +548,17 @@ WebGLContext::DrawArraysInstanced(GLenum
         return;
 
     const ScopedDrawWithTransformFeedback scopedTF(this, funcName, mode, vertCount,
                                                    instanceCount, &error);
     if (error)
         return;
 
     {
-        ScopedMaskWorkaround autoMask(*this);
+        ScopedDrawCallWrapper wrapper(*this);
         gl->fDrawArraysInstanced(mode, first, vertCount, instanceCount);
     }
 
     Draw_cleanup(funcName);
     scopedTF.Advance();
 }
 
 ////////////////////////////////////////
@@ -691,19 +691,22 @@ WebGLContext::DrawElements_check(const c
                         funcName, WebGLContext::EnumName(type));
     }
 
     return true;
 }
 
 void
 WebGLContext::DrawElements(GLenum mode, GLsizei vertCount, GLenum type,
-                           WebGLintptr byteOffset)
+                           WebGLintptr byteOffset, const char* funcName)
 {
-    const char funcName[] = "drawElements";
+    if (!funcName) {
+        funcName = "drawElements";
+    }
+
     if (IsContextLost())
         return;
 
     MakeContextCurrent();
 
     bool error = false;
     ScopedResolveTexturesForDraw scopedResolve(this, funcName, &error);
     if (error)
@@ -714,17 +717,17 @@ WebGLContext::DrawElements(GLenum mode, 
         return;
 
     const ScopedDrawHelper scopedHelper(this, funcName, 0, mMaxFetchedVertices, instanceCount,
                                         &error);
     if (error)
         return;
 
     {
-        ScopedMaskWorkaround autoMask(*this);
+        ScopedDrawCallWrapper wrapper(*this);
         gl->fDrawElements(mode, vertCount, type,
                           reinterpret_cast<GLvoid*>(byteOffset));
     }
 
     Draw_cleanup(funcName);
 }
 
 void
@@ -749,36 +752,30 @@ WebGLContext::DrawElementsInstanced(GLen
         return;
 
     const ScopedDrawHelper scopedHelper(this, funcName, 0, mMaxFetchedVertices, instanceCount,
                                         &error);
     if (error)
         return;
 
     {
-        ScopedMaskWorkaround autoMask(*this);
+        ScopedDrawCallWrapper wrapper(*this);
         gl->fDrawElementsInstanced(mode, vertCount, type,
                                    reinterpret_cast<GLvoid*>(byteOffset),
                                    instanceCount);
     }
 
     Draw_cleanup(funcName);
 }
 
 ////////////////////////////////////////
 
 void
 WebGLContext::Draw_cleanup(const char* funcName)
 {
-    if (!mBoundDrawFramebuffer) {
-        Invalidate();
-        mShouldPresent = true;
-        MOZ_ASSERT(!mBackbufferNeedsClear);
-    }
-
     if (gl->WorkAroundDriverBugs()) {
         if (gl->Renderer() == gl::GLRenderer::Tegra) {
             mDrawCallsSinceLastFlush++;
 
             if (mDrawCallsSinceLastFlush >= MAX_DRAW_CALLS_SINCE_FLUSH) {
                 gl->fFlush();
                 mDrawCallsSinceLastFlush = 0;
             }
--- a/dom/canvas/WebGLContextFramebufferOperations.cpp
+++ b/dom/canvas/WebGLContextFramebufferOperations.cpp
@@ -27,34 +27,24 @@ WebGLContext::Clear(GLbitfield mask)
         return ErrorInvalidValue("%s: invalid mask bits", funcName);
 
     if (mask == 0) {
         GenerateWarning("Calling gl.clear(0) has no effect.");
     } else if (mRasterizerDiscardEnabled) {
         GenerateWarning("Calling gl.clear() with RASTERIZER_DISCARD enabled has no effects.");
     }
 
-    if (mBoundDrawFramebuffer) {
-        if (!mBoundDrawFramebuffer->ValidateAndInitAttachments(funcName))
-            return;
-
-        gl->fClear(mask);
+    if (mBoundDrawFramebuffer &&
+        !mBoundDrawFramebuffer->ValidateAndInitAttachments(funcName))
+    {
         return;
-    } else {
-        ClearBackbufferIfNeeded();
     }
 
-    // Ok, we're clearing the default framebuffer/screen.
-    {
-        ScopedMaskWorkaround autoMask(*this);
-        gl->fClear(mask);
-    }
-
-    Invalidate();
-    mShouldPresent = true;
+    ScopedDrawCallWrapper wrapper(*this);
+    gl->fClear(mask);
 }
 
 static GLfloat
 GLClampFloat(GLfloat val)
 {
     if (val < 0.0)
         return 0.0;
 
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -1373,116 +1373,129 @@ WebGLContext::ReadPixels(GLint x, GLint 
     }
 
     gl->MakeCurrent();
     const ScopedLazyBind lazyBind(gl, LOCAL_GL_PIXEL_PACK_BUFFER, buffer);
 
     ReadPixelsImpl(x, y, width, height, format, type, (void*)offset, bytesAfterOffset);
 }
 
-static bool
-ValidateReadPixelsFormatAndType(const webgl::FormatInfo* srcFormat,
-                                const webgl::PackingInfo& pi, gl::GLContext* gl,
-                                WebGLContext* webgl)
+static webgl::PackingInfo
+DefaultReadPixelPI(const webgl::FormatUsageInfo* usage)
 {
-    // Check the format and type params to assure they are an acceptable pair (as per spec)
-    GLenum mainFormat;
-    GLenum mainType;
-
-    switch (srcFormat->componentType) {
-    case webgl::ComponentType::Float:
-        mainFormat = LOCAL_GL_RGBA;
-        mainType = LOCAL_GL_FLOAT;
-        break;
-
-    case webgl::ComponentType::UInt:
-        mainFormat = LOCAL_GL_RGBA_INTEGER;
-        mainType = LOCAL_GL_UNSIGNED_INT;
-        break;
+    MOZ_ASSERT(usage->IsRenderable());
+
+    switch (usage->format->componentType) {
+    case webgl::ComponentType::NormUInt:
+        return { LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE };
 
     case webgl::ComponentType::Int:
-        mainFormat = LOCAL_GL_RGBA_INTEGER;
-        mainType = LOCAL_GL_INT;
-        break;
-
-    case webgl::ComponentType::NormInt:
-    case webgl::ComponentType::NormUInt:
-        mainFormat = LOCAL_GL_RGBA;
-        mainType = LOCAL_GL_UNSIGNED_BYTE;
-        break;
+        return { LOCAL_GL_RGBA_INTEGER, LOCAL_GL_INT };
+
+    case webgl::ComponentType::UInt:
+        return { LOCAL_GL_RGBA_INTEGER, LOCAL_GL_UNSIGNED_INT };
+
+    case webgl::ComponentType::Float:
+        return { LOCAL_GL_RGBA, LOCAL_GL_FLOAT };
 
     default:
-        gfxCriticalNote << "Unhandled srcFormat->componentType: "
-                        << (uint32_t)srcFormat->componentType;
-        webgl->ErrorInvalidOperation("readPixels: Unhandled srcFormat->componentType."
-                                     " Please file a bug!");
-        return false;
+        MOZ_CRASH();
     }
-
-    if (pi.format == mainFormat && pi.type == mainType)
-        return true;
-
-    //////
-    // OpenGL ES 3.0.4 p194 - When the internal format of the rendering surface is
-    // RGB10_A2, a third combination of format RGBA and type UNSIGNED_INT_2_10_10_10_REV
-    // is accepted.
-
-    if (webgl->IsWebGL2() &&
-        srcFormat->effectiveFormat == webgl::EffectiveFormat::RGB10_A2 &&
-        pi.format == LOCAL_GL_RGBA &&
-        pi.type == LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV)
-    {
-        return true;
-    }
-
-    //////
+}
+
+static bool
+ArePossiblePackEnums(const WebGLContext* webgl, const webgl::PackingInfo& pi)
+{
     // OpenGL ES 2.0 $4.3.1 - IMPLEMENTATION_COLOR_READ_{TYPE/FORMAT} is a valid
     // combination for glReadPixels()...
 
     // So yeah, we are actually checking that these are valid as /unpack/ formats, instead
     // of /pack/ formats here, but it should cover the INVALID_ENUM cases.
-    if (!webgl->mFormatUsage->AreUnpackEnumsValid(pi.format, pi.type)) {
-        webgl->ErrorInvalidEnum("readPixels: Bad format and/or type.");
+    if (!webgl->mFormatUsage->AreUnpackEnumsValid(pi.format, pi.type))
         return false;
-    }
 
     // Only valid when pulled from:
     // * GLES 2.0.25 p105:
     //   "table 3.4, excluding formats LUMINANCE and LUMINANCE_ALPHA."
     // * GLES 3.0.4 p193:
     //   "table 3.2, excluding formats DEPTH_COMPONENT and DEPTH_STENCIL."
     switch (pi.format) {
     case LOCAL_GL_LUMINANCE:
     case LOCAL_GL_LUMINANCE_ALPHA:
     case LOCAL_GL_DEPTH_COMPONENT:
     case LOCAL_GL_DEPTH_STENCIL:
-        webgl->ErrorInvalidEnum("readPixels: Invalid format: 0x%04x", pi.format);
         return false;
     }
 
-    if (pi.type == LOCAL_GL_UNSIGNED_INT_24_8) {
-        webgl->ErrorInvalidEnum("readPixels: Invalid type: 0x%04x", pi.type);
+    if (pi.type == LOCAL_GL_UNSIGNED_INT_24_8)
+        return false;
+
+    return true;
+}
+
+webgl::PackingInfo
+WebGLContext::ValidImplementationColorReadPI(const webgl::FormatUsageInfo* usage) const
+{
+    const auto defaultPI = DefaultReadPixelPI(usage);
+
+    // ES2_compatibility always returns RGBA/UNSIGNED_BYTE, so branch on actual IsGLES().
+    // Also OSX+NV generates an error here.
+    if (!gl->IsGLES())
+        return defaultPI;
+
+    webgl::PackingInfo implPI;
+    gl->fGetIntegerv(LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT, (GLint*)&implPI.format);
+    gl->fGetIntegerv(LOCAL_GL_IMPLEMENTATION_COLOR_READ_TYPE, (GLint*)&implPI.type);
+
+    if (!ArePossiblePackEnums(this, implPI))
+        return defaultPI;
+
+    return implPI;
+}
+
+static bool
+ValidateReadPixelsFormatAndType(const webgl::FormatUsageInfo* srcUsage,
+                                const webgl::PackingInfo& pi, gl::GLContext* gl,
+                                WebGLContext* webgl)
+{
+    const char funcName[] = "readPixels";
+
+    if (!ArePossiblePackEnums(webgl, pi)) {
+        webgl->ErrorInvalidEnum("%s: Unexpected format or type.", funcName);
         return false;
     }
 
+    const auto defaultPI = DefaultReadPixelPI(srcUsage);
+    if (pi == defaultPI)
+        return true;
+
+    ////
+
+    // OpenGL ES 3.0.4 p194 - When the internal format of the rendering surface is
+    // RGB10_A2, a third combination of format RGBA and type UNSIGNED_INT_2_10_10_10_REV
+    // is accepted.
+
+    if (webgl->IsWebGL2() &&
+        srcUsage->format->effectiveFormat == webgl::EffectiveFormat::RGB10_A2 &&
+        pi.format == LOCAL_GL_RGBA &&
+        pi.type == LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV)
+    {
+        return true;
+    }
+
+    ////
+
     MOZ_ASSERT(gl->IsCurrent());
-    if (gl->IsGLES()) {
-        const auto auxFormat = gl->GetIntAs<GLenum>(LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT);
-        const auto auxType = gl->GetIntAs<GLenum>(LOCAL_GL_IMPLEMENTATION_COLOR_READ_TYPE);
-
-        if (auxFormat && auxType &&
-            pi.format == auxFormat && pi.type == auxType)
-        {
-            return true;
-        }
-    }
-
-    //////
-
-    webgl->ErrorInvalidOperation("readPixels: Invalid format or type.");
+    const auto implPI = webgl->ValidImplementationColorReadPI(srcUsage);
+    if (pi == implPI)
+        return true;
+
+    ////
+
+    webgl->ErrorInvalidOperation("%s: Incompatible format or type.", funcName);
     return false;
 }
 
 void
 WebGLContext::ReadPixelsImpl(GLint x, GLint y, GLsizei rawWidth, GLsizei rawHeight,
                              GLenum packFormat, GLenum packType, void* dest,
                              uint32_t dataLen)
 {
@@ -1502,17 +1515,17 @@ WebGLContext::ReadPixelsImpl(GLint x, GL
     uint32_t srcWidth;
     uint32_t srcHeight;
     if (!ValidateCurFBForRead("readPixels", &srcFormat, &srcWidth, &srcHeight))
         return;
 
     //////
 
     const webgl::PackingInfo pi = {packFormat, packType};
-    if (!ValidateReadPixelsFormatAndType(srcFormat->format, pi, gl, this))
+    if (!ValidateReadPixelsFormatAndType(srcFormat, pi, gl, this))
         return;
 
     uint8_t bytesPerPixel;
     if (!webgl::GetBytesPerPixel(pi, &bytesPerPixel)) {
         ErrorInvalidOperation("readPixels: Unsupported format and type.");
         return;
     }
 
@@ -2041,17 +2054,17 @@ WebGLContext::UniformMatrixAxBfv(const c
 
         &gl::GLContext::fUniformMatrix4x2fv,
         &gl::GLContext::fUniformMatrix4x3fv,
         &gl::GLContext::fUniformMatrix4fv
     };
     const auto func = kFuncList[3*(A-2) + (B-2)];
 
     MakeContextCurrent();
-    (gl->*func)(loc->mLoc, numElementsToUpload, false, elemBytes);
+    (gl->*func)(loc->mLoc, numElementsToUpload, transpose, elemBytes);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
 void
 WebGLContext::UseProgram(WebGLProgram* prog)
 {
     if (IsContextLost())
--- a/dom/canvas/WebGLContextState.cpp
+++ b/dom/canvas/WebGLContextState.cpp
@@ -237,27 +237,25 @@ WebGLContext::GetParameter(JSContext* cx
             return JS::Int32Value(mImplMaxColorAttachments);
 
         } else if (pname == LOCAL_GL_MAX_DRAW_BUFFERS) {
             return JS::Int32Value(mImplMaxDrawBuffers);
 
         } else if (pname >= LOCAL_GL_DRAW_BUFFER0 &&
                    pname < GLenum(LOCAL_GL_DRAW_BUFFER0 + mImplMaxDrawBuffers))
         {
-            GLint iv = 0;
-            gl->fGetIntegerv(pname, &iv);
-
-            if (mBoundDrawFramebuffer)
-                return JS::Int32Value(iv);
-
-            const GLint index = (pname - LOCAL_GL_DRAW_BUFFER0);
-            if (iv == LOCAL_GL_COLOR_ATTACHMENT0 + index)
-                return JS::Int32Value(LOCAL_GL_BACK);
-
-            return JS::Int32Value(LOCAL_GL_NONE);
+            GLint ret = LOCAL_GL_NONE;
+            if (!mBoundDrawFramebuffer) {
+                if (pname == LOCAL_GL_DRAW_BUFFER0) {
+                    ret = gl->Screen()->GetDrawBufferMode();
+                }
+            } else {
+                gl->fGetIntegerv(pname, &ret);
+            }
+            return JS::Int32Value(ret);
         }
     }
 
     if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::OES_vertex_array_object)) {
         if (pname == LOCAL_GL_VERTEX_ARRAY_BINDING) {
             WebGLVertexArray* vao =
                 (mBoundVertexArray != mDefaultVertexArray) ? mBoundVertexArray.get() : nullptr;
             return WebGLObjectAsJSValue(cx, vao, rv);
@@ -390,58 +388,34 @@ WebGLContext::GetParameter(JSContext* cx
             GLint i = 0;
             gl->fGetIntegerv(pname, &i);
             return JS::NumberValue(uint32_t(i));
         }
 
         case LOCAL_GL_GENERATE_MIPMAP_HINT:
             return JS::NumberValue(mGenerateMipmapHint);
 
+        case LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT:
         case LOCAL_GL_IMPLEMENTATION_COLOR_READ_TYPE: {
             const webgl::FormatUsageInfo* usage;
             uint32_t width, height;
             if (!ValidateCurFBForRead(funcName, &usage, &width, &height))
                 return JS::NullValue();
 
-            GLint i = 0;
-            if (gl->IsGLES()) {
-                // ES2_compatibility always returns UNSIGNED_BYTE here, so
-                // branch on actual IsGLES().
-                // Also OSX+NV generates an error here.
-                gl->fGetIntegerv(pname, &i);
-            } else {
-                i = LOCAL_GL_UNSIGNED_BYTE;
-            }
-            return JS::NumberValue(uint32_t(i));
-        }
-        case LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT: {
-            const webgl::FormatUsageInfo* usage;
-            uint32_t width, height;
-            if (!ValidateCurFBForRead(funcName, &usage, &width, &height))
-                return JS::NullValue();
+            const auto implPI = ValidImplementationColorReadPI(usage);
 
-            GLint i = 0;
-            if (gl->IsGLES()) {
-                // ES2_compatibility always returns UNSIGNED_BYTE here, so
-                // branch on actual IsGLES().
-                // Also OSX+NV generates an error here.
-                gl->fGetIntegerv(pname, &i);
+            GLenum ret;
+            if (pname == LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT) {
+                ret = implPI.format;
             } else {
-                i = LOCAL_GL_RGBA;
+                ret = implPI.type;
             }
+            return JS::NumberValue(uint32_t(ret));
+        }
 
-            // OpenGL ES 3.0.4 p112 Table 3.2 shows that read format SRGB_ALPHA is
-            // not supported. And if internal format of fbo is SRGB8_ALPHA8, then
-            // IMPLEMENTATION_COLOR_READ_FORMAT is SRGB_ALPHA which is not supported
-            // by ReadPixels. So, just return RGBA here.
-            if (i == LOCAL_GL_SRGB_ALPHA)
-                i = LOCAL_GL_RGBA;
-
-            return JS::NumberValue(uint32_t(i));
-        }
         // int
         case LOCAL_GL_STENCIL_REF:
         case LOCAL_GL_STENCIL_BACK_REF: {
             GLint stencilBits = 0;
             if (!GetStencilBits(&stencilBits))
                 return JS::NullValue();
 
             // Assuming stencils have 8 bits
--- a/dom/canvas/WebGLContextTextures.cpp
+++ b/dom/canvas/WebGLContextTextures.cpp
@@ -292,30 +292,28 @@ WebGLContext::IsTexture(WebGLTexture* te
 {
     if (!ValidateIsObject("isTexture", tex))
         return false;
 
     return tex->IsTexture();
 }
 
 void
-WebGLContext::TexParameter_base(GLenum rawTexTarget, GLenum pname, GLint* maybeIntParam,
-                                GLfloat* maybeFloatParam)
+WebGLContext::TexParameter_base(GLenum rawTexTarget, GLenum pname,
+                                const FloatOrInt& param)
 {
-    MOZ_ASSERT(maybeIntParam || maybeFloatParam);
-
     const char funcName[] = "texParameter";
     const uint8_t funcDims = 0;
 
     TexTarget texTarget;
     WebGLTexture* tex;
     if (!ValidateTexTarget(this, funcName, funcDims, rawTexTarget, &texTarget, &tex))
         return;
 
-    tex->TexParameter(texTarget, pname, maybeIntParam, maybeFloatParam);
+    tex->TexParameter(texTarget, pname, param);
 }
 
 //////////////////////////////////////////////////////////////////////////////////////////
 // Uploads
 
 void
 WebGLContext::CompressedTexImage(const char* funcName, uint8_t funcDims, GLenum rawTarget,
                                  GLint level, GLenum internalFormat, GLsizei width,
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -357,16 +357,26 @@ WebGLContext::ValidateAttribPointer(bool
     MOZ_ASSERT(IsPowerOfTwo(requiredAlignment));
     GLsizei requiredAlignmentMask = requiredAlignment - 1;
 
     if (size < 1 || size > 4) {
         ErrorInvalidValue("%s: invalid element size", info);
         return false;
     }
 
+    switch (type) {
+    case LOCAL_GL_INT_2_10_10_10_REV:
+    case LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV:
+        if (size != 4) {
+            ErrorInvalidOperation("%s: size must be 4 for this type.", info);
+            return false;
+        }
+        break;
+    }
+
     // see WebGL spec section 6.6 "Vertex Attribute Data Stride"
     if (stride < 0 || stride > 255) {
         ErrorInvalidValue("%s: negative or too large stride", info);
         return false;
     }
 
     if (byteOffset < 0) {
         ErrorInvalidValue("%s: negative offset", info);
--- a/dom/canvas/WebGLFormats.h
+++ b/dom/canvas/WebGLFormats.h
@@ -264,16 +264,21 @@ struct PackingInfo
 
     bool operator <(const PackingInfo& x) const
     {
         if (format != x.format)
             return format < x.format;
 
         return type < x.type;
     }
+
+    bool operator ==(const PackingInfo& x) const {
+        return (format == x.format &&
+                type == x.type);
+    }
 };
 
 struct DriverUnpackInfo
 {
     GLenum internalFormat;
     GLenum unpackFormat;
     GLenum unpackType;
 
--- a/dom/canvas/WebGLFramebuffer.cpp
+++ b/dom/canvas/WebGLFramebuffer.cpp
@@ -496,18 +496,22 @@ WebGLFBAttachPoint::GetParameter(const c
     }
 
     if (!isPNameValid) {
         webgl->ErrorInvalidEnum("%s: Invalid pname: 0x%04x", funcName, pname);
         return JS::NullValue();
     }
 
     const auto usage = Format();
-    if (!usage)
+    if (!usage) {
+        if (pname == LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING)
+            return JS::NumberValue(LOCAL_GL_LINEAR);
+
         return JS::NullValue();
+    }
 
     auto format = usage->format;
 
     GLint ret = 0;
     switch (pname) {
     case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE:
         ret = format->r;
         break;
@@ -880,16 +884,22 @@ WebGLFramebuffer::ValidateForRead(const 
     }
 
     if (!mColorReadBuffer->IsDefined()) {
         mContext->ErrorInvalidOperation("%s: The READ_BUFFER attachment is not defined.",
                                         funcName);
         return false;
     }
 
+    if (mColorReadBuffer->Samples()) {
+        mContext->ErrorInvalidOperation("%s: The READ_BUFFER attachment is multisampled.",
+                                        funcName);
+        return false;
+    }
+
     *out_format = mColorReadBuffer->Format();
     mColorReadBuffer->Size(out_width, out_height);
     return true;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // Resolution and caching
 
@@ -1232,16 +1242,25 @@ WebGLFramebuffer::DrawBuffers(const char
         //  equal to that of the MAX_DRAW_BUFFERS_WEBGL parameter."
         // This means that if buffers.Length() isn't larger than MaxDrawBuffers, it won't
         // be larger than MaxColorAttachments.
         const auto& cur = buffers[i];
         if (cur == LOCAL_GL_COLOR_ATTACHMENT0 + i) {
             const auto& attach = mColorAttachments[i];
             newColorDrawBuffers.push_back(&attach);
         } else if (cur != LOCAL_GL_NONE) {
+            const bool isColorEnum = (cur >= LOCAL_GL_COLOR_ATTACHMENT0 &&
+                                      cur < mContext->LastColorAttachmentEnum());
+            if (cur != LOCAL_GL_BACK &&
+                !isColorEnum)
+            {
+                mContext->ErrorInvalidEnum("%s: Unexpected enum in buffers.", funcName);
+                return;
+            }
+
             mContext->ErrorInvalidOperation("%s: `buffers[i]` must be NONE or"
                                             " COLOR_ATTACHMENTi.",
                                             funcName);
             return;
         }
     }
 
     ////
@@ -1649,17 +1668,16 @@ WebGLFramebuffer::BlitFramebuffer(WebGLC
     }
 
     ////
     // Clear unused buffer bits
 
     if (mask & LOCAL_GL_COLOR_BUFFER_BIT &&
         !srcColorFormat && !dstHasColor)
     {
-
         mask ^= LOCAL_GL_COLOR_BUFFER_BIT;
     }
 
     if (mask & LOCAL_GL_DEPTH_BUFFER_BIT &&
         !srcDepthFormat && !dstDepthFormat)
     {
         mask ^= LOCAL_GL_DEPTH_BUFFER_BIT;
     }
@@ -1761,69 +1779,57 @@ WebGLFramebuffer::BlitFramebuffer(WebGLC
             return;
         }
     }
 
     ////
     // Check for feedback
 
     if (srcFB && dstFB) {
-        const auto fnValidateBuffers = [&](GLenum bufferBit, const char* bufferBitName,
-                                           bool srcHas, bool dstHas)
-        {
-            if (mask & bufferBit &&
-                srcHas != dstHas)
-            {
-                webgl->ErrorInvalidOperation("%s: With %s, must have a corresponding draw"
-                                             " buffer iff there's a relevent read"
-                                             " buffer.",
-                                             funcName, bufferBitName);
-                return false;
+        const WebGLFBAttachPoint* feedback = nullptr;
+
+        if (mask & LOCAL_GL_COLOR_BUFFER_BIT) {
+            for (const auto& cur : dstFB->mResolvedCompleteData->colorDrawBuffers) {
+                if (srcFB->mColorReadBuffer->IsEquivalent(*cur)) {
+                    feedback = cur;
+                }
             }
-            return true;
-        };
-
-        if (!fnValidateBuffers( LOCAL_GL_COLOR_BUFFER_BIT, "COLOR_BUFFER_BIT",
-                                bool(srcFB->mColorReadBuffer),
-                                bool(dstFB->mColorDrawBuffers.size()) ) ||
-            !fnValidateBuffers( LOCAL_GL_DEPTH_BUFFER_BIT, "DEPTH_BUFFER_BIT",
-                                bool(srcFB->mResolvedCompleteData->depthBuffer),
-                                bool(dstFB->mResolvedCompleteData->depthBuffer) ) ||
-            !fnValidateBuffers( LOCAL_GL_STENCIL_BUFFER_BIT, "STENCIL_BUFFER_BIT",
-                                bool(srcFB->mResolvedCompleteData->stencilBuffer),
-                                bool(dstFB->mResolvedCompleteData->stencilBuffer) ))
-        {
-            return;
         }
 
-        const auto& readSet = srcFB->mResolvedCompleteData->readSet;
-        const auto& drawSet = dstFB->mResolvedCompleteData->drawSet;
+        const auto& srcDepthBuffer = srcFB->mResolvedCompleteData->depthBuffer;
+        const auto& dstDepthBuffer = dstFB->mResolvedCompleteData->depthBuffer;
+        if (mask & LOCAL_GL_DEPTH_BUFFER_BIT &&
+            srcDepthBuffer->IsEquivalent(*dstDepthBuffer))
+        {
+            feedback = dstDepthBuffer;
+        }
 
-        std::vector<WebGLFBAttachPoint::Ordered> intersection;
-        std::set_intersection(drawSet.begin(), drawSet.end(),
-                              readSet.begin(), readSet.end(),
-                              std::back_inserter(intersection));
+        const auto& srcStencilBuffer = srcFB->mResolvedCompleteData->stencilBuffer;
+        const auto& dstStencilBuffer = dstFB->mResolvedCompleteData->stencilBuffer;
+        if (mask & LOCAL_GL_STENCIL_BUFFER_BIT &&
+            srcStencilBuffer->IsEquivalent(*dstStencilBuffer))
+        {
+            feedback = dstStencilBuffer;
+        }
 
-        if (intersection.size()) {
-            // set_intersection pulls from the first range, so it records conflicts on the
-            // DRAW_FRAMEBUFFER.
-            const auto& example = intersection.cbegin()->mRef;
+        if (feedback) {
             webgl->ErrorInvalidOperation("%s: Feedback detected into DRAW_FRAMEBUFFER's"
                                          " 0x%04x attachment.",
-                                         funcName, example.mAttachmentPoint);
+                                         funcName, feedback->mAttachmentPoint);
             return;
         }
     } else if (!srcFB && !dstFB) {
         webgl->ErrorInvalidOperation("%s: Feedback with default framebuffer.", funcName);
         return;
     }
 
     ////
 
     gl->MakeCurrent();
+    WebGLContext::ScopedDrawCallWrapper wrapper(*webgl);
     gl->fBlitFramebuffer(srcX0, srcY0, srcX1, srcY1,
                          dstX0, dstY0, dstX1, dstY1,
                          mask, filter);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // Goop.
 
--- a/dom/canvas/WebGLFramebuffer.h
+++ b/dom/canvas/WebGLFramebuffer.h
@@ -97,16 +97,28 @@ public:
     void Resolve(gl::GLContext* gl) const;
 
     JS::Value GetParameter(const char* funcName, WebGLContext* webgl, JSContext* cx,
                            GLenum target, GLenum attachment, GLenum pname,
                            ErrorResult* const out_error) const;
 
     void OnBackingStoreRespecified() const;
 
+    bool IsEquivalent(const WebGLFBAttachPoint& other) const {
+        MOZ_ASSERT(IsDefined() && other.IsDefined());
+
+#define _(X) X == other.X
+        return ( _(mRenderbufferPtr) &&
+                 _(mTexturePtr) &&
+                 _(mTexImageTarget.get()) &&
+                 _(mTexImageLevel) &&
+                 _(mTexImageLayer) );
+#undef _
+    }
+
     ////
 
     struct Ordered {
         const WebGLFBAttachPoint& mRef;
 
         explicit Ordered(const WebGLFBAttachPoint& ref)
             : mRef(ref)
         { }
@@ -168,19 +180,19 @@ protected:
     std::vector<const WebGLFBAttachPoint*> mColorDrawBuffers; // Non-null
     const WebGLFBAttachPoint* mColorReadBuffer; // Null if NONE
 
     ////
 
     struct ResolvedData {
         // BlitFramebuffer
         bool hasSampleBuffers;
-        std::vector<const WebGLFBAttachPoint*> colorDrawBuffers;
-        const WebGLFBAttachPoint* depthBuffer;
-        const WebGLFBAttachPoint* stencilBuffer;
+        std::vector<const WebGLFBAttachPoint*> colorDrawBuffers; // Non-null, defined
+        const WebGLFBAttachPoint* depthBuffer; // null if not defined
+        const WebGLFBAttachPoint* stencilBuffer; // null if not defined
 
         // IsFeedback
         std::vector<const WebGLFBAttachPoint*> texDrawBuffers; // Non-null
         std::set<WebGLFBAttachPoint::Ordered> drawSet;
         std::set<WebGLFBAttachPoint::Ordered> readSet;
 
         explicit ResolvedData(const WebGLFramebuffer& parent);
     };
--- a/dom/canvas/WebGLProgram.cpp
+++ b/dom/canvas/WebGLProgram.cpp
@@ -25,28 +25,29 @@ namespace mozilla {
  *     `out_baseName`: "foo"
  *     `out_isArray`: true
  *     `out_index`: 3
  *
  * If `name`: "foo"
  * Then returns true, with
  *     `out_baseName`: "foo"
  *     `out_isArray`: false
- *     `out_index`: <not written>
+ *     `out_index`: 0
  */
 static bool
 ParseName(const nsCString& name, nsCString* const out_baseName,
           bool* const out_isArray, size_t* const out_arrayIndex)
 {
     int32_t indexEnd = name.RFind("]");
     if (indexEnd == -1 ||
         (uint32_t)indexEnd != name.Length() - 1)
     {
         *out_baseName = name;
         *out_isArray = false;
+        *out_arrayIndex = 0;
         return true;
     }
 
     int32_t indexOpenBracket = name.RFind("[");
     if (indexOpenBracket == -1)
         return false;
 
     uint32_t indexStart = indexOpenBracket + 1;
@@ -65,16 +66,28 @@ ParseName(const nsCString& name, nsCStri
         return false;
 
     *out_baseName = StringHead(name, indexOpenBracket);
     *out_isArray = true;
     *out_arrayIndex = indexNum;
     return true;
 }
 
+static void
+AssembleName(const nsCString& baseName, bool isArray, size_t arrayIndex,
+             nsCString* const out_name)
+{
+    *out_name = baseName;
+    if (isArray) {
+        out_name->Append('[');
+        out_name->AppendInt(uint64_t(arrayIndex));
+        out_name->Append(']');
+    }
+}
+
 //////////
 
 /*static*/ const webgl::UniformInfo::TexListT*
 webgl::UniformInfo::GetTexList(WebGLActiveInfo* activeInfo)
 {
     const auto& webgl = activeInfo->mWebGL;
 
     switch (activeInfo->mElemType) {
@@ -441,17 +454,17 @@ CreateProgram(gl::GLContext* gl)
     gl->MakeCurrent();
     return gl->fCreateProgram();
 }
 
 WebGLProgram::WebGLProgram(WebGLContext* webgl)
     : WebGLRefCountedObject(webgl)
     , mGLName(CreateProgram(webgl->GL()))
     , mNumActiveTFOs(0)
-    , mNextLink_TransformFeedbackBufferMode(LOCAL_GL_SEPARATE_ATTRIBS)
+    , mNextLink_TransformFeedbackBufferMode(LOCAL_GL_INTERLEAVED_ATTRIBS)
 {
     mContext->mPrograms.insertBack(this);
 }
 
 WebGLProgram::~WebGLProgram()
 {
     DeleteOnce();
 }
@@ -646,24 +659,21 @@ WebGLProgram::GetFragDataLocation(const 
 
     if (!IsLinked()) {
         mContext->ErrorInvalidOperation("getFragDataLocation: `program` must be linked.");
         return -1;
     }
 
     const NS_LossyConvertUTF16toASCII userName(userName_wide);
     nsCString mappedName;
-
-    if (!LinkInfo()->FindFragData(userName, &mappedName)) {
-        mappedName = userName;
-    }
+    if (!LinkInfo()->MapFragDataName(userName, &mappedName))
+        return -1;
 
     gl::GLContext* gl = mContext->GL();
     gl->MakeCurrent();
-
     return gl->fGetFragDataLocation(mGLName, mappedName.BeginReading());
 }
 
 void
 WebGLProgram::GetProgramInfoLog(nsAString* const out) const
 {
     CopyASCIItoUTF16(mLinkLog, *out);
 }
@@ -730,33 +740,19 @@ WebGLProgram::GetUniformBlockIndex(const
 
     if (!IsLinked()) {
         mContext->ErrorInvalidOperation("getUniformBlockIndex: `program` must be linked.");
         return LOCAL_GL_INVALID_INDEX;
     }
 
     const NS_LossyConvertUTF16toASCII userName(userName_wide);
 
-    nsDependentCString baseUserName;
-    bool isArray;
-    size_t arrayIndex;
-    if (!ParseName(userName, &baseUserName, &isArray, &arrayIndex))
-        return LOCAL_GL_INVALID_INDEX;
-
-    const webgl::UniformBlockInfo* info;
-    if (!LinkInfo()->FindUniformBlock(baseUserName, &info)) {
+    nsCString mappedName;
+    if (!LinkInfo()->MapUniformBlockName(userName, &mappedName))
         return LOCAL_GL_INVALID_INDEX;
-    }
-
-    nsAutoCString mappedName(info->mBaseMappedName);
-    if (isArray) {
-        mappedName.AppendLiteral("[");
-        mappedName.AppendInt(uint32_t(arrayIndex));
-        mappedName.AppendLiteral("]");
-    }
 
     gl::GLContext* gl = mContext->GL();
     gl->MakeCurrent();
 
     return gl->fGetUniformBlockIndex(mGLName, mappedName.BeginReading());
 }
 
 void
@@ -862,39 +858,26 @@ WebGLProgram::GetUniformLocation(const n
 
     if (!IsLinked()) {
         mContext->ErrorInvalidOperation("getUniformLocation: `program` must be linked.");
         return nullptr;
     }
 
     const NS_LossyConvertUTF16toASCII userName(userName_wide);
 
-    nsDependentCString baseUserName;
-    bool isArray = false;
     // GLES 2.0.25, Section 2.10, p35
     // If the the uniform location is an array, then the location of the first
     // element of that array can be retrieved by either using the name of the
     // uniform array, or the name of the uniform array appended with "[0]".
-    // The ParseName() can't recognize this rule. So always initialize
-    // arrayIndex with 0.
-    size_t arrayIndex = 0;
-    if (!ParseName(userName, &baseUserName, &isArray, &arrayIndex))
-        return nullptr;
-
+    nsCString mappedName;
+    size_t arrayIndex;
     webgl::UniformInfo* info;
-    if (!LinkInfo()->FindUniform(baseUserName, &info))
+    if (!LinkInfo()->FindUniform(userName, &mappedName, &arrayIndex, &info))
         return nullptr;
 
-    nsAutoCString mappedName(info->mActiveInfo->mBaseMappedName);
-    if (isArray) {
-        mappedName.AppendLiteral("[");
-        mappedName.AppendInt(uint32_t(arrayIndex));
-        mappedName.AppendLiteral("]");
-    }
-
     gl::GLContext* gl = mContext->GL();
     gl->MakeCurrent();
 
     GLint loc = gl->fGetUniformLocation(mGLName, mappedName.BeginReading());
     if (loc == -1)
         return nullptr;
 
     RefPtr<WebGLUniformLocation> locObj = new WebGLUniformLocation(mContext, LinkInfo(),
@@ -916,46 +899,32 @@ WebGLProgram::GetUniformIndices(const do
     nsTArray<GLuint>& arr = retval.SetValue();
 
     gl::GLContext* gl = mContext->GL();
     gl->MakeCurrent();
 
     for (size_t i = 0; i < count; i++) {
         const NS_LossyConvertUTF16toASCII userName(uniformNames[i]);
 
-        nsDependentCString baseUserName;
-        bool isArray;
+        nsCString mappedName;
         size_t arrayIndex;
-        if (!ParseName(userName, &baseUserName, &isArray, &arrayIndex)) {
-            arr.AppendElement(LOCAL_GL_INVALID_INDEX);
-            continue;
-        }
-
         webgl::UniformInfo* info;
-        if (!LinkInfo()->FindUniform(baseUserName, &info)) {
+        if (!LinkInfo()->FindUniform(userName, &mappedName, &arrayIndex, &info)) {
             arr.AppendElement(LOCAL_GL_INVALID_INDEX);
             continue;
         }
 
-        nsAutoCString mappedName(info->mActiveInfo->mBaseMappedName);
-        if (isArray) {
-            mappedName.AppendLiteral("[");
-            mappedName.AppendInt(uint32_t(arrayIndex));
-            mappedName.AppendLiteral("]");
-        }
+        const GLchar* const mappedNameBegin = mappedName.get();
 
-        const GLchar* mappedNameBytes = mappedName.BeginReading();
-
-        GLuint index = 0;
-        gl->fGetUniformIndices(mGLName, 1, &mappedNameBytes, &index);
+        GLuint index = LOCAL_GL_INVALID_INDEX;
+        gl->fGetUniformIndices(mGLName, 1, &mappedNameBegin, &index);
         arr.AppendElement(index);
     }
 }
 
-
 void
 WebGLProgram::UniformBlockBinding(GLuint uniformBlockIndex,
                                   GLuint uniformBlockBinding) const
 {
     const char funcName[] = "getActiveUniformBlockName";
     if (!IsLinked()) {
         mContext->ErrorInvalidOperation("%s: `program` must be linked.", funcName);
         return;
@@ -1506,55 +1475,120 @@ WebGLProgram::EnumerateFragOutputs(std::
     MOZ_ASSERT(mFragShader);
 
     mFragShader->EnumerateFragOutputs(out_FragOutputs);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
 bool
-webgl::LinkedProgramInfo::FindAttrib(const nsCString& baseUserName,
+IsBaseName(const nsCString& name)
+{
+    if (!name.Length())
+        return true;
+
+    return name[name.Length() - 1] != ']'; // Doesn't end in ']'.
+}
+
+bool
+webgl::LinkedProgramInfo::FindAttrib(const nsCString& userName,
                                      const webgl::AttribInfo** const out) const
 {
+    // VS inputs cannot be arrays or structures.
+    // `userName` is thus always `baseUserName`.
     for (const auto& attrib : attribs) {
-        if (attrib.mActiveInfo->mBaseUserName == baseUserName) {
+        if (attrib.mActiveInfo->mBaseUserName == userName) {
             *out = &attrib;
             return true;
         }
     }
 
     return false;
 }
 
 bool
-webgl::LinkedProgramInfo::FindUniform(const nsCString& baseUserName,
-                                      webgl::UniformInfo** const out) const
+webgl::LinkedProgramInfo::FindUniform(const nsCString& userName,
+                                      nsCString* const out_mappedName,
+                                      size_t* const out_arrayIndex,
+                                      webgl::UniformInfo** const out_info) const
 {
+    nsCString baseUserName;
+    bool isArray;
+    size_t arrayIndex;
+    if (!ParseName(userName, &baseUserName, &isArray, &arrayIndex))
+        return false;
+
+    webgl::UniformInfo* info = nullptr;
     for (const auto& uniform : uniforms) {
         if (uniform->mActiveInfo->mBaseUserName == baseUserName) {
-            *out = uniform;
-            return true;
+            info = uniform;
+            break;
         }
     }
+    if (!info)
+        return false;
 
-    return false;
+    const auto& baseMappedName = info->mActiveInfo->mBaseMappedName;
+    AssembleName(baseMappedName, isArray, arrayIndex, out_mappedName);
+
+    *out_arrayIndex = arrayIndex;
+    *out_info = info;
+    return true;
 }
 
 bool
-webgl::LinkedProgramInfo::FindUniformBlock(const nsCString& baseUserName,
-                                           const webgl::UniformBlockInfo** const out) const
+webgl::LinkedProgramInfo::MapUniformBlockName(const nsCString& userName,
+                                              nsCString* const out_mappedName) const
 {
+    nsCString baseUserName;
+    bool isArray;
+    size_t arrayIndex;
+    if (!ParseName(userName, &baseUserName, &isArray, &arrayIndex))
+        return false;
+
+    const webgl::UniformBlockInfo* info = nullptr;
     for (const auto& block : uniformBlocks) {
         if (block->mBaseUserName == baseUserName) {
-            *out = block;
-            return true;
+            info = block;
+            break;
         }
     }
+    if (!info)
+        return false;
 
-    return false;
+    const auto& baseMappedName = info->mBaseMappedName;
+    AssembleName(baseMappedName, isArray, arrayIndex, out_mappedName);
+    return true;
+}
+
+bool
+webgl::LinkedProgramInfo::MapFragDataName(const nsCString& userName,
+                                          nsCString* const out_mappedName) const
+{
+    // FS outputs can be arrays, but not structures.
+
+    if (!fragDataMap.size()) {
+        // No mappings map from validation, so just forward it.
+        *out_mappedName = userName;
+        return true;
+    }
+
+    nsCString baseUserName;
+    bool isArray;
+    size_t arrayIndex;
+    if (!ParseName(userName, &baseUserName, &isArray, &arrayIndex))
+        return false;
+
+    const auto itr = fragDataMap.find(baseUserName);
+    if (itr == fragDataMap.end())
+        return false;
+
+    const auto& baseMappedName = itr->second;
+    AssembleName(baseMappedName, isArray, arrayIndex, out_mappedName);
+    return true;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
 JSObject*
 WebGLProgram::WrapObject(JSContext* js, JS::Handle<JSObject*> givenProto)
 {
     return dom::WebGLProgramBinding::Wrap(js, this, givenProto);
--- a/dom/canvas/WebGLProgram.h
+++ b/dom/canvas/WebGLProgram.h
@@ -100,33 +100,23 @@ struct LinkedProgramInfo final
     //////
 
     // The maps for the frag data names to the translated names.
     std::map<nsCString, const nsCString> fragDataMap;
 
     explicit LinkedProgramInfo(WebGLProgram* prog);
     ~LinkedProgramInfo();
 
-    bool FindAttrib(const nsCString& baseUserName, const AttribInfo** const out) const;
-    bool FindUniform(const nsCString& baseUserName, UniformInfo** const out) const;
-    bool FindUniformBlock(const nsCString& baseUserName,
-                          const UniformBlockInfo** const out) const;
-
-    bool
-    FindFragData(const nsCString& baseUserName,
-                 nsCString* const out_baseMappedName) const
-    {
-        const auto itr = fragDataMap.find(baseUserName);
-        if (itr == fragDataMap.end()) {
-            return false;
-        }
-
-        *out_baseMappedName = itr->second;
-        return true;
-    }
+    bool FindAttrib(const nsCString& userName, const AttribInfo** const out_info) const;
+    bool FindUniform(const nsCString& userName, nsCString* const out_mappedName,
+                     size_t* const out_arrayIndex, UniformInfo** const out_info) const;
+    bool MapUniformBlockName(const nsCString& userName,
+                             nsCString* const out_mappedName) const;
+    bool MapFragDataName(const nsCString& userName,
+                         nsCString* const out_mappedName) const;
 };
 
 } // namespace webgl
 
 class WebGLProgram final
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLProgram>
     , public LinkedListElement<WebGLProgram>
--- a/dom/canvas/WebGLRenderbuffer.cpp
+++ b/dom/canvas/WebGLRenderbuffer.cpp
@@ -200,17 +200,17 @@ WebGLRenderbuffer::RenderbufferStorage(c
     mContext->MakeContextCurrent();
 
     if (!usage->maxSamplesKnown) {
         const_cast<webgl::FormatUsageInfo*>(usage)->ResolveMaxSamples(mContext->gl);
     }
     MOZ_ASSERT(usage->maxSamplesKnown);
 
     if (samples > usage->maxSamples) {
-        mContext->ErrorInvalidValue("%s: `samples` is out of the valid range.", funcName);
+        mContext->ErrorInvalidOperation("%s: `samples` is out of the valid range.", funcName);
         return;
     }
 
     // Validation complete.
 
     const GLenum error = DoRenderbufferStorage(samples, usage, width, height);
     if (error) {
         const char* errorName = mContext->ErrorName(error);
@@ -273,17 +273,17 @@ WebGLRenderbuffer::GetRenderbufferParame
             gl->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, mPrimaryRB);
             GLint i = 0;
             gl->fGetRenderbufferParameteriv(target.get(), pname.get(), &i);
             return i;
         }
 
     case LOCAL_GL_RENDERBUFFER_INTERNAL_FORMAT:
         {
-            GLenum ret = 0;
+            GLenum ret = LOCAL_GL_RGBA4;
             if (mFormat) {
                 ret = mFormat->format->sizedFormat;
 
                 if (!mContext->IsWebGL2() && ret == LOCAL_GL_DEPTH24_STENCIL8) {
                     ret = LOCAL_GL_DEPTH_STENCIL;
                 }
             }
             return ret;
--- a/dom/canvas/WebGLSampler.cpp
+++ b/dom/canvas/WebGLSampler.cpp
@@ -50,18 +50,20 @@ WebGLSampler::GetParentObject() const
 JSObject*
 WebGLSampler::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto)
 {
     return dom::WebGLSamplerBinding::Wrap(cx, this, givenProto);
 }
 
 static bool
 ValidateSamplerParameterParams(WebGLContext* webgl, const char* funcName, GLenum pname,
-                               GLint paramInt)
+                               const FloatOrInt& param)
 {
+    const auto& paramInt = param.i;
+
     switch (pname) {
     case LOCAL_GL_TEXTURE_MIN_FILTER:
         switch (paramInt) {
         case LOCAL_GL_NEAREST:
         case LOCAL_GL_LINEAR:
         case LOCAL_GL_NEAREST_MIPMAP_NEAREST:
         case LOCAL_GL_NEAREST_MIPMAP_LINEAR:
         case LOCAL_GL_LINEAR_MIPMAP_NEAREST:
@@ -136,72 +138,77 @@ ValidateSamplerParameterParams(WebGLCont
         return false;
     }
 
     webgl->ErrorInvalidEnum("%s: invalid param: %s", funcName, webgl->EnumName(paramInt));
     return false;
 }
 
 void
-WebGLSampler::SamplerParameter(const char* funcName, GLenum pname, GLint paramInt)
+WebGLSampler::SamplerParameter(const char* funcName, GLenum pname,
+                               const FloatOrInt& param)
 {
-    if (!ValidateSamplerParameterParams(mContext, funcName, pname, paramInt))
+    if (!ValidateSamplerParameterParams(mContext, funcName, pname, param))
         return;
 
     switch (pname) {
     case LOCAL_GL_TEXTURE_MIN_FILTER:
-        mMinFilter = paramInt;
+        mMinFilter = param.i;
         break;
 
     case LOCAL_GL_TEXTURE_MAG_FILTER:
-        mMagFilter = paramInt;
+        mMagFilter = param.i;
         break;
 
     case LOCAL_GL_TEXTURE_WRAP_S:
-        mWrapS = paramInt;
+        mWrapS = param.i;
         break;
 
     case LOCAL_GL_TEXTURE_WRAP_T:
-        mWrapT = paramInt;
+        mWrapT = param.i;
         break;
 
     case LOCAL_GL_TEXTURE_WRAP_R:
-        mWrapR = paramInt;
+        mWrapR = param.i;
         break;
 
     case LOCAL_GL_TEXTURE_COMPARE_MODE:
-        mCompareMode = paramInt;
+        mCompareMode = param.i;
         break;
 
     case LOCAL_GL_TEXTURE_COMPARE_FUNC:
-        mCompareFunc = paramInt;
+        mCompareFunc = param.i;
         break;
 
     case LOCAL_GL_TEXTURE_MIN_LOD:
-        mMinLod = paramInt;
+        mMinLod = param.f;
         break;
 
     case LOCAL_GL_TEXTURE_MAX_LOD:
-        mMaxLod = paramInt;
+        mMaxLod = param.f;
         break;
 
     default:
         MOZ_CRASH("GFX: Unhandled pname");
         break;
     }
 
     for (uint32_t i = 0; i < mContext->mBoundSamplers.Length(); ++i) {
         if (this == mContext->mBoundSamplers[i])
             mContext->InvalidateResolveCacheForTextureWithTexUnit(i);
     }
 
     ////
 
     mContext->gl->MakeCurrent();
-    mContext->gl->fSamplerParameteri(mGLName, pname, paramInt);
+    if (param.isFloat) {
+        mContext->gl->fSamplerParameterf(mGLName, pname, param.f);
+    } else {
+        mContext->gl->fSamplerParameteri(mGLName, pname, param.i);
+    }
 }
 
 ////
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLSampler)
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLSampler, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLSampler, Release)
 
--- a/dom/canvas/WebGLSampler.h
+++ b/dom/canvas/WebGLSampler.h
@@ -26,29 +26,29 @@ public:
 
     const GLuint mGLName;
 
     void Delete();
     WebGLContext* GetParentObject() const;
 
     virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
 
-    void SamplerParameter(const char* funcName, GLenum pname, GLint paramInt);
+    void SamplerParameter(const char* funcName, GLenum pname, const FloatOrInt& param);
 
 private:
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLSampler)
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLSampler)
 
     TexMinFilter mMinFilter;
     TexMagFilter mMagFilter;
     TexWrap mWrapS;
     TexWrap mWrapT;
     TexWrap mWrapR;
-    GLint mMinLod;
-    GLint mMaxLod;
+    GLfloat mMinLod;
+    GLfloat mMaxLod;
     TexCompareMode mCompareMode;
     TexCompareFunc mCompareFunc;
 
 private:
     ~WebGLSampler();
 };
 
 } // namespace mozilla
--- a/dom/canvas/WebGLShaderValidator.cpp
+++ b/dom/canvas/WebGLShaderValidator.cpp
@@ -23,48 +23,31 @@ IdentifierHashFunc(const char* name, siz
 {
     // NB: we use the x86 function everywhere, even though it's suboptimal perf
     // on x64.  They return different results; not sure if that's a requirement.
     uint64_t hash[2];
     MurmurHash3_x86_128(name, len, 0, hash);
     return hash[0];
 }
 
-static int
+static ShCompileOptions
 ChooseValidatorCompileOptions(const ShBuiltInResources& resources,
                               const mozilla::gl::GLContext* gl)
 {
-    int options = SH_VARIABLES |
-                  SH_ENFORCE_PACKING_RESTRICTIONS |
-                  SH_OBJECT_CODE |
-                  SH_LIMIT_CALL_STACK_DEPTH |
-                  SH_INIT_GL_POSITION;
-
-    if (resources.MaxExpressionComplexity > 0) {
-        options |= SH_LIMIT_EXPRESSION_COMPLEXITY;
-    }
+    ShCompileOptions options = SH_VARIABLES |
+                               SH_ENFORCE_PACKING_RESTRICTIONS |
+                               SH_OBJECT_CODE |
+                               SH_INIT_GL_POSITION;
 
     // Sampler arrays indexed with non-constant expressions are forbidden in
     // GLSL 1.30 and later.
     // ESSL 3 requires constant-integral-expressions for this as well.
     // Just do it universally.
     options |= SH_UNROLL_FOR_LOOP_WITH_SAMPLER_ARRAY_INDEX;
 
-    if (gfxPrefs::WebGLAllANGLEOptions()) {
-        return options |
-               SH_VALIDATE_LOOP_INDEXING |
-               SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX |
-               SH_UNROLL_FOR_LOOP_WITH_SAMPLER_ARRAY_INDEX |
-               SH_CLAMP_INDIRECT_ARRAY_BOUNDS |
-               SH_UNFOLD_SHORT_CIRCUIT |
-               SH_SCALARIZE_VEC_AND_MAT_CONSTRUCTOR_ARGS |
-               SH_INIT_OUTPUT_VARIABLES |
-               SH_REGENERATE_STRUCT_NAMES;
-    }
-
 #ifndef XP_MACOSX
     // We want to do this everywhere, but to do this on Mac, we need
     // to do it only on Mac OSX > 10.6 as this causes the shader
     // compiler in 10.6 to crash
     options |= SH_CLAMP_INDIRECT_ARRAY_BOUNDS;
 #endif
 
 #ifdef XP_MACOSX
@@ -74,16 +57,40 @@ ChooseValidatorCompileOptions(const ShBu
         options |= SH_UNFOLD_SHORT_CIRCUIT;
 
         // Work around that Mac drivers handle struct scopes incorrectly.
         options |= SH_REGENERATE_STRUCT_NAMES;
         options |= SH_INIT_OUTPUT_VARIABLES;
     }
 #endif
 
+    if (gfxPrefs::WebGLAllANGLEOptions()) {
+        options = -1;
+
+        options ^= SH_INTERMEDIATE_TREE;
+        options ^= SH_LINE_DIRECTIVES;
+        options ^= SH_SOURCE_PATH;
+
+        options ^= SH_LIMIT_EXPRESSION_COMPLEXITY;
+        options ^= SH_LIMIT_CALL_STACK_DEPTH;
+
+        options ^= SH_EXPAND_SELECT_HLSL_INTEGER_POW_EXPRESSIONS;
+        options ^= SH_HLSL_GET_DIMENSIONS_IGNORES_BASE_LEVEL;
+
+        options ^= SH_DONT_REMOVE_INVARIANT_FOR_FRAGMENT_INPUT;
+        options ^= SH_REMOVE_INVARIANT_AND_CENTROID_FOR_ESSL3;
+    }
+
+    if (resources.MaxExpressionComplexity > 0) {
+        options |= SH_LIMIT_EXPRESSION_COMPLEXITY;
+    }
+    if (resources.MaxCallStackDepth > 0) {
+        options |= SH_LIMIT_CALL_STACK_DEPTH;
+    }
+
     return options;
 }
 
 } // namespace webgl
 
 ////////////////////////////////////////
 
 static ShShaderOutput
@@ -161,30 +168,30 @@ WebGLContext::CreateShaderValidator(GLen
 #ifdef XP_MACOSX
         if (gl->Vendor() == gl::GLVendor::NVIDIA) {
             // Work around bug 890432
             resources.MaxExpressionComplexity = 1000;
         }
 #endif
     }
 
-    int compileOptions = webgl::ChooseValidatorCompileOptions(resources, gl);
-
+    const auto compileOptions = webgl::ChooseValidatorCompileOptions(resources, gl);
     return webgl::ShaderValidator::Create(shaderType, spec, outputLanguage, resources,
                                           compileOptions);
 }
 
 ////////////////////////////////////////
 
 namespace webgl {
 
 /*static*/ ShaderValidator*
 ShaderValidator::Create(GLenum shaderType, ShShaderSpec spec,
                         ShShaderOutput outputLanguage,
-                        const ShBuiltInResources& resources, int compileOptions)
+                        const ShBuiltInResources& resources,
+                        ShCompileOptions compileOptions)
 {
     ShHandle handle = ShConstructCompiler(shaderType, spec, outputLanguage, &resources);
     if (!handle)
         return nullptr;
 
     return new ShaderValidator(handle, compileOptions, resources.MaxVaryingVectors);
 }
 
--- a/dom/canvas/WebGLShaderValidator.h
+++ b/dom/canvas/WebGLShaderValidator.h
@@ -12,28 +12,29 @@
 #include <string>
 
 namespace mozilla {
 namespace webgl {
 
 class ShaderValidator final
 {
     const ShHandle mHandle;
-    const int mCompileOptions;
+    const ShCompileOptions mCompileOptions;
     const int mMaxVaryingVectors;
     bool mHasRun;
 
 public:
     static ShaderValidator* Create(GLenum shaderType, ShShaderSpec spec,
                                    ShShaderOutput outputLanguage,
                                    const ShBuiltInResources& resources,
-                                   int compileOptions);
+                                   ShCompileOptions compileOptions);
 
 private:
-    ShaderValidator(ShHandle handle, int compileOptions, int maxVaryingVectors)
+    ShaderValidator(ShHandle handle, ShCompileOptions compileOptions,
+                    int maxVaryingVectors)
         : mHandle(handle)
         , mCompileOptions(compileOptions)
         , mMaxVaryingVectors(maxVaryingVectors)
         , mHasRun(false)
     { }
 
 public:
     ~ShaderValidator();
--- a/dom/canvas/WebGLTexture.cpp
+++ b/dom/canvas/WebGLTexture.cpp
@@ -186,18 +186,20 @@ WebGLTexture::SetImageInfosAtLevel(uint3
     for (uint8_t i = 0; i < mFaceCount; i++) {
         ImageInfoAtFace(i, level) = newInfo;
     }
 
     InvalidateResolveCache();
 }
 
 bool
-WebGLTexture::IsMipmapComplete(uint32_t texUnit) const
+WebGLTexture::IsMipmapComplete(const char* funcName, uint32_t texUnit,
+                               bool* const out_initFailed)
 {
+    *out_initFailed = false;
     MOZ_ASSERT(DoesMinFilterRequireMipmap());
     // GLES 3.0.4, p161
 
     uint32_t maxLevel;
     if (!MaxEffectiveMipmapLevel(texUnit, &maxLevel))
         return false;
 
     // "* `level_base <= level_max`"
@@ -209,16 +211,21 @@ WebGLTexture::IsMipmapComplete(uint32_t 
 
     // Reference dimensions based on the current level.
     uint32_t refWidth = baseImageInfo.mWidth;
     uint32_t refHeight = baseImageInfo.mHeight;
     uint32_t refDepth = baseImageInfo.mDepth;
     MOZ_ASSERT(refWidth && refHeight && refDepth);
 
     for (uint32_t level = mBaseMipmapLevel; level <= maxLevel; level++) {
+        if (!EnsureLevelInitialized(funcName, level)) {
+            *out_initFailed = true;
+            return false;
+        }
+
         // "A cube map texture is mipmap complete if each of the six texture images,
         //  considered individually, is mipmap complete."
 
         for (uint8_t face = 0; face < mFaceCount; face++) {
             const ImageInfo& cur = ImageInfoAtFace(face, level);
 
             // "* The set of mipmap arrays `level_base` through `q` (where `q` is defined
             //    the "Mipmapping" discussion of section 3.8.10) were each specified with
@@ -296,18 +303,26 @@ WebGLTexture::IsCubeComplete() const
             return false;
         }
     }
 
     return true;
 }
 
 bool
-WebGLTexture::IsComplete(uint32_t texUnit, const char** const out_reason) const
+WebGLTexture::IsComplete(const char* funcName, uint32_t texUnit,
+                         const char** const out_reason, bool* const out_initFailed)
 {
+    *out_initFailed = false;
+
+    if (!EnsureLevelInitialized(funcName, mBaseMipmapLevel)) {
+        *out_initFailed = true;
+        return false;
+    }
+
     // Texture completeness is established at GLES 3.0.4, p160-161.
     // "[A] texture is complete unless any of the following conditions hold true:"
 
     // "* Any dimension of the `level_base` array is not positive."
     const ImageInfo& baseImageInfo = BaseImageInfo();
     if (!baseImageInfo.IsDefined()) {
         // In case of undefined texture image, we don't print any message because this is
         // a very common and often legitimate case (asynchronous texture loading).
@@ -329,17 +344,20 @@ WebGLTexture::IsComplete(uint32_t texUni
     WebGLSampler* sampler = mContext->mBoundSamplers[texUnit];
     TexMinFilter minFilter = sampler ? sampler->mMinFilter : mMinFilter;
     TexMagFilter magFilter = sampler ? sampler->mMagFilter : mMagFilter;
 
     // "* The minification filter requires a mipmap (is neither NEAREST nor LINEAR) and
     //    the texture is not mipmap complete."
     const bool requiresMipmap = (minFilter != LOCAL_GL_NEAREST &&
                                  minFilter != LOCAL_GL_LINEAR);
-    if (requiresMipmap && !IsMipmapComplete(texUnit)) {
+    if (requiresMipmap && !IsMipmapComplete(funcName, texUnit, out_initFailed)) {
+        if (*out_initFailed)
+            return false;
+
         *out_reason = "Because the minification filter requires mipmapping, the texture"
                       " must be \"mipmap complete\".";
         return false;
     }
 
     const bool isMinFilteringNearest = (minFilter == LOCAL_GL_NEAREST ||
                                         minFilter == LOCAL_GL_NEAREST_MIPMAP_NEAREST);
     const bool isMagFilteringNearest = (magFilter == LOCAL_GL_NEAREST);
@@ -452,84 +470,35 @@ WebGLTexture::MaxEffectiveMipmapLevel(ui
     return true;
 }
 
 bool
 WebGLTexture::GetFakeBlackType(const char* funcName, uint32_t texUnit,
                                FakeBlackType* const out_fakeBlack)
 {
     const char* incompleteReason;
-    if (!IsComplete(texUnit, &incompleteReason)) {
+    bool initFailed = false;
+    if (!IsComplete(funcName, texUnit, &incompleteReason, &initFailed)) {
+        if (initFailed) {
+            mContext->ErrorOutOfMemory("%s: Failed to initialize texture data.",
+                                       funcName);
+            return false; // The world just exploded.
+        }
+
         if (incompleteReason) {
             mContext->GenerateWarning("%s: Active texture %u for target 0x%04x is"
                                       " 'incomplete', and will be rendered as"
                                       " RGBA(0,0,0,1), as per the GLES 2.0.24 $3.8.2: %s",
                                       funcName, texUnit, mTarget.get(),
                                       incompleteReason);
         }
         *out_fakeBlack = FakeBlackType::RGBA0001;
         return true;
     }
 
-    // We may still want FakeBlack as an optimization for uninitialized image data.
-    bool hasUninitializedData = false;
-    bool hasInitializedData = false;
-
-    uint32_t maxLevel;
-    MOZ_ALWAYS_TRUE( MaxEffectiveMipmapLevel(texUnit, &maxLevel) );
-
-    MOZ_ASSERT(mBaseMipmapLevel <= maxLevel);
-    for (uint32_t level = mBaseMipmapLevel; level <= maxLevel; level++) {
-        for (uint8_t face = 0; face < mFaceCount; face++) {
-            const auto& cur = ImageInfoAtFace(face, level);
-            if (cur.IsDataInitialized())
-                hasInitializedData = true;
-            else
-                hasUninitializedData = true;
-        }
-    }
-    MOZ_ASSERT(hasUninitializedData || hasInitializedData);
-
-    if (!hasUninitializedData) {
-        *out_fakeBlack = FakeBlackType::None;
-        return true;
-    }
-
-    if (!hasInitializedData) {
-        const auto format = ImageInfoAtFace(0, mBaseMipmapLevel).mFormat->format;
-        if (format->IsColorFormat()) {
-            *out_fakeBlack = (format->a ? FakeBlackType::RGBA0000
-                                        : FakeBlackType::RGBA0001);
-            return true;
-        }
-
-        mContext->GenerateWarning("%s: Active texture %u for target 0x%04x is"
-                                  " uninitialized, and will be (perhaps slowly) cleared"
-                                  " by the implementation.",
-                                  funcName, texUnit, mTarget.get());
-    } else {
-        mContext->GenerateWarning("%s: Active texture %u for target 0x%04x contains"
-                                  " TexImages with uninitialized data along with"
-                                  " TexImages with initialized data, forcing the"
-                                  " implementation to (slowly) initialize the"
-                                  " uninitialized TexImages.",
-                                  funcName, texUnit, mTarget.get());
-    }
-
-    GLenum baseImageTarget = mTarget.get();
-    if (baseImageTarget == LOCAL_GL_TEXTURE_CUBE_MAP)
-        baseImageTarget = LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X;
-
-    for (uint32_t level = mBaseMipmapLevel; level <= maxLevel; level++) {
-        for (uint8_t face = 0; face < mFaceCount; face++) {
-            TexImageTarget imageTarget = baseImageTarget + face;
-            if (!EnsureImageDataInitialized(funcName, imageTarget, level))
-                return false; // The world just exploded.
-        }
-    }
 
     *out_fakeBlack = FakeBlackType::None;
     return true;
 }
 
 static void
 SetSwizzle(gl::GLContext* gl, TexTarget target, const GLint* swizzle)
 {
@@ -580,24 +549,41 @@ WebGLTexture::ResolveForDraw(const char*
     return true;
 }
 
 bool
 WebGLTexture::EnsureImageDataInitialized(const char* funcName, TexImageTarget target,
                                          uint32_t level)
 {
     auto& imageInfo = ImageInfoAt(target, level);
-    MOZ_ASSERT(imageInfo.IsDefined());
+    if (!imageInfo.IsDefined())
+        return true;
 
     if (imageInfo.IsDataInitialized())
         return true;
 
     return InitializeImageData(funcName, target, level);
 }
 
+bool
+WebGLTexture::EnsureLevelInitialized(const char* funcName, uint32_t level)
+{
+    if (mTarget != LOCAL_GL_TEXTURE_CUBE_MAP)
+        return EnsureImageDataInitialized(funcName, mTarget.get(), level);
+
+    for (GLenum texImageTarget = LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X;
+         texImageTarget <= LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z;
+         ++texImageTarget)
+    {
+        if (!EnsureImageDataInitialized(funcName, texImageTarget, level))
+            return false;
+    }
+    return true;
+}
+
 static void
 ZeroANGLEDepthTexture(WebGLContext* webgl, GLuint tex,
                       const webgl::FormatUsageInfo* usage, uint32_t width,
                       uint32_t height)
 {
     const auto& format = usage->format;
     GLenum attachPoint = 0;
     GLbitfield clearBits = 0;
@@ -1014,24 +1000,18 @@ WebGLTexture::IsTexture() const
 {
     return HasEverBeenBound() && !IsDeleted();
 }
 
 // Here we have to support all pnames with both int and float params.
 // See this discussion:
 //   https://www.khronos.org/webgl/public-mailing-list/archives/1008/msg00014.html
 void
-WebGLTexture::TexParameter(TexTarget texTarget, GLenum pname, GLint* maybeIntParam,
-                           GLfloat* maybeFloatParam)
+WebGLTexture::TexParameter(TexTarget texTarget, GLenum pname, const FloatOrInt& param)
 {
-    MOZ_ASSERT(maybeIntParam || maybeFloatParam);
-
-    GLint   intParam   = maybeIntParam   ? *maybeIntParam   : GLint(roundf(*maybeFloatParam));
-    GLfloat floatParam = maybeFloatParam ? *maybeFloatParam : GLfloat(*maybeIntParam);
-
     bool isPNameValid = false;
     switch (pname) {
     // GLES 2.0.25 p76:
     case LOCAL_GL_TEXTURE_WRAP_S:
     case LOCAL_GL_TEXTURE_WRAP_T:
     case LOCAL_GL_TEXTURE_MIN_FILTER:
     case LOCAL_GL_TEXTURE_MAG_FILTER:
         isPNameValid = true;
@@ -1064,26 +1044,26 @@ WebGLTexture::TexParameter(TexTarget tex
     // Validate params and invalidate if needed.
 
     bool paramBadEnum = false;
     bool paramBadValue = false;
 
     switch (pname) {
     case LOCAL_GL_TEXTURE_BASE_LEVEL:
     case LOCAL_GL_TEXTURE_MAX_LEVEL:
-        paramBadValue = (intParam < 0);
+        paramBadValue = (param.i < 0);
         break;
 
     case LOCAL_GL_TEXTURE_COMPARE_MODE:
-        paramBadValue = (intParam != LOCAL_GL_NONE &&
-                         intParam != LOCAL_GL_COMPARE_REF_TO_TEXTURE);
+        paramBadValue = (param.i != LOCAL_GL_NONE &&
+                         param.i != LOCAL_GL_COMPARE_REF_TO_TEXTURE);
         break;
 
     case LOCAL_GL_TEXTURE_COMPARE_FUNC:
-        switch (intParam) {
+        switch (param.i) {
         case LOCAL_GL_LEQUAL:
         case LOCAL_GL_GEQUAL:
         case LOCAL_GL_LESS:
         case LOCAL_GL_GREATER:
         case LOCAL_GL_EQUAL:
         case LOCAL_GL_NOTEQUAL:
         case LOCAL_GL_ALWAYS:
         case LOCAL_GL_NEVER:
@@ -1091,125 +1071,124 @@ WebGLTexture::TexParameter(TexTarget tex
 
         default:
             paramBadValue = true;
             break;
         }
         break;
 
     case LOCAL_GL_TEXTURE_MIN_FILTER:
-        switch (intParam) {
+        switch (param.i) {
         case LOCAL_GL_NEAREST:
         case LOCAL_GL_LINEAR:
         case LOCAL_GL_NEAREST_MIPMAP_NEAREST:
         case LOCAL_GL_LINEAR_MIPMAP_NEAREST:
         case LOCAL_GL_NEAREST_MIPMAP_LINEAR:
         case LOCAL_GL_LINEAR_MIPMAP_LINEAR:
             break;
 
         default:
             paramBadEnum = true;
             break;
         }
         break;
 
     case LOCAL_GL_TEXTURE_MAG_FILTER:
-        switch (intParam) {
+        switch (param.i) {
         case LOCAL_GL_NEAREST:
         case LOCAL_GL_LINEAR:
             break;
 
         default:
             paramBadEnum = true;
             break;
         }
         break;
 
     case LOCAL_GL_TEXTURE_WRAP_S:
     case LOCAL_GL_TEXTURE_WRAP_T:
     case LOCAL_GL_TEXTURE_WRAP_R:
-        switch (intParam) {
+        switch (param.i) {
         case LOCAL_GL_CLAMP_TO_EDGE:
         case LOCAL_GL_MIRRORED_REPEAT:
         case LOCAL_GL_REPEAT:
             break;
 
         default:
             paramBadEnum = true;
             break;
         }
         break;
 
     case LOCAL_GL_TEXTURE_MAX_ANISOTROPY_EXT:
-        if (maybeFloatParam && floatParam < 1.0f)
-            paramBadValue = true;
-        else if (maybeIntParam && intParam < 1)
+        if (param.f < 1.0f)
             paramBadValue = true;
 
         break;
     }
 
     if (paramBadEnum) {
-        if (maybeIntParam) {
+        if (!param.isFloat) {
             mContext->ErrorInvalidEnum("texParameteri: pname 0x%04x: Invalid param"
                                        " 0x%04x.",
-                                       pname, intParam);
+                                       pname, param.i);
         } else {
             mContext->ErrorInvalidEnum("texParameterf: pname 0x%04x: Invalid param %g.",
-                                       pname, floatParam);
+                                       pname, param.f);
         }
         return;
     }
 
     if (paramBadValue) {
-        if (maybeIntParam) {
+        if (!param.isFloat) {
             mContext->ErrorInvalidValue("texParameteri: pname 0x%04x: Invalid param %i"
                                         " (0x%x).",
-                                        pname, intParam, intParam);
+                                        pname, param.i, param.i);
         } else {
             mContext->ErrorInvalidValue("texParameterf: pname 0x%04x: Invalid param %g.",
-                                        pname, floatParam);
+                                        pname, param.f);
         }
         return;
     }
 
     ////////////////
     // Store any needed values
 
+    FloatOrInt clamped = param;
     switch (pname) {
     case LOCAL_GL_TEXTURE_BASE_LEVEL:
-        mBaseMipmapLevel = intParam;
+        mBaseMipmapLevel = clamped.i;
         ClampLevelBaseAndMax();
-        intParam = mBaseMipmapLevel;
+        clamped = FloatOrInt(GLint(mBaseMipmapLevel));
         break;
 
     case LOCAL_GL_TEXTURE_MAX_LEVEL:
-        mMaxMipmapLevel = intParam;
+        mMaxMipmapLevel = clamped.i;
         ClampLevelBaseAndMax();
-        intParam = mMaxMipmapLevel;
+        clamped = FloatOrInt(GLint(mMaxMipmapLevel));
         break;
 
     case LOCAL_GL_TEXTURE_MIN_FILTER:
-        mMinFilter = intParam;
+        mMinFilter = clamped.i;
         break;
 
     case LOCAL_GL_TEXTURE_MAG_FILTER:
-        mMagFilter = intParam;
+        mMagFilter = clamped.i;
         break;
 
     case LOCAL_GL_TEXTURE_WRAP_S:
-        mWrapS = intParam;
+        mWrapS = clamped.i;
         break;
 
     case LOCAL_GL_TEXTURE_WRAP_T:
-        mWrapT = intParam;
+        mWrapT = clamped.i;
         break;
 
     case LOCAL_GL_TEXTURE_COMPARE_MODE:
-        mTexCompareMode = intParam;
+        mTexCompareMode = clamped.i;
         break;
 
     // We don't actually need to store the WRAP_R, since it doesn't change texture
     // completeness rules.
     }
 
     // Only a couple of pnames don't need to invalidate our resolve status cache.
     switch (pname) {
@@ -1220,20 +1199,20 @@ WebGLTexture::TexParameter(TexTarget tex
     default:
         InvalidateResolveCache();
         break;
     }
 
     ////////////////
 
     mContext->MakeContextCurrent();
-    if (maybeIntParam)
-        mContext->gl->fTexParameteri(texTarget.get(), pname, intParam);
+    if (!clamped.isFloat)
+        mContext->gl->fTexParameteri(texTarget.get(), pname, clamped.i);
     else
-        mContext->gl->fTexParameterf(texTarget.get(), pname, floatParam);
+        mContext->gl->fTexParameterf(texTarget.get(), pname, clamped.f);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLTexture)
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLTexture, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLTexture, Release)
--- a/dom/canvas/WebGLTexture.h
+++ b/dom/canvas/WebGLTexture.h
@@ -20,16 +20,17 @@
 #include "WebGLFramebufferAttachable.h"
 #include "WebGLObjectModel.h"
 #include "WebGLStrongTypes.h"
 #include "WebGLTypes.h"
 
 namespace mozilla {
 class ErrorResult;
 class WebGLContext;
+struct FloatOrInt;
 struct TexImageSource;
 
 namespace dom {
 class Element;
 class HTMLVideoElement;
 class ImageData;
 class ArrayBufferViewOrSharedArrayBufferView;
 } // namespace dom
@@ -221,18 +222,17 @@ protected:
 
 public:
     ////////////////////////////////////
     // GL calls
     bool BindTexture(TexTarget texTarget);
     void GenerateMipmap(TexTarget texTarget);
     JS::Value GetTexParameter(TexTarget texTarget, GLenum pname);
     bool IsTexture() const;
-    void TexParameter(TexTarget texTarget, GLenum pname, GLint* maybeIntParam,
-                      GLfloat* maybeFloatParam);
+    void TexParameter(TexTarget texTarget, GLenum pname, const FloatOrInt& param);
 
     ////////////////////////////////////
     // WebGLTextureUpload.cpp
 
 protected:
     void TexOrSubImageBlob(bool isSubImage, const char* funcName, TexImageTarget target,
                            GLint level, GLenum internalFormat, GLint xOffset,
                            GLint yOffset, GLint zOffset,
@@ -345,16 +345,17 @@ public:
     }
 
     size_t MemoryUsage() const;
 
     bool InitializeImageData(const char* funcName, TexImageTarget target, uint32_t level);
 protected:
     bool EnsureImageDataInitialized(const char* funcName, TexImageTarget target,
                                     uint32_t level);
+    bool EnsureLevelInitialized(const char* funcName, uint32_t level);
 
     bool CheckFloatTextureFilterParams() const {
         // Without OES_texture_float_linear, only NEAREST and
         // NEAREST_MIMPAMP_NEAREST are supported.
         return mMagFilter == LOCAL_GL_NEAREST &&
                (mMinFilter == LOCAL_GL_NEAREST ||
                 mMinFilter == LOCAL_GL_NEAREST_MIPMAP_NEAREST);
     }
@@ -371,21 +372,23 @@ public:
     }
 
     void SetGeneratedMipmap();
 
     void SetCustomMipmap();
 
     bool AreAllLevel0ImageInfosEqual() const;
 
-    bool IsMipmapComplete(uint32_t texUnit) const;
+    bool IsMipmapComplete(const char* funcName, uint32_t texUnit,
+                          bool* const out_initFailed);
 
     bool IsCubeComplete() const;
 
-    bool IsComplete(uint32_t texUnit, const char** const out_reason) const;
+    bool IsComplete(const char* funcName, uint32_t texUnit, const char** const out_reason,
+                    bool* const out_initFailed);
 
     bool IsMipmapCubeComplete() const;
 
     bool IsCubeMap() const { return (mTarget == LOCAL_GL_TEXTURE_CUBE_MAP); }
 
     // Resolve cache optimizations
 protected:
     bool GetFakeBlackType(const char* funcName, uint32_t texUnit,
--- a/dom/canvas/test/webgl-conf/generated-mochitest.ini
+++ b/dom/canvas/test/webgl-conf/generated-mochitest.ini
@@ -4706,17 +4706,16 @@ skip-if = (os == 'android' || os == 'lin
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance__extensions__oes-texture-float-linear.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance__extensions__webgl-compressed-texture-atc.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance__extensions__webgl-compressed-texture-pvrtc.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance__extensions__webgl-compressed-texture-s3tc.html]
-fail-if = (os == 'mac') || (os == 'win')
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance__extensions__webgl-compressed-texture-size-limit.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance__extensions__webgl-debug-renderer-info.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance__extensions__webgl-debug-shaders.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance__extensions__webgl-shared-resources.html]
--- a/dom/canvas/test/webgl-conf/mochitest-errata.ini
+++ b/dom/canvas/test/webgl-conf/mochitest-errata.ini
@@ -131,18 +131,16 @@ skip-if = (os == 'android' && debug)
 skip-if = (os == 'mac' && os_version == '10.6')
 [generated/test_conformance__textures__misc__texture-size.html]
 # application crashed [@ mozilla::gl::GLContext::AfterGLCall]
 skip-if = (os == 'android') || (os == 'win')
 
 [generated/test_2_conformance__textures__misc__cube-incomplete-fbo.html]
 fail-if = (os == 'mac')
 skip-if = (os == 'win')
-[generated/test_2_conformance__extensions__webgl-compressed-texture-s3tc.html]
-fail-if = (os == 'mac') || (os == 'win')
 [generated/test_2_conformance2__rendering__draw-buffers.html]
 fail-if = (os == 'mac') || (os == 'win')
 [generated/test_2_conformance__textures__misc__tex-image-with-format-and-type.html]
 fail-if = (os == 'mac')
 [generated/test_2_conformance__attribs__gl-vertexattribpointer.html]
 fail-if = (os == 'mac') || (os == 'win')
 [generated/test_2_conformance2__vertex_arrays__vertex-array-object.html]
 fail-if = (os == 'mac') || (os == 'win')
--- a/dom/xhr/XMLHttpRequestWorker.cpp
+++ b/dom/xhr/XMLHttpRequestWorker.cpp
@@ -1143,18 +1143,18 @@ EventRunnable::PreDispatch(WorkerPrivate
     JS::Rooted<JS::Value> response(cx);
     mResponseResult = xhr->GetResponse(cx, &response);
     if (NS_SUCCEEDED(mResponseResult)) {
       if (!response.isGCThing()) {
         mResponse = response;
       } else {
         bool doClone = true;
         JS::Rooted<JS::Value> transferable(cx);
-        JS::Rooted<JSObject*> obj(cx, response.isObjectOrNull() ?
-                                  response.toObjectOrNull() : nullptr);
+        JS::Rooted<JSObject*> obj(cx, response.isObject() ?
+                                  &response.toObject() : nullptr);
         if (obj && JS_IsArrayBufferObject(obj)) {
           // Use cached response if the arraybuffer has been transfered.
           if (mProxy->mArrayBufferResponseWasTransferred) {
             MOZ_ASSERT(JS_IsDetachedArrayBufferObject(obj));
             mUseCachedArrayBufferResponse = true;
             doClone = false;
           } else {
             MOZ_ASSERT(!JS_IsDetachedArrayBufferObject(obj));
--- a/dom/xul/templates/nsXULContentUtils.cpp
+++ b/dom/xul/templates/nsXULContentUtils.cpp
@@ -22,17 +22,16 @@
 /*
 
   A package of routines shared by the XUL content code.
 
  */
 
 #include "mozilla/ArrayUtils.h"
 
-#include "DateTimeFormat.h"
 #include "nsCOMPtr.h"
 #include "nsIContent.h"
 #include "nsIDocument.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMXULCommandDispatcher.h"
 #include "nsIDOMXULDocument.h"
 #include "nsIRDFNode.h"
 #include "nsIRDFService.h"
@@ -44,29 +43,31 @@
 #include "nsRDFCID.h"
 #include "nsString.h"
 #include "nsXPIDLString.h"
 #include "nsGkAtoms.h"
 #include "mozilla/Logging.h"
 #include "prtime.h"
 #include "rdf.h"
 #include "nsContentUtils.h"
+#include "nsIDateTimeFormat.h"
 #include "nsIScriptableDateFormat.h"
 #include "nsICollation.h"
 #include "nsCollationCID.h"
 #include "nsILocale.h"
 #include "nsILocaleService.h"
 #include "nsIConsoleService.h"
 #include "nsEscape.h"
 
 using namespace mozilla;
 
 //------------------------------------------------------------------------
 
 nsIRDFService* nsXULContentUtils::gRDF;
+nsIDateTimeFormat* nsXULContentUtils::gFormat;
 nsICollation *nsXULContentUtils::gCollation;
 
 extern LazyLogModule gXULTemplateLog;
 
 #define XUL_RESOURCE(ident, uri) nsIRDFResource* nsXULContentUtils::ident
 #define XUL_LITERAL(ident, val) nsIRDFLiteral* nsXULContentUtils::ident
 #include "nsXULResourceList.h"
 #undef XUL_RESOURCE
@@ -96,31 +97,37 @@ nsXULContentUtils::Init()
    rv = gRDF->GetLiteral(val, &(ident));                          \
    if (NS_FAILED(rv)) return rv;                                  \
   PR_END_MACRO
 
 #include "nsXULResourceList.h"
 #undef XUL_RESOURCE
 #undef XUL_LITERAL
 
+    gFormat = nsIDateTimeFormat::Create().take();
+    if (!gFormat) {
+        return NS_ERROR_FAILURE;
+    }
+
     return NS_OK;
 }
 
 
 nsresult
 nsXULContentUtils::Finish()
 {
     NS_IF_RELEASE(gRDF);
 
 #define XUL_RESOURCE(ident, uri) NS_IF_RELEASE(ident)
 #define XUL_LITERAL(ident, val) NS_IF_RELEASE(ident)
 #include "nsXULResourceList.h"
 #undef XUL_RESOURCE
 #undef XUL_LITERAL
 
+    NS_IF_RELEASE(gFormat);
     NS_IF_RELEASE(gCollation);
 
     return NS_OK;
 }
 
 nsICollation*
 nsXULContentUtils::GetCollation()
 {
@@ -202,21 +209,21 @@ nsXULContentUtils::GetTextForNode(nsIRDF
 
     nsCOMPtr<nsIRDFDate> dateLiteral = do_QueryInterface(aNode);
     if (dateLiteral) {
         PRTime value;
         rv = dateLiteral->GetValue(&value);
         if (NS_FAILED(rv)) return rv;
 
         nsAutoString str;
-        rv = DateTimeFormat::FormatPRTime(kDateFormatShort,
-                                          kTimeFormatSeconds,
-                                          value,
-                                          str);
-
+        rv = gFormat->FormatPRTime(nullptr /* nsILocale* locale */,
+                                  kDateFormatShort,
+                                  kTimeFormatSeconds,
+                                  value,
+                                  str);
         aResult.Assign(str);
 
         if (NS_FAILED(rv)) return rv;
 
         return NS_OK;
     }
 
     nsCOMPtr<nsIRDFInt> intLiteral = do_QueryInterface(aNode);
--- a/dom/xul/templates/nsXULContentUtils.h
+++ b/dom/xul/templates/nsXULContentUtils.h
@@ -16,16 +16,17 @@
 
 class nsIAtom;
 class nsIContent;
 class nsIDocument;
 class nsIRDFNode;
 class nsIRDFResource;
 class nsIRDFLiteral;
 class nsIRDFService;
+class nsIDateTimeFormat;
 class nsICollation;
 
 // errors to pass to LogTemplateError
 #define ERROR_TEMPLATE_INVALID_QUERYPROCESSOR                           \
         "querytype attribute doesn't specify a valid query processor"
 #define ERROR_TEMPLATE_INVALID_QUERYSET                                 \
         "unexpected <queryset> element"
 #define ERROR_TEMPLATE_NO_MEMBERVAR                                     \
@@ -80,16 +81,17 @@ class nsICollation;
         "the type of a query parameter is wrong"
 #define ERROR_TEMPLATE_STORAGE_QUERY_PARAMETER_NOT_BOUND                \
         "a query parameter cannot be bound to the SQL query"
 
 class nsXULContentUtils
 {
 protected:
     static nsIRDFService* gRDF;
+    static nsIDateTimeFormat* gFormat;
     static nsICollation *gCollation;
 
     static bool gDisableXULCache;
 
     static int
     DisableXULCacheChangedCallback(const char* aPrefName, void* aClosure);
 
 public:
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -976,33 +976,31 @@ HTMLEditRules::GetIndentState(bool* aCan
     nsCOMPtr<nsIDOMNode> parent, tmp, root = do_QueryInterface(mHTMLEditor->GetRoot());
     NS_ENSURE_TRUE(root, NS_ERROR_NULL_POINTER);
     int32_t selOffset;
     NS_ENSURE_STATE(mHTMLEditor);
     RefPtr<Selection> selection = mHTMLEditor->GetSelection();
     NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
 
     // test start parent hierarchy
-    NS_ENSURE_STATE(mHTMLEditor);
-    rv = mHTMLEditor->GetStartNodeAndOffset(selection, getter_AddRefs(parent),
-                                            &selOffset);
+    rv = EditorBase::GetStartNodeAndOffset(selection, getter_AddRefs(parent),
+                                           &selOffset);
     NS_ENSURE_SUCCESS(rv, rv);
     while (parent && parent != root) {
       if (HTMLEditUtils::IsNodeThatCanOutdent(parent)) {
         *aCanOutdent = true;
         break;
       }
       tmp = parent;
       tmp->GetParentNode(getter_AddRefs(parent));
     }
 
     // test end parent hierarchy
-    NS_ENSURE_STATE(mHTMLEditor);
-    rv = mHTMLEditor->GetEndNodeAndOffset(selection, getter_AddRefs(parent),
-                                          &selOffset);
+    rv = EditorBase::GetEndNodeAndOffset(selection, getter_AddRefs(parent),
+                                         &selOffset);
     NS_ENSURE_SUCCESS(rv, rv);
     while (parent && parent != root) {
       if (HTMLEditUtils::IsNodeThatCanOutdent(parent)) {
         *aCanOutdent = true;
         break;
       }
       tmp = parent;
       tmp->GetParentNode(getter_AddRefs(parent));
@@ -1047,19 +1045,18 @@ HTMLEditRules::GetParagraphState(bool* a
   // we might have an empty node list.  if so, find selection parent
   // and put that on the list
   if (arrayOfNodes.IsEmpty()) {
     nsCOMPtr<nsINode> selNode;
     int32_t selOffset;
     NS_ENSURE_STATE(mHTMLEditor);
     RefPtr<Selection> selection = mHTMLEditor->GetSelection();
     NS_ENSURE_STATE(selection);
-    NS_ENSURE_STATE(mHTMLEditor);
-    rv = mHTMLEditor->GetStartNodeAndOffset(selection, getter_AddRefs(selNode),
-                                            &selOffset);
+    rv = EditorBase::GetStartNodeAndOffset(selection, getter_AddRefs(selNode),
+                                           &selOffset);
     NS_ENSURE_SUCCESS(rv, rv);
     NS_ENSURE_TRUE(selNode, NS_ERROR_NULL_POINTER);
     arrayOfNodes.AppendElement(*selNode);
   }
 
   // remember root node
   NS_ENSURE_STATE(mHTMLEditor);
   nsCOMPtr<nsIDOMElement> rootElem = do_QueryInterface(mHTMLEditor->GetRoot());
@@ -1267,17 +1264,17 @@ HTMLEditRules::WillInsertText(EditAction
   NS_ENSURE_STATE(mHTMLEditor);
   NS_ENSURE_STATE(aSelection->GetRangeAt(0));
   nsCOMPtr<nsINode> selNode = aSelection->GetRangeAt(0)->GetStartParent();
   int32_t selOffset = aSelection->GetRangeAt(0)->StartOffset();
   NS_ENSURE_STATE(selNode);
 
   // dont put text in places that can't have it
   NS_ENSURE_STATE(mHTMLEditor);
-  if (!mHTMLEditor->IsTextNode(selNode) &&
+  if (!EditorBase::IsTextNode(selNode) &&
       (!mHTMLEditor || !mHTMLEditor->CanContainTag(*selNode,
                                                    *nsGkAtoms::textTagName))) {
     return NS_ERROR_FAILURE;
   }
 
   if (aAction == EditAction::insertIMEText) {
     // Right now the WSRunObject code bails on empty strings, but IME needs
     // the InsertTextImpl() call to still happen since empty strings are meaningful there.
@@ -1665,20 +1662,19 @@ nsresult
 HTMLEditRules::SplitMailCites(Selection* aSelection,
                               bool* aHandled)
 {
   NS_ENSURE_TRUE(aSelection && aHandled, NS_ERROR_NULL_POINTER);
   nsCOMPtr<nsIContent> leftCite, rightCite;
   nsCOMPtr<nsINode> selNode;
   nsCOMPtr<Element> citeNode;
   int32_t selOffset;
-  NS_ENSURE_STATE(mHTMLEditor);
   nsresult rv =
-    mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(selNode),
-                                       &selOffset);
+    EditorBase::GetStartNodeAndOffset(aSelection, getter_AddRefs(selNode),
+                                      &selOffset);
   NS_ENSURE_SUCCESS(rv, rv);
   citeNode = GetTopEnclosingMailCite(*selNode);
   if (citeNode) {
     // If our selection is just before a break, nudge it to be
     // just after it.  This does two things for us.  It saves us the trouble of having to add
     // a break here ourselves to preserve the "blockness" of the inline span mailquote
     // (in the inline case), and :
     // it means the break won't end up making an empty line that happens to be inside a
@@ -2497,18 +2493,18 @@ nsresult
 HTMLEditRules::InsertBRIfNeeded(Selection* aSelection)
 {
   NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
 
   // get selection
   nsCOMPtr<nsINode> node;
   int32_t offset;
   nsresult rv =
-    mTextEditor->GetStartNodeAndOffset(aSelection,
-                                       getter_AddRefs(node), &offset);
+    EditorBase::GetStartNodeAndOffset(aSelection,
+                                      getter_AddRefs(node), &offset);
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
 
   // inline elements don't need any br
   if (!IsBlockNode(*node)) {
     return NS_OK;
   }
 
@@ -3015,19 +3011,19 @@ HTMLEditRules::DidDeleteSelection(Select
 {
   if (!aSelection) {
     return NS_ERROR_NULL_POINTER;
   }
 
   // find where we are
   nsCOMPtr<nsINode> startNode;
   int32_t startOffset;
-  nsresult rv = mTextEditor->GetStartNodeAndOffset(aSelection,
-                                                   getter_AddRefs(startNode),
-                                                   &startOffset);
+  nsresult rv = EditorBase::GetStartNodeAndOffset(aSelection,
+                                                  getter_AddRefs(startNode),
+                                                  &startOffset);
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
 
   // find any enclosing mailcite
   nsCOMPtr<Element> citeNode = GetTopEnclosingMailCite(*startNode);
   if (citeNode) {
     bool isEmpty = true, seenBR = false;
     NS_ENSURE_STATE(mHTMLEditor);
@@ -3621,21 +3617,21 @@ HTMLEditRules::WillCSSIndent(Selection* 
 
   // short circuit: detect case of collapsed selection inside an <li>.
   // just sublist that <li>.  This prevents bug 97797.
 
   nsCOMPtr<Element> liNode;
   if (aSelection->Collapsed()) {
     nsCOMPtr<nsINode> node;
     int32_t offset;
+    nsresult rv =
+      EditorBase::GetStartNodeAndOffset(aSelection,
+                                        getter_AddRefs(node), &offset);
+    NS_ENSURE_SUCCESS(rv, rv);
     NS_ENSURE_STATE(mHTMLEditor);
-    nsresult rv =
-      mHTMLEditor->GetStartNodeAndOffset(aSelection,
-                                         getter_AddRefs(node), &offset);
-    NS_ENSURE_SUCCESS(rv, rv);
     nsCOMPtr<Element> block = mHTMLEditor->GetBlock(*node);
     if (block && HTMLEditUtils::IsListItem(block)) {
       liNode = block;
     }
   }
 
   if (liNode) {
     arrayOfNodes.AppendElement(*liNode);
@@ -5794,17 +5790,17 @@ HTMLEditRules::GetNodesForOperation(
       aOperationType == EditAction::makeList ||
       aOperationType == EditAction::align ||
       aOperationType == EditAction::setAbsolutePosition ||
       aOperationType == EditAction::indent ||
       aOperationType == EditAction::outdent) {
     for (int32_t i = aOutArrayOfNodes.Length() - 1; i >= 0; i--) {
       OwningNonNull<nsINode> node = aOutArrayOfNodes[i];
       if (aTouchContent == TouchContent::yes && IsInlineNode(node) &&
-          htmlEditor->IsContainer(node) && !htmlEditor->IsTextNode(node)) {
+          htmlEditor->IsContainer(node) && !EditorBase::IsTextNode(node)) {
         nsTArray<OwningNonNull<nsINode>> arrayOfInlines;
         nsresult rv = BustUpInlinesAtBRs(*node->AsContent(), arrayOfInlines);
         NS_ENSURE_SUCCESS(rv, rv);
 
         // Put these nodes in aOutArrayOfNodes, replacing the current node
         aOutArrayOfNodes.RemoveElementAt(i);
         aOutArrayOfNodes.InsertElementsAt(i, arrayOfInlines);
       }
@@ -6334,17 +6330,17 @@ HTMLEditRules::ReturnInParagraph(Selecti
   nsCOMPtr<nsIContent> sibling;
   nsCOMPtr<nsIDOMNode> selNode = aNode;
   int32_t selOffset = aOffset;
 
   NS_ENSURE_STATE(mHTMLEditor);
   if (aNode == aPara && doesCRCreateNewP) {
     // we are at the edges of the block, newBRneeded not needed!
     sibling = node->AsContent();
-  } else if (mHTMLEditor->IsTextNode(aNode)) {
+  } else if (EditorBase::IsTextNode(aNode)) {
     nsCOMPtr<nsIDOMText> textNode = do_QueryInterface(aNode);
     uint32_t strLength;
     nsresult rv = textNode->GetLength(&strLength);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // at beginning of text node?
     if (!aOffset) {
       // is there a BR prior to it?
@@ -6473,17 +6469,17 @@ HTMLEditRules::SplitParagraph(nsIDOMNode
   NS_ENSURE_SUCCESS(rv, rv);
 
   // selection to beginning of right hand para;
   // look inside any containers that are up front.
   nsCOMPtr<nsINode> rightParaNode = do_QueryInterface(rightPara);
   NS_ENSURE_STATE(mHTMLEditor && rightParaNode);
   nsCOMPtr<nsIDOMNode> child =
     GetAsDOMNode(mHTMLEditor->GetLeftmostChild(rightParaNode, true));
-  if (mHTMLEditor->IsTextNode(child) ||
+  if (EditorBase::IsTextNode(child) ||
       mHTMLEditor->IsContainer(child)) {
     aSelection->Collapse(child,0);
   } else {
     int32_t offset;
     nsCOMPtr<nsIDOMNode> parent = EditorBase::GetNodeLocation(child, &offset);
     aSelection->Collapse(parent,offset);
   }
   return NS_OK;
@@ -7211,20 +7207,19 @@ HTMLEditRules::AdjustSpecialBreaks()
 }
 
 nsresult
 HTMLEditRules::AdjustWhitespace(Selection* aSelection)
 {
   // get selection point
   nsCOMPtr<nsIDOMNode> selNode;
   int32_t selOffset;
-  NS_ENSURE_STATE(mHTMLEditor);
   nsresult rv =
-    mHTMLEditor->GetStartNodeAndOffset(aSelection,
-                                       getter_AddRefs(selNode), &selOffset);
+    EditorBase::GetStartNodeAndOffset(aSelection,
+                                      getter_AddRefs(selNode), &selOffset);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // ask whitespace object to tweak nbsp's
   NS_ENSURE_STATE(mHTMLEditor);
   return WSRunObject(mHTMLEditor, selNode, selOffset).AdjustWhitespace();
 }
 
 nsresult
@@ -7233,20 +7228,19 @@ HTMLEditRules::PinSelectionToNewBlock(Se
   NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
   if (!aSelection->Collapsed()) {
     return NS_OK;
   }
 
   // get the (collapsed) selection location
   nsCOMPtr<nsIDOMNode> selNode, temp;
   int32_t selOffset;
-  NS_ENSURE_STATE(mHTMLEditor);
   nsresult rv =
-    mHTMLEditor->GetStartNodeAndOffset(aSelection,
-                                       getter_AddRefs(selNode), &selOffset);
+    EditorBase::GetStartNodeAndOffset(aSelection,
+                                      getter_AddRefs(selNode), &selOffset);
   NS_ENSURE_SUCCESS(rv, rv);
   temp = selNode;
 
   // use ranges and sRangeHelper to compare sel point to new block
   nsCOMPtr<nsINode> node = do_QueryInterface(selNode);
   NS_ENSURE_STATE(node);
   RefPtr<nsRange> range = new nsRange(node);
   rv = range->SetStart(selNode, selOffset);
@@ -7262,32 +7256,32 @@ HTMLEditRules::PinSelectionToNewBlock(Se
   if (nodeBefore && nodeAfter) {
     return NS_OK;  // selection is inside block
   } else if (nodeBefore) {
     // selection is after block.  put at end of block.
     nsCOMPtr<nsIDOMNode> tmp = GetAsDOMNode(mNewBlock);
     NS_ENSURE_STATE(mHTMLEditor);
     tmp = GetAsDOMNode(mHTMLEditor->GetLastEditableChild(*block));
     uint32_t endPoint;
-    if (mHTMLEditor->IsTextNode(tmp) ||
+    if (EditorBase::IsTextNode(tmp) ||
         mHTMLEditor->IsContainer(tmp)) {
       rv = EditorBase::GetLengthOfDOMNode(tmp, endPoint);
       NS_ENSURE_SUCCESS(rv, rv);
     } else {
       tmp = EditorBase::GetNodeLocation(tmp, (int32_t*)&endPoint);
       endPoint++;  // want to be after this node
     }
     return aSelection->Collapse(tmp, (int32_t)endPoint);
   } else {
     // selection is before block.  put at start of block.
     nsCOMPtr<nsIDOMNode> tmp = GetAsDOMNode(mNewBlock);
     NS_ENSURE_STATE(mHTMLEditor);
     tmp = GetAsDOMNode(mHTMLEditor->GetFirstEditableChild(*block));
     int32_t offset;
-    if (mHTMLEditor->IsTextNode(tmp) ||
+    if (EditorBase::IsTextNode(tmp) ||
         mHTMLEditor->IsContainer(tmp)) {
       tmp = EditorBase::GetNodeLocation(tmp, &offset);
     }
     return aSelection->Collapse(tmp, 0);
   }
 }
 
 void
@@ -7342,20 +7336,19 @@ HTMLEditRules::AdjustSelection(Selection
   // only a single break selected, and collapse it.  Good thing?  Beats me.
   if (!aSelection->Collapsed()) {
     return NS_OK;
   }
 
   // get the (collapsed) selection location
   nsCOMPtr<nsINode> selNode, temp;
   int32_t selOffset;
-  NS_ENSURE_STATE(mHTMLEditor);
   nsresult rv =
-    mHTMLEditor->GetStartNodeAndOffset(aSelection,
-                                       getter_AddRefs(selNode), &selOffset);
+    EditorBase::GetStartNodeAndOffset(aSelection,
+                                      getter_AddRefs(selNode), &selOffset);
   NS_ENSURE_SUCCESS(rv, rv);
   temp = selNode;
 
   // are we in an editable node?
   NS_ENSURE_STATE(mHTMLEditor);
   while (!mHTMLEditor->IsEditable(selNode)) {
     // scan up the tree until we find an editable place to be
     selNode = EditorBase::GetNodeLocation(temp, &selOffset);
@@ -7528,18 +7521,17 @@ HTMLEditRules::FindNearSelectableNode(ns
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     }
   }
 
   // scan in the right direction until we find an eligible text node,
   // but don't cross any breaks, images, or table elements.
-  NS_ENSURE_STATE(mHTMLEditor);
-  while (nearNode && !(mHTMLEditor->IsTextNode(nearNode) ||
+  while (nearNode && !(EditorBase::IsTextNode(nearNode) ||
                        TextEditUtils::IsBreak(nearNode) ||
                        HTMLEditUtils::IsImage(nearNode))) {
     curNode = nearNode;
     if (aDirection == nsIEditor::ePrevious) {
       NS_ENSURE_STATE(mHTMLEditor);
       nsresult rv =
         mHTMLEditor->GetPriorHTMLNode(curNode, address_of(nearNode));
       if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -7932,20 +7924,19 @@ HTMLEditRules::ConfirmSelectionInBody()
   // get the selection
   NS_ENSURE_STATE(mHTMLEditor);
   RefPtr<Selection> selection = mHTMLEditor->GetSelection();
   NS_ENSURE_STATE(selection);
 
   // get the selection start location
   nsCOMPtr<nsIDOMNode> selNode, temp, parent;
   int32_t selOffset;
-  NS_ENSURE_STATE(mHTMLEditor);
   nsresult rv =
-    mHTMLEditor->GetStartNodeAndOffset(selection,
-                                       getter_AddRefs(selNode), &selOffset);
+    EditorBase::GetStartNodeAndOffset(selection,
+                                      getter_AddRefs(selNode), &selOffset);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   temp = selNode;
 
   // check that selNode is inside body
   while (temp && !TextEditUtils::IsBody(temp)) {
@@ -7956,19 +7947,18 @@ HTMLEditRules::ConfirmSelectionInBody()
   // if we aren't in the body, force the issue
   if (!temp) {
 //    uncomment this to see when we get bad selections
 //    NS_NOTREACHED("selection not in body");
     selection->Collapse(rootElement, 0);
   }
 
   // get the selection end location
-  NS_ENSURE_STATE(mHTMLEditor);
-  rv = mHTMLEditor->GetEndNodeAndOffset(selection,
-                                        getter_AddRefs(selNode), &selOffset);
+  rv = EditorBase::GetEndNodeAndOffset(selection,
+                                       getter_AddRefs(selNode), &selOffset);
   NS_ENSURE_SUCCESS(rv, rv);
   temp = selNode;
 
   // check that selNode is inside body
   while (temp && !TextEditUtils::IsBody(temp)) {
     rv = temp->GetParentNode(getter_AddRefs(parent));
     temp = parent;
   }
@@ -8241,26 +8231,24 @@ HTMLEditRules::WillDeleteSelection(nsISe
   if (NS_WARN_IF(!aSelection)) {
     return NS_ERROR_INVALID_ARG;
   }
   RefPtr<Selection> selection = aSelection->AsSelection();
   // get the (collapsed) selection location
   nsCOMPtr<nsIDOMNode> selNode;
   int32_t selOffset;
 
-  NS_ENSURE_STATE(mHTMLEditor);
   nsresult rv =
-    mHTMLEditor->GetStartNodeAndOffset(selection,
-                                       getter_AddRefs(selNode), &selOffset);
+    EditorBase::GetStartNodeAndOffset(selection,
+                                      getter_AddRefs(selNode), &selOffset);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = mUtilRange->SetStart(selNode, selOffset);
   NS_ENSURE_SUCCESS(rv, rv);
-  NS_ENSURE_STATE(mHTMLEditor);
-  rv = mHTMLEditor->GetEndNodeAndOffset(selection,
-                                        getter_AddRefs(selNode), &selOffset);
+  rv = EditorBase::GetEndNodeAndOffset(selection,
+                                       getter_AddRefs(selNode), &selOffset);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = mUtilRange->SetEnd(selNode, selOffset);
   NS_ENSURE_SUCCESS(rv, rv);
   return UpdateDocChangeRange(mUtilRange);
 }
 
 NS_IMETHODIMP
 HTMLEditRules::DidDeleteSelection(nsISelection *aSelection)
@@ -8274,18 +8262,17 @@ HTMLEditRules::DidDeleteSelection(nsISel
 // children). We break on tables and don't look at their children.
 nsresult
 HTMLEditRules::RemoveAlignment(nsIDOMNode* aNode,
                                const nsAString& aAlignType,
                                bool aChildrenOnly)
 {
   NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
 
-  NS_ENSURE_STATE(mHTMLEditor);
-  if (mHTMLEditor->IsTextNode(aNode) || HTMLEditUtils::IsTable(aNode)) {
+  if (EditorBase::IsTextNode(aNode) || HTMLEditUtils::IsTable(aNode)) {
     return NS_OK;
   }
 
   nsCOMPtr<nsIDOMNode> child = aNode,tmp;
   if (aChildrenOnly) {
     aNode->GetFirstChild(getter_AddRefs(child));
   }
   NS_ENSURE_STATE(mHTMLEditor);
--- a/editor/libeditor/TextEditRules.cpp
+++ b/editor/libeditor/TextEditRules.cpp
@@ -438,18 +438,18 @@ TextEditRules::CollapseSelectionToTraili
     mTextEditor->EndOfDocument();
   }
 
   // if we are at the end of the textarea, we need to set the
   // selection to stick to the mozBR at the end of the textarea.
   int32_t selOffset;
   nsCOMPtr<nsIDOMNode> selNode;
   nsresult rv =
-    mTextEditor->GetStartNodeAndOffset(aSelection,
-                                       getter_AddRefs(selNode), &selOffset);
+    EditorBase::GetStartNodeAndOffset(aSelection,
+                                      getter_AddRefs(selNode), &selOffset);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIDOMText> nodeAsText = do_QueryInterface(selNode);
   if (!nodeAsText) {
     return NS_OK; // Nothing to do if we're not at a text node.
   }
 
   uint32_t length;
@@ -707,17 +707,17 @@ TextEditRules::WillInsertText(EditAction
   // get the (collapsed) selection location
   NS_ENSURE_STATE(aSelection->GetRangeAt(0));
   nsCOMPtr<nsINode> selNode = aSelection->GetRangeAt(0)->GetStartParent();
   int32_t selOffset = aSelection->GetRangeAt(0)->StartOffset();
   NS_ENSURE_STATE(selNode);
 
   // don't put text in places that can't have it
   NS_ENSURE_STATE(mTextEditor);
-  if (!mTextEditor->IsTextNode(selNode) &&
+  if (!EditorBase::IsTextNode(selNode) &&
       !mTextEditor->CanContainTag(*selNode, *nsGkAtoms::textTagName)) {
     return NS_ERROR_FAILURE;
   }
 
   // we need to get the doc
   NS_ENSURE_STATE(mTextEditor);
   nsCOMPtr<nsIDocument> doc = mTextEditor->GetDocument();
   NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED);
@@ -882,20 +882,19 @@ TextEditRules::WillDeleteSelection(Selec
     }
     // Extended selection.
     else {
       mPasswordText.Cut(start, end-start);
     }
   } else {
     nsCOMPtr<nsIDOMNode> startNode;
     int32_t startOffset;
-    NS_ENSURE_STATE(mTextEditor);
     nsresult rv =
-      mTextEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(startNode),
-                                         &startOffset);
+      EditorBase::GetStartNodeAndOffset(aSelection, getter_AddRefs(startNode),
+                                        &startOffset);
     NS_ENSURE_SUCCESS(rv, rv);
     NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
 
     bool bCollapsed;
     rv = aSelection->GetIsCollapsed(&bCollapsed);
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (!bCollapsed) {
@@ -927,32 +926,32 @@ TextEditRules::WillDeleteSelection(Selec
 
 nsresult
 TextEditRules::DidDeleteSelection(Selection* aSelection,
                                   nsIEditor::EDirection aCollapsedAction,
                                   nsresult aResult)
 {
   nsCOMPtr<nsIDOMNode> startNode;
   int32_t startOffset;
-  NS_ENSURE_STATE(mTextEditor);
   nsresult rv =
-    mTextEditor->GetStartNodeAndOffset(aSelection,
-                                       getter_AddRefs(startNode), &startOffset);
+    EditorBase::GetStartNodeAndOffset(aSelection,
+                                      getter_AddRefs(startNode), &startOffset);
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
 
   // delete empty text nodes at selection
-  if (mTextEditor->IsTextNode(startNode)) {
+  if (EditorBase::IsTextNode(startNode)) {
     nsCOMPtr<nsIDOMText> textNode = do_QueryInterface(startNode);
     uint32_t strLength;
     rv = textNode->GetLength(&strLength);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // are we in an empty text node?
     if (!strLength) {
+      NS_ENSURE_STATE(mTextEditor);
       rv = mTextEditor->DeleteNode(startNode);
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
   if (mDidExplicitlySetInterline) {
     return NS_OK;
   }
   // We prevent the caret from sticking on the left of prior BR
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -978,37 +978,45 @@ private:
 public:
     void fClear(GLbitfield mask) {
         BeforeGLDrawCall();
         raw_fClear(mask);
         AfterGLDrawCall();
     }
 
     void fClearBufferfi(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) {
+        BeforeGLDrawCall();
         BEFORE_GL_CALL;
         mSymbols.fClearBufferfi(buffer, drawbuffer, depth, stencil);
         AFTER_GL_CALL;
+        AfterGLDrawCall();
     }
 
     void fClearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat* value) {
+        BeforeGLDrawCall();
         BEFORE_GL_CALL;
         mSymbols.fClearBufferfv(buffer, drawbuffer, value);
         AFTER_GL_CALL;
+        AfterGLDrawCall();
     }
 
     void fClearBufferiv(GLenum buffer, GLint drawbuffer, const GLint* value) {
+        BeforeGLDrawCall();
         BEFORE_GL_CALL;
         mSymbols.fClearBufferiv(buffer, drawbuffer, value);
         AFTER_GL_CALL;
+        AfterGLDrawCall();
     }
 
     void fClearBufferuiv(GLenum buffer, GLint drawbuffer, const GLuint* value) {
+        BeforeGLDrawCall();
         BEFORE_GL_CALL;
         mSymbols.fClearBufferuiv(buffer, drawbuffer, value);
         AFTER_GL_CALL;
+        AfterGLDrawCall();
     }
 
     void fClearColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) {
         BEFORE_GL_CALL;
         mSymbols.fClearColor(r, g, b, a);
         AFTER_GL_CALL;
     }
 
--- a/gfx/gl/GLScreenBuffer.h
+++ b/gfx/gl/GLScreenBuffer.h
@@ -236,19 +236,18 @@ public:
     void BeforeReadCall();
 
     bool CopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x,
                         GLint y, GLsizei width, GLsizei height, GLint border);
 
     void SetReadBuffer(GLenum userMode);
     void SetDrawBuffer(GLenum userMode);
 
-    GLenum GetReadBufferMode() const {
-        return mUserReadBufferMode;
-    }
+    GLenum GetReadBufferMode() const { return mUserReadBufferMode; }
+    GLenum GetDrawBufferMode() const { return mUserDrawBufferMode; }
 
     /**
      * Attempts to read pixels from the current bound framebuffer, if
      * it is backed by a SharedSurface.
      *
      * Returns true if the pixel data has been read back, false
      * otherwise.
      */
--- a/intl/build/nsI18nModule.cpp
+++ b/intl/build/nsI18nModule.cpp
@@ -56,22 +56,25 @@ NS_DEFINE_NAMED_CID(NS_STRINGBUNDLESERVI
 NS_DEFINE_NAMED_CID(NS_STRINGBUNDLETEXTOVERRIDE_CID);
 NS_DEFINE_NAMED_CID(NS_LOCALESERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_COLLATIONFACTORY_CID);
 NS_DEFINE_NAMED_CID(NS_SCRIPTABLEDATEFORMAT_CID);
 NS_DEFINE_NAMED_CID(NS_LANGUAGEATOMSERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_PLATFORMCHARSET_CID);
 #ifdef XP_WIN
 NS_DEFINE_NAMED_CID(NS_COLLATION_CID);
+NS_DEFINE_NAMED_CID(NS_DATETIMEFORMAT_CID);
 #endif
 #ifdef USE_UNIX_LOCALE
 NS_DEFINE_NAMED_CID(NS_COLLATION_CID);
+NS_DEFINE_NAMED_CID(NS_DATETIMEFORMAT_CID);
 #endif
 #ifdef USE_MAC_LOCALE
 NS_DEFINE_NAMED_CID(NS_COLLATION_CID);
+NS_DEFINE_NAMED_CID(NS_DATETIMEFORMAT_CID);
 #endif
 
 static const mozilla::Module::CIDEntry kIntlCIDs[] = {
     { &kNS_LBRK_CID, false, nullptr, nsJISx4051LineBreakerConstructor },
     { &kNS_WBRK_CID, false, nullptr, nsSampleWordBreakerConstructor },
     { &kNS_SEMANTICUNITSCANNER_CID, false, nullptr, nsSemanticUnitScannerConstructor },
     { &kNS_UNICHARUTIL_CID, false, nullptr, nsCaseConversionImp2Constructor },
     { &kNS_UNICHARCATEGORY_CID, false, nullptr, nsCategoryImpConstructor },
@@ -82,22 +85,25 @@ static const mozilla::Module::CIDEntry k
     { &kNS_STRINGBUNDLETEXTOVERRIDE_CID, false, nullptr, nsStringBundleTextOverrideConstructor },
     { &kNS_LOCALESERVICE_CID, false, nullptr, CreateLocaleService },
     { &kNS_COLLATIONFACTORY_CID, false, nullptr, nsCollationFactoryConstructor },
     { &kNS_SCRIPTABLEDATEFORMAT_CID, false, nullptr, NS_NewScriptableDateFormat },
     { &kNS_LANGUAGEATOMSERVICE_CID, false, nullptr, nsLanguageAtomServiceConstructor },
     { &kNS_PLATFORMCHARSET_CID, false, nullptr, nsPlatformCharsetConstructor },
 #ifdef XP_WIN
     { &kNS_COLLATION_CID, false, nullptr, nsCollationWinConstructor },
+    { &kNS_DATETIMEFORMAT_CID, false, nullptr, nsDateTimeFormatWinConstructor },
 #endif
 #ifdef USE_UNIX_LOCALE
     { &kNS_COLLATION_CID, false, nullptr, nsCollationUnixConstructor },
+    { &kNS_DATETIMEFORMAT_CID, false, nullptr, nsDateTimeFormatUnixConstructor },
 #endif
 #ifdef USE_MAC_LOCALE
     { &kNS_COLLATION_CID, false, nullptr, nsCollationMacUCConstructor },
+    { &kNS_DATETIMEFORMAT_CID, false, nullptr, nsDateTimeFormatMacConstructor },
 #endif
     { nullptr }
 };
 
 static const mozilla::Module::ContractIDEntry kIntlContracts[] = {
     { NS_LBRK_CONTRACTID, &kNS_LBRK_CID },
     { NS_WBRK_CONTRACTID, &kNS_WBRK_CID },
     { NS_SEMANTICUNITSCANNER_CONTRACTID, &kNS_SEMANTICUNITSCANNER_CID },
@@ -110,22 +116,25 @@ static const mozilla::Module::ContractID
     { NS_STRINGBUNDLETEXTOVERRIDE_CONTRACTID, &kNS_STRINGBUNDLETEXTOVERRIDE_CID },
     { NS_LOCALESERVICE_CONTRACTID, &kNS_LOCALESERVICE_CID },
     { NS_COLLATIONFACTORY_CONTRACTID, &kNS_COLLATIONFACTORY_CID },
     { NS_SCRIPTABLEDATEFORMAT_CONTRACTID, &kNS_SCRIPTABLEDATEFORMAT_CID },
     { NS_LANGUAGEATOMSERVICE_CONTRACTID, &kNS_LANGUAGEATOMSERVICE_CID },
     { NS_PLATFORMCHARSET_CONTRACTID, &kNS_PLATFORMCHARSET_CID },
 #ifdef XP_WIN
     { NS_COLLATION_CONTRACTID, &kNS_COLLATION_CID },
+    { NS_DATETIMEFORMAT_CONTRACTID, &kNS_DATETIMEFORMAT_CID },
 #endif
 #ifdef USE_UNIX_LOCALE
     { NS_COLLATION_CONTRACTID, &kNS_COLLATION_CID },
+    { NS_DATETIMEFORMAT_CONTRACTID, &kNS_DATETIMEFORMAT_CID },
 #endif
 #ifdef USE_MAC_LOCALE
     { NS_COLLATION_CONTRACTID, &kNS_COLLATION_CID },
+    { NS_DATETIMEFORMAT_CONTRACTID, &kNS_DATETIMEFORMAT_CID },
 #endif
     { nullptr }
 };
 
 static const mozilla::Module kIntlModule = {
     mozilla::Module::kVersion,
     kIntlCIDs,
     kIntlContracts,
deleted file mode 100644
--- a/intl/locale/DateTimeFormat.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef mozilla_DateTimeFormat_h
-#define mozilla_DateTimeFormat_h
-
-#include <time.h>
-#include "nsIScriptableDateFormat.h"
-#include "nsStringGlue.h"
-#include "prtime.h"
-
-#ifndef ENABLE_INTL_API
-#include "nsCOMPtr.h"
-#include "nsIUnicodeDecoder.h"
-#endif
-
-namespace mozilla {
-
-class DateTimeFormat {
-public:
-  // performs a locale sensitive date formatting operation on the time_t parameter
-  static nsresult FormatTime(const nsDateFormatSelector aDateFormatSelector,
-                             const nsTimeFormatSelector aTimeFormatSelector,
-                             const time_t aTimetTime,
-                             nsAString& aStringOut);
-
-  // performs a locale sensitive date formatting operation on the PRTime parameter
-  static nsresult FormatPRTime(const nsDateFormatSelector aDateFormatSelector,
-                               const nsTimeFormatSelector aTimeFormatSelector,
-                               const PRTime aPrTime,
-                               nsAString& aStringOut);
-
-  // performs a locale sensitive date formatting operation on the PRExplodedTime parameter
-  static nsresult FormatPRExplodedTime(const nsDateFormatSelector aDateFormatSelector,
-                                       const nsTimeFormatSelector aTimeFormatSelector,
-                                       const PRExplodedTime* aExplodedTime,
-                                       nsAString& aStringOut);
-
-  static void Shutdown();
-
-private:
-  DateTimeFormat() = delete;
-
-  static nsresult Initialize();
-
-#ifdef ENABLE_INTL_API
-  static nsCString* mLocale;
-#else
-  // performs a locale sensitive date formatting operation on the struct tm parameter
-  static nsresult FormatTMTime(const nsDateFormatSelector aDateFormatSelector,
-                               const nsTimeFormatSelector aTimeFormatSelector,
-                               const struct tm* aTmTime,
-                               nsAString& aStringOut);
-
-  static void LocalePreferred24hour();
-
-  static bool mLocalePreferred24hour;                       // true if 24 hour format is preferred by current locale
-  static bool mLocaleAMPMfirst;                             // true if AM/PM string is preferred before the time
-  static nsCOMPtr<nsIUnicodeDecoder> mDecoder;
-#endif
-};
-
-}
-
-#endif  /* mozilla_DateTimeFormat_h */
deleted file mode 100644
--- a/intl/locale/DateTimeFormatICU.cpp
+++ /dev/null
@@ -1,161 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "DateTimeFormat.h"
-#include "nsCOMPtr.h"
-#include "nsIServiceManager.h"
-#include "nsILocaleService.h"
-#include "unicode/udat.h"
-
-namespace mozilla {
-
-nsCString* DateTimeFormat::mLocale = nullptr;
-
-/*static*/ nsresult
-DateTimeFormat::Initialize()
-{
-  nsAutoString localeStr;
-  nsresult rv = NS_OK;
-
-  if (!mLocale) {
-    mLocale = new nsCString();
-  } else if (!mLocale->IsEmpty()) {
-    return NS_OK;
-  }
-
-  nsCOMPtr<nsILocaleService> localeService =
-           do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv);
-  if (NS_SUCCEEDED(rv)) {
-    nsCOMPtr<nsILocale> appLocale;
-    rv = localeService->GetApplicationLocale(getter_AddRefs(appLocale));
-    if (NS_SUCCEEDED(rv)) {
-      rv = appLocale->GetCategory(NS_LITERAL_STRING("NSILOCALE_TIME"), localeStr);
-      if (NS_SUCCEEDED(rv) && !localeStr.IsEmpty()) {
-        *mLocale = NS_LossyConvertUTF16toASCII(localeStr); // cache locale name
-      }
-    }
-  }
-
-  return rv;
-}
-
-// performs a locale sensitive date formatting operation on the time_t parameter
-/*static*/ nsresult
-DateTimeFormat::FormatTime(const nsDateFormatSelector aDateFormatSelector,
-                           const nsTimeFormatSelector aTimeFormatSelector,
-                           const time_t aTimetTime,
-                           nsAString& aStringOut)
-{
-  return FormatPRTime(aDateFormatSelector, aTimeFormatSelector, (aTimetTime * PR_USEC_PER_SEC), aStringOut);
-}
-
-// performs a locale sensitive date formatting operation on the PRTime parameter
-/*static*/ nsresult
-DateTimeFormat::FormatPRTime(const nsDateFormatSelector aDateFormatSelector,
-                             const nsTimeFormatSelector aTimeFormatSelector,
-                             const PRTime aPrTime,
-                             nsAString& aStringOut)
-{
-#define DATETIME_FORMAT_INITIAL_LEN 127
-  int32_t dateTimeLen = 0;
-  nsresult rv = NS_OK;
-
-  // return, nothing to format
-  if (aDateFormatSelector == kDateFormatNone && aTimeFormatSelector == kTimeFormatNone) {
-    aStringOut.Truncate();
-    return NS_OK;
-  }
-
-  // set up locale data
-  rv = Initialize();
-
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  UDate timeUDate = aPrTime / PR_USEC_PER_MSEC;
-
-  // Get the date style for the formatter:
-  UDateFormatStyle dateStyle;
-  switch (aDateFormatSelector) {
-    case kDateFormatLong:
-      dateStyle = UDAT_LONG;
-      break;
-    case kDateFormatShort:
-      dateStyle = UDAT_SHORT;
-      break;
-    case kDateFormatNone:
-      dateStyle = UDAT_NONE;
-      break;
-    default:
-      NS_ERROR("Unknown nsDateFormatSelector");
-      return NS_ERROR_ILLEGAL_VALUE;
-  }
-
-  // Get the time style for the formatter:
-  UDateFormatStyle timeStyle;
-  switch (aTimeFormatSelector) {
-    case kTimeFormatSeconds:
-      timeStyle = UDAT_MEDIUM;
-      break;
-    case kTimeFormatNoSeconds:
-      timeStyle = UDAT_SHORT;
-      break;
-    case kTimeFormatNone:
-      timeStyle = UDAT_NONE;
-      break;
-    default:
-      NS_ERROR("Unknown nsTimeFormatSelector");
-      return NS_ERROR_ILLEGAL_VALUE;
-  }
-
-  // generate date/time string
-
-  UErrorCode status = U_ZERO_ERROR;
-
-  UDateFormat* dateTimeFormat = udat_open(timeStyle, dateStyle, mLocale->get(), nullptr, -1, nullptr, -1, &status);
-
-  if (U_SUCCESS(status) && dateTimeFormat) {
-    aStringOut.SetLength(DATETIME_FORMAT_INITIAL_LEN);
-    dateTimeLen = udat_format(dateTimeFormat, timeUDate, reinterpret_cast<UChar*>(aStringOut.BeginWriting()), DATETIME_FORMAT_INITIAL_LEN, nullptr, &status);
-    aStringOut.SetLength(dateTimeLen);
-
-    if (status == U_BUFFER_OVERFLOW_ERROR) {
-      status = U_ZERO_ERROR;
-      udat_format(dateTimeFormat, timeUDate, reinterpret_cast<UChar*>(aStringOut.BeginWriting()), dateTimeLen, nullptr, &status);
-    }
-  }
-
-  if (U_FAILURE(status)) {
-    rv = NS_ERROR_FAILURE;
-  }
-
-  if (dateTimeFormat) {
-    udat_close(dateTimeFormat);
-  }
-
-  return rv;
-}
-
-// performs a locale sensitive date formatting operation on the PRExplodedTime parameter
-/*static*/ nsresult
-DateTimeFormat::FormatPRExplodedTime(const nsDateFormatSelector aDateFormatSelector,
-                                     const nsTimeFormatSelector aTimeFormatSelector,
-                                     const PRExplodedTime* aExplodedTime,
-                                     nsAString& aStringOut)
-{
-  return FormatPRTime(aDateFormatSelector, aTimeFormatSelector, PR_ImplodeTime(aExplodedTime), aStringOut);
-}
-
-/*static*/ void
-DateTimeFormat::Shutdown()
-{
-  if (mLocale) {
-    delete mLocale;
-  }
-}
-
-}
deleted file mode 100644
--- a/intl/locale/DateTimeFormatUnix.cpp
+++ /dev/null
@@ -1,239 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "DateTimeFormat.h"
-#include "plstr.h"
-#include "nsIServiceManager.h"
-#include "nsILocaleService.h"
-#include "nsIPlatformCharset.h"
-#include "mozilla/dom/EncodingUtils.h"
-
-using mozilla::dom::EncodingUtils;
-
-namespace mozilla {
-
-bool DateTimeFormat::mLocalePreferred24hour;
-bool DateTimeFormat::mLocaleAMPMfirst;
-nsCOMPtr<nsIUnicodeDecoder> DateTimeFormat::mDecoder;
-
-/*static*/ nsresult
-DateTimeFormat::Initialize()
-{
-  nsAutoString localeStr;
-  nsAutoCString charset;
-  nsresult rv = NS_OK;
-
-  if (mDecoder) {
-    return NS_OK;
-  }
-
-  charset.AssignLiteral("windows-1252");
-
-  nsCOMPtr<nsILocaleService> localeService =
-    do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv);
-  if (NS_SUCCEEDED(rv)) {
-    nsCOMPtr<nsILocale> appLocale;
-    rv = localeService->GetApplicationLocale(getter_AddRefs(appLocale));
-    if (NS_SUCCEEDED(rv)) {
-      rv = appLocale->GetCategory(NS_LITERAL_STRING("NSILOCALE_TIME##PLATFORM"), localeStr);
-      NS_ASSERTION(NS_SUCCEEDED(rv), "failed to get app locale info");
-    }
-  }
-
-  if (NS_SUCCEEDED(rv) && !localeStr.IsEmpty()) {
-    nsCOMPtr<nsIPlatformCharset> platformCharset = do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &rv);
-    if (NS_SUCCEEDED(rv)) {
-      nsAutoCString mappedCharset;
-      rv = platformCharset->GetDefaultCharsetForLocale(localeStr, mappedCharset);
-      if (NS_SUCCEEDED(rv)) {
-        charset = mappedCharset;
-      }
-    }
-  }
-
-  mDecoder = EncodingUtils::DecoderForEncoding(charset);
-
-  LocalePreferred24hour();
-
-  return rv;
-}
-
-/*static*/ void
-DateTimeFormat::LocalePreferred24hour()
-{
-  char str[100];
-  time_t tt;
-  struct tm *tmc;
-  int i;
-
-  tt = time(nullptr);
-  tmc = localtime(&tt);
-
-  tmc->tm_hour=22;    // put the test sample hour to 22:00 which is 10PM
-  tmc->tm_min=0;      // set the min & sec other number than '2'
-  tmc->tm_sec=0;
-
-  strftime(str, (size_t)99, "%X", (struct tm *)tmc);
-
-  mLocalePreferred24hour = false;
-  for (i=0; str[i]; i++) {
-    if (str[i] == '2') {    // if there is any '2', that locale use 0-23 time format
-      mLocalePreferred24hour = true;
-      break;
-    }
-  }
-
-  mLocaleAMPMfirst = true;
-  if (mLocalePreferred24hour == false) {
-    if (str[0] && str[0] == '1') { // if the first character is '1' of 10:00,
-			           // AMPM string is located after 10:00
-      mLocaleAMPMfirst = false;
-    }
-  }
-}
-
-/*static*/ nsresult
-DateTimeFormat::FormatTime(const nsDateFormatSelector aDateFormatSelector,
-                           const nsTimeFormatSelector aTimeFormatSelector,
-                           const time_t aTimetTime,
-                           nsAString& aStringOut)
-{
-  struct tm tmTime;
-  memcpy(&tmTime, localtime(&aTimetTime), sizeof(struct tm));
-  return FormatTMTime(aDateFormatSelector, aTimeFormatSelector, &tmTime, aStringOut);
-}
-
-// performs a locale sensitive date formatting operation on the struct tm parameter
-/*static*/ nsresult
-DateTimeFormat::FormatTMTime(const nsDateFormatSelector aDateFormatSelector,
-                             const nsTimeFormatSelector aTimeFormatSelector,
-                             const struct tm* aTmTime,
-                             nsAString& aStringOut)
-{
-#define NSDATETIME_FORMAT_BUFFER_LEN 80
-  char strOut[NSDATETIME_FORMAT_BUFFER_LEN*2];  // buffer for date and time
-  char fmtD[NSDATETIME_FORMAT_BUFFER_LEN], fmtT[NSDATETIME_FORMAT_BUFFER_LEN];
-  nsresult rv;
-
-  // set up locale data
-  (void) Initialize();
-  NS_ENSURE_TRUE(mDecoder, NS_ERROR_NOT_INITIALIZED);
-
-  // set date format
-  if (aDateFormatSelector == kDateFormatLong && aTimeFormatSelector == kTimeFormatSeconds) {
-    PL_strncpy(fmtD, "%c", NSDATETIME_FORMAT_BUFFER_LEN);
-    PL_strncpy(fmtT, "", NSDATETIME_FORMAT_BUFFER_LEN);
-  } else {
-
-    switch (aDateFormatSelector) {
-      case kDateFormatNone:
-        PL_strncpy(fmtD, "", NSDATETIME_FORMAT_BUFFER_LEN);
-        break;
-      case kDateFormatLong:
-      case kDateFormatShort:
-        PL_strncpy(fmtD, "%x", NSDATETIME_FORMAT_BUFFER_LEN);
-        break;
-      default:
-        PL_strncpy(fmtD, "", NSDATETIME_FORMAT_BUFFER_LEN);
-    }
-
-    // set time format
-    switch (aTimeFormatSelector) {
-      case kTimeFormatNone:
-        PL_strncpy(fmtT, "", NSDATETIME_FORMAT_BUFFER_LEN);
-        break;
-      case kTimeFormatSeconds:
-        PL_strncpy(fmtT, "%X", NSDATETIME_FORMAT_BUFFER_LEN);
-        break;
-      case kTimeFormatNoSeconds:
-        PL_strncpy(fmtT,
-                   mLocalePreferred24hour ? "%H:%M" : mLocaleAMPMfirst ? "%p %I:%M" : "%I:%M %p",
-                   NSDATETIME_FORMAT_BUFFER_LEN);
-        break;
-      default:
-        PL_strncpy(fmtT, "", NSDATETIME_FORMAT_BUFFER_LEN);
-    }
-  }
-
-  // generate date/time string
-  if (strlen(fmtD) && strlen(fmtT)) {
-    PL_strncat(fmtD, " ", NSDATETIME_FORMAT_BUFFER_LEN);
-    PL_strncat(fmtD, fmtT, NSDATETIME_FORMAT_BUFFER_LEN);
-    strftime(strOut, NSDATETIME_FORMAT_BUFFER_LEN, fmtD, aTmTime);
-  } else if (strlen(fmtD) && !strlen(fmtT)) {
-    strftime(strOut, NSDATETIME_FORMAT_BUFFER_LEN, fmtD, aTmTime);
-  } else if (!strlen(fmtD) && strlen(fmtT)) {
-    strftime(strOut, NSDATETIME_FORMAT_BUFFER_LEN, fmtT, aTmTime);
-  } else {
-    PL_strncpy(strOut, "", NSDATETIME_FORMAT_BUFFER_LEN);
-  }
-
-  // convert result to unicode
-  int32_t srcLength = (int32_t) strlen(strOut);
-  int32_t unicharLength = NSDATETIME_FORMAT_BUFFER_LEN*2;
-  char16_t unichars[NSDATETIME_FORMAT_BUFFER_LEN*2];   // buffer for date and time
-
-  rv = mDecoder->Convert(strOut, &srcLength, unichars, &unicharLength);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-  aStringOut.Assign(unichars, unicharLength);
-
-  return rv;
-}
-
-// performs a locale sensitive date formatting operation on the PRTime parameter
-/*static*/ nsresult
-DateTimeFormat::FormatPRTime(const nsDateFormatSelector aDateFormatSelector,
-                             const nsTimeFormatSelector aTimeFormatSelector,
-                             const PRTime aPrTime,
-                             nsAString& aStringOut)
-{
-  PRExplodedTime explodedTime;
-  PR_ExplodeTime(aPrTime, PR_LocalTimeParameters, &explodedTime);
-
-  return FormatPRExplodedTime(aDateFormatSelector, aTimeFormatSelector, &explodedTime, aStringOut);
-}
-
-// performs a locale sensitive date formatting operation on the PRExplodedTime parameter
-/*static*/ nsresult
-DateTimeFormat::FormatPRExplodedTime(const nsDateFormatSelector aDateFormatSelector,
-                                     const nsTimeFormatSelector aTimeFormatSelector,
-                                     const PRExplodedTime* aExplodedTime,
-                                     nsAString& aStringOut)
-{
-  struct tm  tmTime;
-  /* be safe and set all members of struct tm to zero
-   *
-   * there are other fields in the tm struct that we aren't setting
-   * (tm_isdst, tm_gmtoff, tm_zone, should we set these?) and since
-   * tmTime is on the stack, it may be filled with garbage, but
-   * the garbage may vary.  (this may explain why some saw bug #10412, and
-   * others did not.
-   *
-   * when tmTime is passed to strftime() with garbage bad things may happen.
-   * see bug #10412
-   */
-  memset( &tmTime, 0, sizeof(tmTime) );
-
-  tmTime.tm_yday = aExplodedTime->tm_yday;
-  tmTime.tm_wday = aExplodedTime->tm_wday;
-  tmTime.tm_year = aExplodedTime->tm_year;
-  tmTime.tm_year -= 1900;
-  tmTime.tm_mon = aExplodedTime->tm_month;
-  tmTime.tm_mday = aExplodedTime->tm_mday;
-  tmTime.tm_hour = aExplodedTime->tm_hour;
-  tmTime.tm_min = aExplodedTime->tm_min;
-  tmTime.tm_sec = aExplodedTime->tm_sec;
-
-  return FormatTMTime(aDateFormatSelector, aTimeFormatSelector, &tmTime, aStringOut);
-}
-
-/*static*/ void
-DateTimeFormat::Shutdown()
-{
-}
-
-}
--- a/intl/locale/mac/moz.build
+++ b/intl/locale/mac/moz.build
@@ -1,15 +1,16 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 UNIFIED_SOURCES += [
     'nsCollationMacUC.cpp',
+    'nsDateTimeFormatMac.cpp',
     'nsMacCharset.cpp',
 ]
 
 FINAL_LIBRARY = 'xul'
 LOCAL_INCLUDES += [
     '..',
 ]
new file mode 100644
--- /dev/null
+++ b/intl/locale/mac/nsDateTimeFormatMac.cpp
@@ -0,0 +1,266 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <CoreFoundation/CoreFoundation.h>
+#include "nsIServiceManager.h"
+#include "nsDateTimeFormatMac.h"
+#include <CoreFoundation/CFDateFormatter.h>
+#include "nsIComponentManager.h"
+#include "nsILocaleService.h"
+#include "nsCRT.h"
+#include "plstr.h"
+#include "nsUnicharUtils.h"
+#include "nsTArray.h"
+
+
+NS_IMPL_ISUPPORTS(nsDateTimeFormatMac, nsIDateTimeFormat)
+
+nsresult nsDateTimeFormatMac::Initialize(nsILocale* locale)
+{
+  nsAutoString localeStr;
+  nsAutoString category(NS_LITERAL_STRING("NSILOCALE_TIME"));
+  nsresult res;
+
+  // use cached info if match with stored locale
+  if (nullptr == locale) {
+    if (!mLocale.IsEmpty() &&
+        mLocale.Equals(mAppLocale, nsCaseInsensitiveStringComparator())) {
+      return NS_OK;
+    }
+  }
+  else {
+    res = locale->GetCategory(category, localeStr);
+    if (NS_SUCCEEDED(res) && !localeStr.IsEmpty()) {
+      if (!mLocale.IsEmpty() &&
+          mLocale.Equals(localeStr,
+                         nsCaseInsensitiveStringComparator())) {
+        return NS_OK;
+      }
+    }
+  }
+
+  // get application locale
+  nsCOMPtr<nsILocaleService> localeService = 
+           do_GetService(NS_LOCALESERVICE_CONTRACTID, &res);
+  if (NS_SUCCEEDED(res)) {
+    nsCOMPtr<nsILocale> appLocale;
+    res = localeService->GetApplicationLocale(getter_AddRefs(appLocale));
+    if (NS_SUCCEEDED(res)) {
+      res = appLocale->GetCategory(category, localeStr);
+      if (NS_SUCCEEDED(res) && !localeStr.IsEmpty()) {
+        mAppLocale = localeStr; // cache app locale name
+      }
+    }
+  }
+  
+  // use app default if no locale specified
+  if (nullptr == locale) {
+    mUseDefaultLocale = true;
+  }
+  else {
+    mUseDefaultLocale = false;
+    res = locale->GetCategory(category, localeStr);
+  }
+    
+  if (NS_SUCCEEDED(res) && !localeStr.IsEmpty()) {
+    mLocale.Assign(localeStr); // cache locale name
+  }
+
+  return res;
+}
+
+// performs a locale sensitive date formatting operation on the time_t parameter
+nsresult nsDateTimeFormatMac::FormatTime(nsILocale* locale, 
+                                      const nsDateFormatSelector  dateFormatSelector, 
+                                      const nsTimeFormatSelector timeFormatSelector, 
+                                      const time_t  timetTime, 
+                                      nsAString& stringOut)
+{
+  struct tm tmTime;
+  return FormatTMTime(locale, dateFormatSelector, timeFormatSelector, localtime_r(&timetTime, &tmTime), stringOut);
+}
+
+// performs a locale sensitive date formatting operation on the struct tm parameter
+nsresult nsDateTimeFormatMac::FormatTMTime(nsILocale* locale, 
+                                           const nsDateFormatSelector  dateFormatSelector, 
+                                           const nsTimeFormatSelector timeFormatSelector, 
+                                           const struct tm*  tmTime, 
+                                           nsAString& stringOut)
+{
+  nsresult res = NS_OK;
+
+  // set up locale data
+  (void) Initialize(locale);
+  
+  // return, nothing to format
+  if (dateFormatSelector == kDateFormatNone && timeFormatSelector == kTimeFormatNone) {
+    stringOut.Truncate();
+    return NS_OK;
+  }
+
+  NS_ASSERTION(tmTime->tm_mon >= 0, "tm is not set correctly");
+  NS_ASSERTION(tmTime->tm_mday >= 1, "tm is not set correctly");
+  NS_ASSERTION(tmTime->tm_hour >= 0, "tm is not set correctly");
+  NS_ASSERTION(tmTime->tm_min >= 0, "tm is not set correctly");
+  NS_ASSERTION(tmTime->tm_sec >= 0, "tm is not set correctly");
+  NS_ASSERTION(tmTime->tm_wday >= 0, "tm is not set correctly");
+
+  // Got the locale for the formatter:
+  CFLocaleRef formatterLocale;
+  if (!locale) {
+    formatterLocale = CFLocaleCopyCurrent();
+  } else {
+    CFStringRef localeStr = CFStringCreateWithCharacters(nullptr,
+                                                         reinterpret_cast<const UniChar*>(mLocale.get()),
+                                                         mLocale.Length());
+    formatterLocale = CFLocaleCreate(nullptr, localeStr);
+    CFRelease(localeStr);
+  }
+
+  // Get the date style for the formatter:  
+  CFDateFormatterStyle dateStyle;
+  switch (dateFormatSelector) {
+    case kDateFormatLong:
+      dateStyle = kCFDateFormatterLongStyle;
+      break;
+    case kDateFormatShort:
+      dateStyle = kCFDateFormatterShortStyle;
+      break;
+    case kDateFormatYearMonth:
+    case kDateFormatWeekday:
+      dateStyle = kCFDateFormatterNoStyle; // formats handled below
+      break;
+    case kDateFormatNone:
+      dateStyle = kCFDateFormatterNoStyle;
+      break;
+    default:
+      NS_ERROR("Unknown nsDateFormatSelector");
+      res = NS_ERROR_FAILURE;
+      dateStyle = kCFDateFormatterNoStyle;
+  }
+  
+  // Get the time style for the formatter:
+  CFDateFormatterStyle timeStyle;
+  switch (timeFormatSelector) {
+    case kTimeFormatSeconds:
+    case kTimeFormatSecondsForce24Hour: // 24 hour part fixed below
+      timeStyle = kCFDateFormatterMediumStyle;
+      break;
+    case kTimeFormatNoSeconds:
+    case kTimeFormatNoSecondsForce24Hour: // 24 hour part fixed below
+      timeStyle = kCFDateFormatterShortStyle;
+      break;
+    case kTimeFormatNone:
+      timeStyle = kCFDateFormatterNoStyle;
+      break;
+    default:
+      NS_ERROR("Unknown nsTimeFormatSelector");
+      res = NS_ERROR_FAILURE;
+      timeStyle = kCFDateFormatterNoStyle;
+  }
+  
+  // Create the formatter and fix up its formatting as necessary:
+  CFDateFormatterRef formatter =
+    CFDateFormatterCreate(nullptr, formatterLocale, dateStyle, timeStyle);
+  
+  CFRelease(formatterLocale);
+  
+  if (dateFormatSelector == kDateFormatYearMonth ||
+      dateFormatSelector == kDateFormatWeekday) {
+    CFStringRef dateFormat =
+      dateFormatSelector == kDateFormatYearMonth ? CFSTR("yyyy/MM ") : CFSTR("EEE ");
+    
+    CFStringRef oldFormat = CFDateFormatterGetFormat(formatter);
+    CFMutableStringRef newFormat = CFStringCreateMutableCopy(nullptr, 0, oldFormat);
+    CFStringInsert(newFormat, 0, dateFormat);
+    CFDateFormatterSetFormat(formatter, newFormat);
+    CFRelease(newFormat); // note we don't own oldFormat
+  }
+  
+  if (timeFormatSelector == kTimeFormatSecondsForce24Hour ||
+      timeFormatSelector == kTimeFormatNoSecondsForce24Hour) {
+    // Replace "h" with "H", and remove "a":
+    CFStringRef oldFormat = CFDateFormatterGetFormat(formatter);
+    CFMutableStringRef newFormat = CFStringCreateMutableCopy(nullptr, 0, oldFormat);
+    CFIndex replaceCount = CFStringFindAndReplace(newFormat,
+                                                  CFSTR("h"), CFSTR("H"),
+                                                  CFRangeMake(0, CFStringGetLength(newFormat)),	
+                                                  0);
+    NS_ASSERTION(replaceCount <= 2, "Unexpected number of \"h\" occurrences");
+    replaceCount = CFStringFindAndReplace(newFormat,
+                                          CFSTR("a"), CFSTR(""),
+                                          CFRangeMake(0, CFStringGetLength(newFormat)),	
+                                          0);
+    NS_ASSERTION(replaceCount <= 1, "Unexpected number of \"a\" occurrences");
+    CFDateFormatterSetFormat(formatter, newFormat);
+    CFRelease(newFormat); // note we don't own oldFormat
+  }
+  
+  // Now get the formatted date:
+  CFGregorianDate date;
+  date.second = tmTime->tm_sec;
+  date.minute = tmTime->tm_min;
+  date.hour = tmTime->tm_hour;
+  date.day = tmTime->tm_mday;      // Mac is 1-based, tm is 1-based
+  date.month = tmTime->tm_mon + 1; // Mac is 1-based, tm is 0-based
+  date.year = tmTime->tm_year + 1900;
+
+  CFTimeZoneRef timeZone = CFTimeZoneCopySystem(); // tmTime is in local time
+  CFAbsoluteTime absTime = CFGregorianDateGetAbsoluteTime(date, timeZone);
+  CFRelease(timeZone);
+
+  CFStringRef formattedDate = CFDateFormatterCreateStringWithAbsoluteTime(nullptr,
+                                                                          formatter,
+                                                                          absTime);
+
+  CFIndex stringLen = CFStringGetLength(formattedDate);
+
+  AutoTArray<UniChar, 256> stringBuffer;
+  stringBuffer.SetLength(stringLen + 1);
+  CFStringGetCharacters(formattedDate, CFRangeMake(0, stringLen), stringBuffer.Elements());
+  stringOut.Assign(reinterpret_cast<char16_t*>(stringBuffer.Elements()), stringLen);
+
+  CFRelease(formattedDate);
+  CFRelease(formatter);
+
+  return res;
+}
+
+// performs a locale sensitive date formatting operation on the PRTime parameter
+nsresult nsDateTimeFormatMac::FormatPRTime(nsILocale* locale, 
+                                           const nsDateFormatSelector  dateFormatSelector, 
+                                           const nsTimeFormatSelector timeFormatSelector, 
+                                           const PRTime  prTime, 
+                                           nsAString& stringOut)
+{
+  PRExplodedTime explodedTime;
+  PR_ExplodeTime(prTime, PR_LocalTimeParameters, &explodedTime);
+
+  return FormatPRExplodedTime(locale, dateFormatSelector, timeFormatSelector, &explodedTime, stringOut);
+}
+
+// performs a locale sensitive date formatting operation on the PRExplodedTime parameter
+nsresult nsDateTimeFormatMac::FormatPRExplodedTime(nsILocale* locale, 
+                                                   const nsDateFormatSelector  dateFormatSelector, 
+                                                   const nsTimeFormatSelector timeFormatSelector, 
+                                                   const PRExplodedTime*  explodedTime, 
+                                                   nsAString& stringOut)
+{
+  struct tm  tmTime;
+  memset( &tmTime, 0, sizeof(tmTime) );
+
+  tmTime.tm_yday = explodedTime->tm_yday;
+  tmTime.tm_wday = explodedTime->tm_wday;
+  tmTime.tm_year = explodedTime->tm_year;
+  tmTime.tm_year -= 1900;
+  tmTime.tm_mon = explodedTime->tm_month;
+  tmTime.tm_mday = explodedTime->tm_mday;
+  tmTime.tm_hour = explodedTime->tm_hour;
+  tmTime.tm_min = explodedTime->tm_min;
+  tmTime.tm_sec = explodedTime->tm_sec;
+
+  return FormatTMTime(locale, dateFormatSelector, timeFormatSelector, &tmTime, stringOut);
+}
+
new file mode 100644
--- /dev/null
+++ b/intl/locale/mac/nsDateTimeFormatMac.h
@@ -0,0 +1,61 @@
+
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef nsDateTimeFormatMac_h__
+#define nsDateTimeFormatMac_h__
+
+
+#include "nsCOMPtr.h"
+#include "nsIDateTimeFormat.h"
+
+
+class nsDateTimeFormatMac : public nsIDateTimeFormat {
+
+public: 
+  NS_DECL_THREADSAFE_ISUPPORTS 
+
+  // performs a locale sensitive date formatting operation on the time_t parameter
+  NS_IMETHOD FormatTime(nsILocale* locale, 
+                        const nsDateFormatSelector  dateFormatSelector, 
+                        const nsTimeFormatSelector timeFormatSelector, 
+                        const time_t  timetTime, 
+                        nsAString& stringOut) override; 
+
+  // performs a locale sensitive date formatting operation on the struct tm parameter
+  NS_IMETHOD FormatTMTime(nsILocale* locale, 
+                          const nsDateFormatSelector  dateFormatSelector, 
+                          const nsTimeFormatSelector timeFormatSelector, 
+                          const struct tm*  tmTime, 
+                          nsAString& stringOut) override; 
+  // performs a locale sensitive date formatting operation on the PRTime parameter
+  NS_IMETHOD FormatPRTime(nsILocale* locale, 
+                          const nsDateFormatSelector  dateFormatSelector, 
+                          const nsTimeFormatSelector timeFormatSelector, 
+                          const PRTime  prTime, 
+                          nsAString& stringOut) override;
+
+  // performs a locale sensitive date formatting operation on the PRExplodedTime parameter
+  NS_IMETHOD FormatPRExplodedTime(nsILocale* locale, 
+                                  const nsDateFormatSelector  dateFormatSelector, 
+                                  const nsTimeFormatSelector timeFormatSelector, 
+                                  const PRExplodedTime*  explodedTime, 
+                                  nsAString& stringOut) override; 
+
+  nsDateTimeFormatMac() {}
+
+protected:
+  virtual ~nsDateTimeFormatMac() {}
+
+private:
+  // init this interface to a specified locale
+  NS_IMETHOD Initialize(nsILocale* locale);
+
+  nsString    mLocale;
+  nsString    mAppLocale;
+  bool        mUseDefaultLocale;
+};
+
+#endif  /* nsDateTimeFormatMac_h__ */
--- a/intl/locale/moz.build
+++ b/intl/locale/moz.build
@@ -20,44 +20,37 @@ XPIDL_SOURCES += [
     'nsILocale.idl',
     'nsILocaleService.idl',
     'nsIScriptableDateFormat.idl',
 ]
 
 XPIDL_MODULE = 'locale'
 
 EXPORTS += [
-    'DateTimeFormat.h',
     'nsCollation.h',
     'nsCollationCID.h',
+    'nsDateTimeFormatCID.h',
+    'nsIDateTimeFormat.h',
     'nsILanguageAtomService.h',
     'nsIPlatformCharset.h',
     'nsPosixLocale.h',
     'nsUConvPropertySearch.h',
     'nsWin32Locale.h',
 ]
 
 UNIFIED_SOURCES += [
     'nsCollation.cpp',
+    'nsIDateTimeFormat.cpp',
     'nsLanguageAtomService.cpp',
     'nsLocale.cpp',
     'nsLocaleService.cpp',
     'nsScriptableDateFormat.cpp',
     'nsUConvPropertySearch.cpp',
 ]
 
-if CONFIG['ENABLE_INTL_API']:
-    UNIFIED_SOURCES += [
-        'DateTimeFormatICU.cpp',
-    ]
-else:
-    UNIFIED_SOURCES += [
-        'DateTimeFormatUnix.cpp',
-    ]
-
 EXTRA_JS_MODULES += [
     'PluralForm.jsm',
 ]
 
 FINAL_LIBRARY = 'xul'
 
 LOCAL_INCLUDES += [
     '/intl/uconv',
new file mode 100644
--- /dev/null
+++ b/intl/locale/nsDateTimeFormatCID.h
@@ -0,0 +1,20 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef nsDateTimeFormatCID_h__
+#define nsDateTimeFormatCID_h__
+
+
+#include "nscore.h"
+#include "nsISupports.h"
+
+#define NS_DATETIMEFORMAT_CONTRACTID "@mozilla.org/intl/datetimeformat;1"
+
+// {0704E7C0-A758-11d2-9119-006008A6EDF6}
+#define NS_DATETIMEFORMAT_CID \
+{ 0x704e7c0, 0xa758, 0x11d2, \
+{ 0x91, 0x19, 0x0, 0x60, 0x8, 0xa6, 0xed, 0xf6 } }
+
+#endif  // nsDateTimeFormatCID_h__
+
new file mode 100644
--- /dev/null
+++ b/intl/locale/nsIDateTimeFormat.cpp
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIDateTimeFormat.h"
+#include "mozilla/RefPtr.h"
+
+#if defined(XP_MACOSX)
+#define USE_MAC_LOCALE
+#elif defined(XP_UNIX)
+#define USE_UNIX_LOCALE
+#endif
+
+#ifdef XP_WIN
+#include "windows/nsDateTimeFormatWin.h"
+#endif
+#ifdef USE_UNIX_LOCALE
+#include "unix/nsDateTimeFormatUnix.h"
+#endif
+#ifdef USE_MAC_LOCALE
+#include "mac/nsDateTimeFormatMac.h"
+#endif
+
+using mozilla::MakeAndAddRef;
+
+/*static*/ already_AddRefed<nsIDateTimeFormat>
+nsIDateTimeFormat::Create()
+{
+#ifdef XP_WIN
+  return MakeAndAddRef<nsDateTimeFormatWin>();
+#elif defined(USE_UNIX_LOCALE)
+  return MakeAndAddRef<nsDateTimeFormatUnix>();
+#elif defined(USE_MAC_LOCALE)
+  return MakeAndAddRef<nsDateTimeFormatMac>();
+#else
+  return nullptr;
+#endif
+}
new file mode 100644
--- /dev/null
+++ b/intl/locale/nsIDateTimeFormat.h
@@ -0,0 +1,69 @@
+
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef nsIDateTimeFormat_h__
+#define nsIDateTimeFormat_h__
+
+
+#include "nsISupports.h"
+#include "nscore.h"
+#include "nsStringGlue.h"
+#include "nsILocale.h"
+#include "nsIScriptableDateFormat.h"
+#include "prtime.h"
+#include <time.h>
+
+
+// {2BBAA0B0-A591-11d2-9119-006008A6EDF6}
+#define NS_IDATETIMEFORMAT_IID \
+{ 0x2bbaa0b0, 0xa591, 0x11d2, \
+{ 0x91, 0x19, 0x0, 0x60, 0x8, 0xa6, 0xed, 0xf6 } }
+
+
+// Locale sensitive date and time format interface
+// 
+class nsIDateTimeFormat : public nsISupports {
+protected:
+  nsIDateTimeFormat() {}
+  virtual ~nsIDateTimeFormat() {}
+
+public: 
+  NS_DECLARE_STATIC_IID_ACCESSOR(NS_IDATETIMEFORMAT_IID)
+
+  static already_AddRefed<nsIDateTimeFormat> Create();
+
+  // performs a locale sensitive date formatting operation on the time_t parameter
+  NS_IMETHOD FormatTime(nsILocale* locale, 
+                        const nsDateFormatSelector  dateFormatSelector, 
+                        const nsTimeFormatSelector timeFormatSelector, 
+                        const time_t  timetTime,
+                        nsAString& stringOut) = 0; 
+
+  // performs a locale sensitive date formatting operation on the struct tm parameter
+  NS_IMETHOD FormatTMTime(nsILocale* locale, 
+                          const nsDateFormatSelector  dateFormatSelector, 
+                          const nsTimeFormatSelector timeFormatSelector, 
+                          const struct tm*  tmTime, 
+                          nsAString& stringOut) = 0; 
+
+  // performs a locale sensitive date formatting operation on the PRTime parameter
+  NS_IMETHOD FormatPRTime(nsILocale* locale, 
+                          const nsDateFormatSelector  dateFormatSelector, 
+                          const nsTimeFormatSelector timeFormatSelector, 
+                          const PRTime  prTime, 
+                          nsAString& stringOut) = 0;
+
+  // performs a locale sensitive date formatting operation on the PRExplodedTime parameter
+  NS_IMETHOD FormatPRExplodedTime(nsILocale* locale, 
+                                  const nsDateFormatSelector  dateFormatSelector, 
+                                  const nsTimeFormatSelector timeFormatSelector, 
+                                  const PRExplodedTime*  explodedTime, 
+                                  nsAString& stringOut) = 0; 
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIDateTimeFormat, NS_IDATETIMEFORMAT_IID)
+
+#endif  /* nsIDateTimeFormat_h__ */
--- a/intl/locale/nsLocaleConstructors.h
+++ b/intl/locale/nsLocaleConstructors.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 nsLocaleConstructors_h__
 #define nsLocaleConstructors_h__
 
 #include "nsCollationCID.h"
+#include "nsDateTimeFormatCID.h"
 #include "mozilla/ModuleUtils.h"
 #include "nsILocaleService.h"
 #include "nsIScriptableDateFormat.h"
 #include "nsIServiceManager.h"
 #include "nsLanguageAtomService.h"
 #include "nsPlatformCharset.h"
 
 #if defined(XP_MACOSX)
@@ -19,24 +20,27 @@
 #endif
 
 #if defined(XP_UNIX) && !defined(XP_MACOSX)
 #define USE_UNIX_LOCALE
 #endif
 
 #ifdef XP_WIN
 #include "windows/nsCollationWin.h"
+#include "windows/nsDateTimeFormatWin.h"
 #endif
 
 #ifdef USE_MAC_LOCALE
 #include "mac/nsCollationMacUC.h"
+#include "mac/nsDateTimeFormatMac.h"
 #endif
 
 #ifdef USE_UNIX_LOCALE
 #include "unix/nsCollationUnix.h"
+#include "unix/nsDateTimeFormatUnix.h"
 #endif
 
 #define NSLOCALE_MAKE_CTOR(ctor_, iface_, func_)          \
 static nsresult                                           \
 ctor_(nsISupports* aOuter, REFNSIID aIID, void** aResult) \
 {                                                         \
   *aResult = nullptr;                                      \
   if (aOuter)                                             \
@@ -54,19 +58,22 @@ ctor_(nsISupports* aOuter, REFNSIID aIID
 NSLOCALE_MAKE_CTOR(CreateLocaleService, nsILocaleService, NS_NewLocaleService)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsCollationFactory)
 //NS_GENERIC_FACTORY_CONSTRUCTOR(nsScriptableDateTimeFormat)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsLanguageAtomService)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPlatformCharset, Init)
 
 #ifdef XP_WIN
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsCollationWin)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsDateTimeFormatWin)
 #endif
 
 #ifdef USE_UNIX_LOCALE
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsCollationUnix)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsDateTimeFormatUnix)
 #endif  
 
 #ifdef USE_MAC_LOCALE
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsCollationMacUC)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsDateTimeFormatMac)
 #endif  
 
 #endif
--- a/intl/locale/nsScriptableDateFormat.cpp
+++ b/intl/locale/nsScriptableDateFormat.cpp
@@ -1,21 +1,23 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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 "DateTimeFormat.h"
 #include "mozilla/Sprintf.h"
 #include "nsILocaleService.h"
+#include "nsDateTimeFormatCID.h"
+#include "nsIDateTimeFormat.h"
 #include "nsIScriptableDateFormat.h"
 #include "nsCOMPtr.h"
 #include "nsServiceManagerUtils.h"
 
 static NS_DEFINE_CID(kLocaleServiceCID, NS_LOCALESERVICE_CID);
+static NS_DEFINE_CID(kDateTimeFormatCID, NS_DATETIMEFORMAT_CID);
 
 class nsScriptableDateFormat : public nsIScriptableDateFormat {
  public: 
   NS_DECL_ISUPPORTS 
 
   NS_IMETHOD FormatDateTime(const char16_t *locale, 
                             nsDateFormatSelector dateFormatSelector, 
                             nsTimeFormatSelector timeFormatSelector, 
@@ -82,44 +84,47 @@ NS_IMETHODIMP nsScriptableDateFormat::Fo
     // get locale service
     nsCOMPtr<nsILocaleService> localeService(do_GetService(kLocaleServiceCID, &rv));
     NS_ENSURE_SUCCESS(rv, rv);
     // get locale
     rv = localeService->NewLocale(localeName, getter_AddRefs(locale));
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
+  nsCOMPtr<nsIDateTimeFormat> dateTimeFormat(do_CreateInstance(kDateTimeFormatCID, &rv));
+  NS_ENSURE_SUCCESS(rv, rv);
+
   tm tmTime;
   time_t timetTime;
 
   memset(&tmTime, 0, sizeof(tmTime));
   tmTime.tm_year = year - 1900;
   tmTime.tm_mon = month - 1;
   tmTime.tm_mday = day;
   tmTime.tm_hour = hour;
   tmTime.tm_min = minute;
   tmTime.tm_sec = second;
   tmTime.tm_yday = tmTime.tm_wday = 0;
   tmTime.tm_isdst = -1;
   timetTime = mktime(&tmTime);
 
   if ((time_t)-1 != timetTime) {
-    rv = mozilla::DateTimeFormat::FormatTime(dateFormatSelector, timeFormatSelector,
-                                             timetTime, mStringOut);
+    rv = dateTimeFormat->FormatTime(locale, dateFormatSelector, timeFormatSelector, 
+                                     timetTime, mStringOut);
   }
   else {
     // if mktime fails (e.g. year <= 1970), then try NSPR.
     PRTime prtime;
     char string[32];
     SprintfLiteral(string, "%.2d/%.2d/%d %.2d:%.2d:%.2d", month, day, year, hour, minute, second);
     if (PR_SUCCESS != PR_ParseTimeString(string, false, &prtime))
       return NS_ERROR_INVALID_ARG;
 
-    rv = mozilla::DateTimeFormat::FormatPRTime(dateFormatSelector, timeFormatSelector,
-                                               prtime, mStringOut);
+    rv = dateTimeFormat->FormatPRTime(locale, dateFormatSelector, timeFormatSelector, 
+                                      prtime, mStringOut);
   }
   if (NS_SUCCEEDED(rv))
     *dateTimeString = ToNewUnicode(mStringOut);
 
   return rv;
 }
 
 nsresult
--- a/intl/locale/unix/moz.build
+++ b/intl/locale/unix/moz.build
@@ -1,16 +1,17 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 SOURCES += [
     'nsCollationUnix.cpp',
+    'nsDateTimeFormatUnix.cpp',
     'nsPosixLocale.cpp',
 ]
 
 if CONFIG['OS_TARGET'] == 'Android':
     SOURCES += [
         'nsAndroidCharset.cpp',
     ]
 else:
new file mode 100644
--- /dev/null
+++ b/intl/locale/unix/nsDateTimeFormatUnix.cpp
@@ -0,0 +1,284 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <locale.h>
+#include "plstr.h"
+#include "nsIServiceManager.h"
+#include "nsDateTimeFormatUnix.h"
+#include "nsIComponentManager.h"
+#include "nsILocaleService.h"
+#include "nsIPlatformCharset.h"
+#include "nsPosixLocale.h"
+#include "nsCRT.h"
+#include "nsReadableUtils.h"
+#include "nsUnicharUtils.h"
+#include "mozilla/dom/EncodingUtils.h"
+
+using mozilla::dom::EncodingUtils;
+
+NS_IMPL_ISUPPORTS(nsDateTimeFormatUnix, nsIDateTimeFormat)
+
+// init this interface to a specified locale
+nsresult nsDateTimeFormatUnix::Initialize(nsILocale* locale)
+{
+  nsAutoString localeStr;
+  NS_NAMED_LITERAL_STRING(aCategory, "NSILOCALE_TIME##PLATFORM");
+  nsresult res = NS_OK;
+
+  // use cached info if match with stored locale
+  if (!locale) {
+    if (!mLocale.IsEmpty() &&
+        mLocale.Equals(mAppLocale, nsCaseInsensitiveStringComparator())) {
+      return NS_OK;
+    }
+  }
+  else {
+    res = locale->GetCategory(aCategory, localeStr);
+    if (NS_SUCCEEDED(res) && !localeStr.IsEmpty()) {
+      if (!mLocale.IsEmpty() &&
+          mLocale.Equals(localeStr,
+                         nsCaseInsensitiveStringComparator())) {
+        return NS_OK;
+      }
+    }
+  }
+
+  mCharset.AssignLiteral("windows-1252");
+  mPlatformLocale.AssignLiteral("en_US");
+
+  // get locale name string, use app default if no locale specified
+  if (!locale) {
+    nsCOMPtr<nsILocaleService> localeService = 
+             do_GetService(NS_LOCALESERVICE_CONTRACTID, &res);
+    if (NS_SUCCEEDED(res)) {
+      nsCOMPtr<nsILocale> appLocale;
+      res = localeService->GetApplicationLocale(getter_AddRefs(appLocale));
+      if (NS_SUCCEEDED(res)) {
+        res = appLocale->GetCategory(aCategory, localeStr);
+        if (NS_SUCCEEDED(res) && !localeStr.IsEmpty()) {
+          NS_ASSERTION(NS_SUCCEEDED(res), "failed to get app locale info");
+          mAppLocale = localeStr; // cache app locale name
+        }
+      }
+    }
+  }
+  else {
+    res = locale->GetCategory(aCategory, localeStr);
+    NS_ASSERTION(NS_SUCCEEDED(res), "failed to get locale info");
+  }
+
+  if (NS_SUCCEEDED(res) && !localeStr.IsEmpty()) {
+    mLocale = localeStr; // cache locale name
+
+    nsPosixLocale::GetPlatformLocale(mLocale, mPlatformLocale);
+
+    nsCOMPtr <nsIPlatformCharset> platformCharset = do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &res);
+    if (NS_SUCCEEDED(res)) {
+      nsAutoCString mappedCharset;
+      res = platformCharset->GetDefaultCharsetForLocale(mLocale, mappedCharset);
+      if (NS_SUCCEEDED(res)) {
+        mCharset = mappedCharset;
+      }
+    }
+  }
+
+  mDecoder = EncodingUtils::DecoderForEncoding(mCharset);
+
+  LocalePreferred24hour();
+
+  return res;
+}
+
+void nsDateTimeFormatUnix::LocalePreferred24hour()
+{
+  char str[100];
+  time_t tt;
+  struct tm *tmc;
+  int i;
+
+  tt = time(nullptr);
+  tmc = localtime(&tt);
+
+  tmc->tm_hour=22;    // put the test sample hour to 22:00 which is 10PM
+  tmc->tm_min=0;      // set the min & sec other number than '2'
+  tmc->tm_sec=0;
+
+  char *temp = setlocale(LC_TIME, mPlatformLocale.get());
+  strftime(str, (size_t)99, "%X", (struct tm *)tmc);
+
+  (void) setlocale(LC_TIME, temp);
+
+  mLocalePreferred24hour = false;
+  for (i=0; str[i]; i++) {
+    if (str[i] == '2') {    // if there is any '2', that locale use 0-23 time format
+        mLocalePreferred24hour = true;
+        break;
+    }
+  }
+
+  mLocaleAMPMfirst = true;
+  if (mLocalePreferred24hour == false) {
+    if (str[0] && str[0] == '1') { // if the first character is '1' of 10:00,
+			           // AMPM string is located after 10:00
+      mLocaleAMPMfirst = false;
+    }
+  }
+}
+
+nsresult nsDateTimeFormatUnix::FormatTime(nsILocale* locale, 
+                                      const nsDateFormatSelector  dateFormatSelector, 
+                                      const nsTimeFormatSelector timeFormatSelector, 
+                                      const time_t  timetTime, 
+                                      nsAString& stringOut) 
+{
+  struct tm tmTime;
+  memcpy(&tmTime, localtime(&timetTime), sizeof(struct tm));
+  return FormatTMTime(locale, dateFormatSelector, timeFormatSelector, &tmTime, stringOut);
+}
+
+// performs a locale sensitive date formatting operation on the struct tm parameter
+nsresult nsDateTimeFormatUnix::FormatTMTime(nsILocale* locale, 
+                                        const nsDateFormatSelector  dateFormatSelector, 
+                                        const nsTimeFormatSelector timeFormatSelector, 
+                                        const struct tm*  tmTime, 
+                                        nsAString& stringOut) 
+{
+#define NSDATETIME_FORMAT_BUFFER_LEN  80
+  char strOut[NSDATETIME_FORMAT_BUFFER_LEN*2];  // buffer for date and time
+  char fmtD[NSDATETIME_FORMAT_BUFFER_LEN], fmtT[NSDATETIME_FORMAT_BUFFER_LEN];
+  nsresult rv;
+
+  
+  // set up locale data
+  (void) Initialize(locale);
+  NS_ENSURE_TRUE(mDecoder, NS_ERROR_NOT_INITIALIZED);
+
+  // set date format
+  if (dateFormatSelector == kDateFormatLong && timeFormatSelector == kTimeFormatSeconds) {
+    PL_strncpy(fmtD, "%c", NSDATETIME_FORMAT_BUFFER_LEN);
+    PL_strncpy(fmtT, "", NSDATETIME_FORMAT_BUFFER_LEN); 
+  } else {
+
+    switch (dateFormatSelector) {
+      case kDateFormatNone:
+        PL_strncpy(fmtD, "", NSDATETIME_FORMAT_BUFFER_LEN);
+        break; 
+      case kDateFormatLong:
+      case kDateFormatShort:
+        PL_strncpy(fmtD, "%x", NSDATETIME_FORMAT_BUFFER_LEN);
+        break; 
+      case kDateFormatYearMonth:
+        PL_strncpy(fmtD, "%Y/%m", NSDATETIME_FORMAT_BUFFER_LEN);
+        break; 
+      case kDateFormatWeekday:
+        PL_strncpy(fmtD, "%a", NSDATETIME_FORMAT_BUFFER_LEN);
+        break;
+      default:
+        PL_strncpy(fmtD, "", NSDATETIME_FORMAT_BUFFER_LEN); 
+    }
+
+    // set time format
+    switch (timeFormatSelector) {
+      case kTimeFormatNone:
+        PL_strncpy(fmtT, "", NSDATETIME_FORMAT_BUFFER_LEN); 
+        break;
+      case kTimeFormatSeconds:
+        PL_strncpy(fmtT, "%X", NSDATETIME_FORMAT_BUFFER_LEN);
+        break;
+      case kTimeFormatNoSeconds:
+        PL_strncpy(fmtT, 
+                   mLocalePreferred24hour ? "%H:%M" : mLocaleAMPMfirst ? "%p %I:%M" : "%I:%M %p", 
+                   NSDATETIME_FORMAT_BUFFER_LEN);
+        break;
+      case kTimeFormatSecondsForce24Hour:
+        PL_strncpy(fmtT, "%H:%M:%S", NSDATETIME_FORMAT_BUFFER_LEN);
+        break;
+      case kTimeFormatNoSecondsForce24Hour:
+        PL_strncpy(fmtT, "%H:%M", NSDATETIME_FORMAT_BUFFER_LEN);
+        break;
+      default:
+        PL_strncpy(fmtT, "", NSDATETIME_FORMAT_BUFFER_LEN); 
+    }
+  }
+
+  // generate data/time string
+  char *old_locale = setlocale(LC_TIME, nullptr);
+  (void) setlocale(LC_TIME, mPlatformLocale.get());
+  if (strlen(fmtD) && strlen(fmtT)) {
+    PL_strncat(fmtD, " ", NSDATETIME_FORMAT_BUFFER_LEN);
+    PL_strncat(fmtD, fmtT, NSDATETIME_FORMAT_BUFFER_LEN);
+    strftime(strOut, NSDATETIME_FORMAT_BUFFER_LEN, fmtD, tmTime);
+  }
+  else if (strlen(fmtD) && !strlen(fmtT)) {
+    strftime(strOut, NSDATETIME_FORMAT_BUFFER_LEN, fmtD, tmTime);
+  }
+  else if (!strlen(fmtD) && strlen(fmtT)) {
+    strftime(strOut, NSDATETIME_FORMAT_BUFFER_LEN, fmtT, tmTime);
+  }
+  else {
+    PL_strncpy(strOut, "", NSDATETIME_FORMAT_BUFFER_LEN);
+  }
+  (void) setlocale(LC_TIME, old_locale);
+
+  // convert result to unicode
+  int32_t srcLength = (int32_t) strlen(strOut);
+  int32_t unicharLength = NSDATETIME_FORMAT_BUFFER_LEN*2;
+  char16_t unichars[NSDATETIME_FORMAT_BUFFER_LEN*2];   // buffer for date and time
+
+  rv = mDecoder->Convert(strOut, &srcLength, unichars, &unicharLength);
+  if (NS_FAILED(rv))
+    return rv;
+  stringOut.Assign(unichars, unicharLength);
+
+  return rv;
+}
+
+// performs a locale sensitive date formatting operation on the PRTime parameter
+nsresult nsDateTimeFormatUnix::FormatPRTime(nsILocale* locale, 
+                                           const nsDateFormatSelector  dateFormatSelector, 
+                                           const nsTimeFormatSelector timeFormatSelector, 
+                                           const PRTime  prTime, 
+                                           nsAString& stringOut)
+{
+  PRExplodedTime explodedTime;
+  PR_ExplodeTime(prTime, PR_LocalTimeParameters, &explodedTime);
+
+  return FormatPRExplodedTime(locale, dateFormatSelector, timeFormatSelector, &explodedTime, stringOut);
+}
+
+// performs a locale sensitive date formatting operation on the PRExplodedTime parameter
+nsresult nsDateTimeFormatUnix::FormatPRExplodedTime(nsILocale* locale, 
+                                                   const nsDateFormatSelector  dateFormatSelector, 
+                                                   const nsTimeFormatSelector timeFormatSelector, 
+                                                   const PRExplodedTime*  explodedTime, 
+                                                   nsAString& stringOut)
+{
+  struct tm  tmTime;
+  /* be safe and set all members of struct tm to zero
+   *
+   * there are other fields in the tm struct that we aren't setting
+   * (tm_isdst, tm_gmtoff, tm_zone, should we set these?) and since
+   * tmTime is on the stack, it may be filled with garbage, but
+   * the garbage may vary.  (this may explain why some saw bug #10412, and
+   * others did not.
+   *
+   * when tmTime is passed to strftime() with garbage bad things may happen. 
+   * see bug #10412
+   */
+  memset( &tmTime, 0, sizeof(tmTime) );
+
+  tmTime.tm_yday = explodedTime->tm_yday;
+  tmTime.tm_wday = explodedTime->tm_wday;
+  tmTime.tm_year = explodedTime->tm_year;
+  tmTime.tm_year -= 1900;
+  tmTime.tm_mon = explodedTime->tm_month;
+  tmTime.tm_mday = explodedTime->tm_mday;
+  tmTime.tm_hour = explodedTime->tm_hour;
+  tmTime.tm_min = explodedTime->tm_min;
+  tmTime.tm_sec = explodedTime->tm_sec;
+
+  return FormatTMTime(locale, dateFormatSelector, timeFormatSelector, &tmTime, stringOut);
+}
+
new file mode 100644
--- /dev/null
+++ b/intl/locale/unix/nsDateTimeFormatUnix.h
@@ -0,0 +1,70 @@
+
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef nsDateTimeFormatUnix_h__
+#define nsDateTimeFormatUnix_h__
+
+
+#include "nsCOMPtr.h"
+#include "nsIDateTimeFormat.h"
+#include "nsIUnicodeDecoder.h"
+
+#define kPlatformLocaleLength 64
+
+class nsDateTimeFormatUnix : public nsIDateTimeFormat {
+
+public: 
+  NS_DECL_THREADSAFE_ISUPPORTS 
+
+  // performs a locale sensitive date formatting operation on the time_t parameter
+  NS_IMETHOD FormatTime(nsILocale* locale, 
+                        const nsDateFormatSelector  dateFormatSelector, 
+                        const nsTimeFormatSelector timeFormatSelector, 
+                        const time_t  timetTime, 
+                        nsAString& stringOut) override;
+
+  // performs a locale sensitive date formatting operation on the struct tm parameter
+  NS_IMETHOD FormatTMTime(nsILocale* locale, 
+                        const nsDateFormatSelector  dateFormatSelector, 
+                        const nsTimeFormatSelector timeFormatSelector, 
+                        const struct tm*  tmTime, 
+                        nsAString& stringOut) override;
+
+  // performs a locale sensitive date formatting operation on the PRTime parameter
+  NS_IMETHOD FormatPRTime(nsILocale* locale, 
+                          const nsDateFormatSelector  dateFormatSelector, 
+                          const nsTimeFormatSelector timeFormatSelector, 
+                          const PRTime  prTime, 
+                          nsAString& stringOut) override;
+
+  // performs a locale sensitive date formatting operation on the PRExplodedTime parameter
+  NS_IMETHOD FormatPRExplodedTime(nsILocale* locale, 
+                                  const nsDateFormatSelector  dateFormatSelector, 
+                                  const nsTimeFormatSelector timeFormatSelector, 
+                                  const PRExplodedTime*  explodedTime, 
+                                  nsAString& stringOut) override;
+
+
+  nsDateTimeFormatUnix() {mLocale.Truncate();mAppLocale.Truncate();}
+
+private:
+  virtual ~nsDateTimeFormatUnix() {}
+
+  // init this interface to a specified locale
+  NS_IMETHOD Initialize(nsILocale* locale);
+
+  void LocalePreferred24hour();
+
+  nsString    mLocale;
+  nsString    mAppLocale;
+  nsCString   mCharset;        // in order to convert API result to unicode
+  nsCString   mPlatformLocale; // for setlocale
+  bool        mLocalePreferred24hour;                       // true if 24 hour format is preferred by current locale
+  bool        mLocaleAMPMfirst;                             // true if AM/PM string is preferred before the time
+  nsCOMPtr <nsIUnicodeDecoder>   mDecoder;
+};
+
+#endif  /* nsDateTimeFormatUnix_h__ */
--- a/intl/locale/windows/moz.build
+++ b/intl/locale/windows/moz.build
@@ -1,16 +1,17 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 SOURCES += [
     'nsCollationWin.cpp',
+    'nsDateTimeFormatWin.cpp',
     'nsWin32Locale.cpp',
     'nsWinCharset.cpp',
 ]
 
 FINAL_LIBRARY = 'xul'
 
 GENERATED_FILES = [
     'wincharset.properties.h',
new file mode 100644
--- /dev/null
+++ b/intl/locale/windows/nsDateTimeFormatWin.cpp
@@ -0,0 +1,253 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsDateTimeFormatWin.h"
+#include "nsIServiceManager.h"
+#include "nsIComponentManager.h"
+#include "nsILocaleService.h"
+#include "nsWin32Locale.h"
+#include "nsUnicharUtils.h"
+#include "nsCRT.h"
+#include "nsCOMPtr.h"
+
+
+#define NSDATETIMEFORMAT_BUFFER_LEN  80
+
+NS_IMPL_ISUPPORTS(nsDateTimeFormatWin, nsIDateTimeFormat)
+
+
+// init this interface to a specified locale
+nsresult nsDateTimeFormatWin::Initialize(nsILocale* locale)
+{
+  nsAutoString localeStr;
+  nsresult res = NS_OK;
+
+  // use cached info if match with stored locale
+  if (!locale) {
+    if (!mLocale.IsEmpty() && 
+        mLocale.Equals(mAppLocale, nsCaseInsensitiveStringComparator())) {
+      return NS_OK;
+    }
+  }
+  else {
+    res = locale->GetCategory(NS_LITERAL_STRING("NSILOCALE_TIME"), localeStr);
+    if (NS_SUCCEEDED(res) && !localeStr.IsEmpty()) {
+      if (!mLocale.IsEmpty() && 
+          mLocale.Equals(localeStr, nsCaseInsensitiveStringComparator())) {
+        return NS_OK;
+      }
+    }
+  }
+
+  // default LCID (en-US)
+  mLCID = 1033;
+
+  // get locale string, use app default if no locale specified
+  if (!locale) {
+    nsCOMPtr<nsILocaleService> localeService = 
+             do_GetService(NS_LOCALESERVICE_CONTRACTID);
+    if (localeService) {
+      nsCOMPtr<nsILocale> appLocale;
+      res = localeService->GetApplicationLocale(getter_AddRefs(appLocale));
+      if (NS_SUCCEEDED(res)) {
+        res = appLocale->GetCategory(NS_LITERAL_STRING("NSILOCALE_TIME"), 
+			             localeStr);
+        if (NS_SUCCEEDED(res) && !localeStr.IsEmpty()) {
+          mAppLocale.Assign(localeStr); // cache app locale name
+        }
+      }
+    }
+  }
+  else {
+    res = locale->GetCategory(NS_LITERAL_STRING("NSILOCALE_TIME"), localeStr);
+  }
+
+  // Get LCID and charset name from locale, if available
+  if (NS_SUCCEEDED(res) && !localeStr.IsEmpty()) {
+    mLocale.Assign(localeStr); // cache locale name
+    res = nsWin32Locale::GetPlatformLocale(mLocale, (LCID *) &mLCID);
+  }
+
+  return res;
+}
+
+// performs a locale sensitive date formatting operation on the time_t parameter
+nsresult nsDateTimeFormatWin::FormatTime(nsILocale* locale, 
+                                         const nsDateFormatSelector  dateFormatSelector, 
+                                         const nsTimeFormatSelector timeFormatSelector, 
+                                         const time_t  timetTime, 
+                                         nsAString& stringOut)
+{
+  return FormatTMTime(locale, dateFormatSelector, timeFormatSelector, localtime( &timetTime ), stringOut);
+}
+
+// performs a locale sensitive date formatting operation on the struct tm parameter
+nsresult nsDateTimeFormatWin::FormatTMTime(nsILocale* locale, 
+                                           const nsDateFormatSelector  dateFormatSelector, 
+                                           const nsTimeFormatSelector timeFormatSelector, 
+                                           const struct tm*  tmTime, 
+                                           nsAString& stringOut)
+{
+  SYSTEMTIME system_time;
+  DWORD dwFlags_Date = 0, dwFlags_Time = 0;
+  int dateLen, timeLen;
+  char16_t dateBuffer[NSDATETIMEFORMAT_BUFFER_LEN], timeBuffer[NSDATETIMEFORMAT_BUFFER_LEN];
+
+  // set up locale data
+  (void) Initialize(locale);
+
+  // Map tm to SYSTEMTIME
+  system_time.wYear = 1900 + tmTime->tm_year;
+  system_time.wMonth = tmTime->tm_mon + 1;
+  system_time.wDayOfWeek = tmTime->tm_wday;
+  system_time.wDay = tmTime->tm_mday;
+  system_time.wHour = tmTime->tm_hour;
+  system_time.wMinute = tmTime->tm_min;
+  system_time.wSecond = tmTime->tm_sec;
+  system_time.wMilliseconds = 0;
+
+  // Map to WinAPI date format
+  switch (dateFormatSelector) {
+  case kDateFormatLong:
+    dwFlags_Date = DATE_LONGDATE;
+    break;
+  case kDateFormatShort:
+    dwFlags_Date = DATE_SHORTDATE;
+    break;
+  case kDateFormatWeekday:
+    dwFlags_Date = 0;
+    break;
+  case kDateFormatYearMonth:
+    dwFlags_Date = 0;     // TODO:only availabe NT5
+    break;
+  }
+
+  // Map to WinAPI time format
+  switch (timeFormatSelector) {
+  case kTimeFormatSeconds:
+    dwFlags_Time = 0;
+    break;
+  case kTimeFormatNoSeconds:
+    dwFlags_Time = TIME_NOSECONDS;
+    break;
+  case kTimeFormatSecondsForce24Hour:
+    dwFlags_Time = TIME_FORCE24HOURFORMAT;
+    break;
+  case kTimeFormatNoSecondsForce24Hour:
+    dwFlags_Time = TIME_NOSECONDS + TIME_FORCE24HOURFORMAT;
+    break;
+  }
+
+  // Call GetDateFormatW
+  if (dateFormatSelector == kDateFormatNone) {
+    dateLen = 0;
+  }
+  else {
+    if (dateFormatSelector == kDateFormatYearMonth) {
+      dateLen = nsGetDateFormatW(0, &system_time, "yyyy/MM", 
+                                 dateBuffer, NSDATETIMEFORMAT_BUFFER_LEN);
+    }
+    else if (dateFormatSelector == kDateFormatWeekday) {
+      dateLen = nsGetDateFormatW(0, &system_time, "ddd", 
+                                 dateBuffer, NSDATETIMEFORMAT_BUFFER_LEN);
+    }
+    else {
+      dateLen = nsGetDateFormatW(dwFlags_Date, &system_time, nullptr,
+                                 dateBuffer, NSDATETIMEFORMAT_BUFFER_LEN);
+    }
+    if (dateLen != 0) {
+      dateLen--;  // Since the count includes the terminating null.
+    }
+  }
+
+  // Call GetTimeFormatW
+  if (timeFormatSelector == kTimeFormatNone) {
+    timeLen = 0;
+  }
+  else {
+    timeLen = nsGetTimeFormatW(dwFlags_Time, &system_time, nullptr, 
+                               timeBuffer, NSDATETIMEFORMAT_BUFFER_LEN);
+    if (timeLen != 0) {
+      timeLen--;  // Since the count includes the terminating null.
+    }
+  }
+
+  NS_ASSERTION(NSDATETIMEFORMAT_BUFFER_LEN >= (uint32_t) (dateLen + 1), "internal date buffer is not large enough");
+  NS_ASSERTION(NSDATETIMEFORMAT_BUFFER_LEN >= (uint32_t) (timeLen + 1), "internal time buffer is not large enough");
+
+  // Copy the result
+  stringOut.Truncate();
+  if (dateLen != 0 && timeLen != 0) {
+    stringOut.Assign(dateBuffer, dateLen);
+    stringOut.Append((char16_t *)(L" "), 1);
+    stringOut.Append(timeBuffer, timeLen);
+  }
+  else if (dateLen != 0 && timeLen == 0) {
+    stringOut.Assign(dateBuffer, dateLen);
+  }
+  else if (dateLen == 0 && timeLen != 0) {
+    stringOut.Assign(timeBuffer, timeLen);
+  }
+
+  return NS_OK;
+}
+
+// performs a locale sensitive date formatting operation on the PRTime parameter
+nsresult nsDateTimeFormatWin::FormatPRTime(nsILocale* locale, 
+                                           const nsDateFormatSelector  dateFormatSelector, 
+                                           const nsTimeFormatSelector timeFormatSelector, 
+                                           const PRTime  prTime, 
+                                           nsAString& stringOut)
+{
+  PRExplodedTime explodedTime;
+  PR_ExplodeTime(prTime, PR_LocalTimeParameters, &explodedTime);
+
+  return FormatPRExplodedTime(locale, dateFormatSelector, timeFormatSelector, &explodedTime, stringOut);
+}
+
+// performs a locale sensitive date formatting operation on the PRExplodedTime parameter
+nsresult nsDateTimeFormatWin::FormatPRExplodedTime(nsILocale* locale, 
+                                                   const nsDateFormatSelector  dateFormatSelector, 
+                                                   const nsTimeFormatSelector timeFormatSelector, 
+                                                   const PRExplodedTime*  explodedTime, 
+                                                   nsAString& stringOut)
+{
+  struct tm  tmTime;
+  memset( &tmTime, 0, sizeof(tmTime) );
+
+  tmTime.tm_yday = explodedTime->tm_yday;
+  tmTime.tm_wday = explodedTime->tm_wday;
+  tmTime.tm_year = explodedTime->tm_year;
+  tmTime.tm_year -= 1900;
+  tmTime.tm_mon = explodedTime->tm_month;
+  tmTime.tm_mday = explodedTime->tm_mday;
+  tmTime.tm_hour = explodedTime->tm_hour;
+  tmTime.tm_min = explodedTime->tm_min;
+  tmTime.tm_sec = explodedTime->tm_sec;
+
+  return FormatTMTime(locale, dateFormatSelector, timeFormatSelector, &tmTime, stringOut);
+}
+
+int nsDateTimeFormatWin::nsGetTimeFormatW(DWORD dwFlags, const SYSTEMTIME *lpTime,
+                                          const char* format, char16_t *timeStr, int cchTime)
+{
+  int len = 0;
+  len = GetTimeFormatW(mLCID, dwFlags, lpTime, 
+                       format ?
+                       NS_ConvertASCIItoUTF16(format).get() :
+                       nullptr,
+                       (LPWSTR) timeStr, cchTime);
+  return len;
+}
+
+int nsDateTimeFormatWin::nsGetDateFormatW(DWORD dwFlags, const SYSTEMTIME *lpDate,
+                                          const char* format, char16_t *dateStr, int cchDate)
+{
+  int len = 0;
+  len = GetDateFormatW(mLCID, dwFlags, lpDate, 
+                       format ? NS_ConvertASCIItoUTF16(format).get() : nullptr,
+                       (LPWSTR) dateStr, cchDate);
+  return len;
+}
new file mode 100644
--- /dev/null
+++ b/intl/locale/windows/nsDateTimeFormatWin.h
@@ -0,0 +1,71 @@
+
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef nsDateTimeFormatWin_h__
+#define nsDateTimeFormatWin_h__
+
+
+#include "nsIDateTimeFormat.h"
+#include <windows.h>
+
+
+// Locale sensitive date and time format interface
+// 
+class nsDateTimeFormatWin : public nsIDateTimeFormat {
+  virtual ~nsDateTimeFormatWin() {}
+
+public: 
+  NS_DECL_THREADSAFE_ISUPPORTS 
+
+  // performs a locale sensitive date formatting operation on the time_t parameter
+  NS_IMETHOD FormatTime(nsILocale* locale, 
+                        const nsDateFormatSelector  dateFormatSelector, 
+                        const nsTimeFormatSelector timeFormatSelector, 
+                        const time_t  timetTime, 
+                        nsAString& stringOut); 
+
+  // performs a locale sensitive date formatting operation on the struct tm parameter
+  NS_IMETHOD FormatTMTime(nsILocale* locale, 
+                          const nsDateFormatSelector  dateFormatSelector, 
+                          const nsTimeFormatSelector timeFormatSelector, 
+                          const struct tm*  tmTime, 
+                          nsAString& stringOut); 
+
+  // performs a locale sensitive date formatting operation on the PRTime parameter
+  NS_IMETHOD FormatPRTime(nsILocale* locale, 
+                          const nsDateFormatSelector  dateFormatSelector, 
+                          const nsTimeFormatSelector timeFormatSelector, 
+                          const PRTime  prTime, 
+                          nsAString& stringOut);
+
+  // performs a locale sensitive date formatting operation on the PRExplodedTime parameter
+  NS_IMETHOD FormatPRExplodedTime(nsILocale* locale, 
+                                  const nsDateFormatSelector  dateFormatSelector, 
+                                  const nsTimeFormatSelector timeFormatSelector, 
+                                  const PRExplodedTime*  explodedTime, 
+                                  nsAString& stringOut); 
+
+  nsDateTimeFormatWin() {mLocale.SetLength(0);mAppLocale.SetLength(0);}
+
+
+private:
+  // init this interface to a specified locale
+  NS_IMETHOD Initialize(nsILocale* locale);
+
+  // call GetTimeFormatW or TimeFormatA
+  int nsGetTimeFormatW(DWORD dwFlags, const SYSTEMTIME *lpTime,
+                    const char* format, char16_t *timeStr, int cchTime);
+
+  // call GetDateFormatW or GetDateFormatA
+  int nsGetDateFormatW(DWORD dwFlags, const SYSTEMTIME *lpDate,
+                       const char* format, char16_t *dateStr, int cchDate);
+
+  nsString    mLocale;
+  nsString    mAppLocale;
+  uint32_t    mLCID;             // Windows platform locale ID
+};
+
+#endif  /* nsDateTimeFormatWin_h__ */
--- a/js/public/CallArgs.h
+++ b/js/public/CallArgs.h
@@ -285,17 +285,17 @@ class MOZ_STACK_CLASS CallArgs : public 
     static CallArgs create(unsigned argc, Value* argv, bool constructing) {
         CallArgs args;
         args.clearUsedRval();
         args.argv_ = argv;
         args.argc_ = argc;
         args.constructing_ = constructing;
 #ifdef DEBUG
         for (unsigned i = 0; i < argc; ++i)
-            MOZ_ASSERT_IF(argv[i].isMarkable(), !GCThingIsMarkedGray(GCCellPtr(argv[i])));
+            MOZ_ASSERT_IF(argv[i].isGCThing(), !GCThingIsMarkedGray(GCCellPtr(argv[i])));
 #endif
         return args;
     }
 
   public:
     /*
      * Returns true if there are at least |required| arguments passed in. If
      * false, it reports an error message on the context.
--- a/js/public/Proxy.h
+++ b/js/public/Proxy.h
@@ -451,17 +451,17 @@ SetValueInProxy(Value* slot, const Value
 
 inline void
 SetProxyExtra(JSObject* obj, size_t n, const Value& extra)
 {
     MOZ_ASSERT(n < detail::PROXY_EXTRA_SLOTS);
     Value* vp = &detail::GetProxyDataLayout(obj)->values->extraSlots[n];
 
     // Trigger a barrier before writing the slot.
-    if (vp->isMarkable() || extra.isMarkable())
+    if (vp->isGCThing() || extra.isGCThing())
         SetValueInProxy(vp, extra);
     else
         *vp = extra;
 }
 
 inline bool
 IsScriptedProxy(const JSObject* obj)
 {
@@ -477,17 +477,17 @@ GetReservedOrProxyPrivateSlot(const JSOb
 }
 
 inline void
 SetReservedOrProxyPrivateSlot(JSObject* obj, size_t slot, const Value& value)
 {
     MOZ_ASSERT(slot == 0);
     MOZ_ASSERT(slot < JSCLASS_RESERVED_SLOTS(GetObjectClass(obj)) || IsProxy(obj));
     shadow::Object* sobj = reinterpret_cast<shadow::Object*>(obj);
-    if (sobj->slotRef(slot).isMarkable() || value.isMarkable())
+    if (sobj->slotRef(slot).isGCThing() || value.isGCThing())
         SetReservedOrProxyPrivateSlotWithBarrier(obj, slot, value);
     else
         sobj->slotRef(slot) = value;
 }
 
 class MOZ_STACK_CLASS ProxyOptions {
   protected:
     /* protected constructor for subclass */
--- a/js/public/TraceKind.h
+++ b/js/public/TraceKind.h
@@ -35,19 +35,21 @@ namespace JS {
 //
 // See Value::gcKind() and JSTraceCallback in Tracer.h for more details.
 enum class TraceKind
 {
     // These trace kinds have a publicly exposed, although opaque, C++ type.
     // Note: The order here is determined by our Value packing. Other users
     //       should sort alphabetically, for consistency.
     Object = 0x00,
-    String = 0x01,
-    Symbol = 0x02,
-    Script = 0x03,
+    String = 0x02,
+    Symbol = 0x03,
+
+    // 0x1 is not used for any GCThing Value tag, so we use it for Script.
+    Script = 0x01,
 
     // Shape details are exposed through JS_TraceShapeCycleCollectorChildren.
     Shape = 0x04,
 
     // ObjectGroup details are exposed through JS_TraceObjectGroupCycleCollectorChildren.
     ObjectGroup = 0x05,
 
     // The kind associated with a nullptr.
--- a/js/public/Value.h
+++ b/js/public/Value.h
@@ -46,22 +46,22 @@ namespace JS { class Value; }
 #endif
 
 /* Remember to propagate changes to the C defines below. */
 JS_ENUM_HEADER(JSValueType, uint8_t)
 {
     JSVAL_TYPE_DOUBLE              = 0x00,
     JSVAL_TYPE_INT32               = 0x01,
     JSVAL_TYPE_UNDEFINED           = 0x02,
-    JSVAL_TYPE_BOOLEAN             = 0x03,
-    JSVAL_TYPE_MAGIC               = 0x04,
-    JSVAL_TYPE_STRING              = 0x05,
-    JSVAL_TYPE_SYMBOL              = 0x06,
-    JSVAL_TYPE_PRIVATE_GCTHING     = 0x07,
-    JSVAL_TYPE_NULL                = 0x08,
+    JSVAL_TYPE_NULL                = 0x03,
+    JSVAL_TYPE_BOOLEAN             = 0x04,
+    JSVAL_TYPE_MAGIC               = 0x05,
+    JSVAL_TYPE_STRING              = 0x06,
+    JSVAL_TYPE_SYMBOL              = 0x07,
+    JSVAL_TYPE_PRIVATE_GCTHING     = 0x08,
     JSVAL_TYPE_OBJECT              = 0x0c,
 
     /* These never appear in a jsval; they are only provided as an out-of-band value. */
     JSVAL_TYPE_UNKNOWN             = 0x20,
     JSVAL_TYPE_MISSING             = 0x21
 } JS_ENUM_FOOTER(JSValueType);
 
 static_assert(sizeof(JSValueType) == 1,
@@ -70,58 +70,58 @@ static_assert(sizeof(JSValueType) == 1,
 #if defined(JS_NUNBOX32)
 
 /* Remember to propagate changes to the C defines below. */
 JS_ENUM_HEADER(JSValueTag, uint32_t)
 {
     JSVAL_TAG_CLEAR                = 0xFFFFFF80,
     JSVAL_TAG_INT32                = JSVAL_TAG_CLEAR | JSVAL_TYPE_INT32,
     JSVAL_TAG_UNDEFINED            = JSVAL_TAG_CLEAR | JSVAL_TYPE_UNDEFINED,
+    JSVAL_TAG_NULL                 = JSVAL_TAG_CLEAR | JSVAL_TYPE_NULL,
     JSVAL_TAG_STRING               = JSVAL_TAG_CLEAR | JSVAL_TYPE_STRING,
     JSVAL_TAG_SYMBOL               = JSVAL_TAG_CLEAR | JSVAL_TYPE_SYMBOL,
     JSVAL_TAG_BOOLEAN              = JSVAL_TAG_CLEAR | JSVAL_TYPE_BOOLEAN,
     JSVAL_TAG_MAGIC                = JSVAL_TAG_CLEAR | JSVAL_TYPE_MAGIC,
-    JSVAL_TAG_NULL                 = JSVAL_TAG_CLEAR | JSVAL_TYPE_NULL,
     JSVAL_TAG_OBJECT               = JSVAL_TAG_CLEAR | JSVAL_TYPE_OBJECT,
     JSVAL_TAG_PRIVATE_GCTHING      = JSVAL_TAG_CLEAR | JSVAL_TYPE_PRIVATE_GCTHING
 } JS_ENUM_FOOTER(JSValueTag);
 
 static_assert(sizeof(JSValueTag) == sizeof(uint32_t),
               "compiler typed enum support is apparently buggy");
 
 #elif defined(JS_PUNBOX64)
 
 /* Remember to propagate changes to the C defines below. */
 JS_ENUM_HEADER(JSValueTag, uint32_t)
 {
     JSVAL_TAG_MAX_DOUBLE           = 0x1FFF0,
     JSVAL_TAG_INT32                = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_INT32,
     JSVAL_TAG_UNDEFINED            = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_UNDEFINED,
+    JSVAL_TAG_NULL                 = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_NULL,
     JSVAL_TAG_STRING               = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_STRING,
     JSVAL_TAG_SYMBOL               = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_SYMBOL,
     JSVAL_TAG_BOOLEAN              = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_BOOLEAN,
     JSVAL_TAG_MAGIC                = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_MAGIC,
-    JSVAL_TAG_NULL                 = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_NULL,
     JSVAL_TAG_OBJECT               = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_OBJECT,
     JSVAL_TAG_PRIVATE_GCTHING      = JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_PRIVATE_GCTHING
 } JS_ENUM_FOOTER(JSValueTag);
 
 static_assert(sizeof(JSValueTag) == sizeof(uint32_t),
               "compiler typed enum support is apparently buggy");
 
 JS_ENUM_HEADER(JSValueShiftedTag, uint64_t)
 {
     JSVAL_SHIFTED_TAG_MAX_DOUBLE      = ((((uint64_t)JSVAL_TAG_MAX_DOUBLE)     << JSVAL_TAG_SHIFT) | 0xFFFFFFFF),
     JSVAL_SHIFTED_TAG_INT32           = (((uint64_t)JSVAL_TAG_INT32)           << JSVAL_TAG_SHIFT),
     JSVAL_SHIFTED_TAG_UNDEFINED       = (((uint64_t)JSVAL_TAG_UNDEFINED)       << JSVAL_TAG_SHIFT),
+    JSVAL_SHIFTED_TAG_NULL            = (((uint64_t)JSVAL_TAG_NULL)            << JSVAL_TAG_SHIFT),
     JSVAL_SHIFTED_TAG_STRING          = (((uint64_t)JSVAL_TAG_STRING)          << JSVAL_TAG_SHIFT),
     JSVAL_SHIFTED_TAG_SYMBOL          = (((uint64_t)JSVAL_TAG_SYMBOL)          << JSVAL_TAG_SHIFT),
     JSVAL_SHIFTED_TAG_BOOLEAN         = (((uint64_t)JSVAL_TAG_BOOLEAN)         << JSVAL_TAG_SHIFT),
     JSVAL_SHIFTED_TAG_MAGIC           = (((uint64_t)JSVAL_TAG_MAGIC)           << JSVAL_TAG_SHIFT),
-    JSVAL_SHIFTED_TAG_NULL            = (((uint64_t)JSVAL_TAG_NULL)            << JSVAL_TAG_SHIFT),
     JSVAL_SHIFTED_TAG_OBJECT          = (((uint64_t)JSVAL_TAG_OBJECT)          << JSVAL_TAG_SHIFT),
     JSVAL_SHIFTED_TAG_PRIVATE_GCTHING = (((uint64_t)JSVAL_TAG_PRIVATE_GCTHING) << JSVAL_TAG_SHIFT)
 } JS_ENUM_FOOTER(JSValueShiftedTag);
 
 static_assert(sizeof(JSValueShiftedTag) == sizeof(uint64_t),
               "compiler typed enum support is apparently buggy");
 
 #endif
@@ -135,34 +135,31 @@ static_assert(sizeof(JSValueShiftedTag) 
  */
 #undef JS_ENUM_HEADER
 #undef JS_ENUM_FOOTER
 
 #if defined(JS_NUNBOX32)
 
 #define JSVAL_TYPE_TO_TAG(type)      ((JSValueTag)(JSVAL_TAG_CLEAR | (type)))
 
-#define JSVAL_LOWER_INCL_TAG_OF_OBJ_OR_NULL_SET         JSVAL_TAG_NULL
 #define JSVAL_UPPER_EXCL_TAG_OF_PRIMITIVE_SET           JSVAL_TAG_OBJECT
 #define JSVAL_UPPER_INCL_TAG_OF_NUMBER_SET              JSVAL_TAG_INT32
 #define JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET             JSVAL_TAG_STRING
 
 #elif defined(JS_PUNBOX64)
 
 #define JSVAL_PAYLOAD_MASK           0x00007FFFFFFFFFFFLL
 #define JSVAL_TAG_MASK               0xFFFF800000000000LL
 #define JSVAL_TYPE_TO_TAG(type)      ((JSValueTag)(JSVAL_TAG_MAX_DOUBLE | (type)))
 #define JSVAL_TYPE_TO_SHIFTED_TAG(type) (((uint64_t)JSVAL_TYPE_TO_TAG(type)) << JSVAL_TAG_SHIFT)
 
-#define JSVAL_LOWER_INCL_TAG_OF_OBJ_OR_NULL_SET         JSVAL_TAG_NULL
 #define JSVAL_UPPER_EXCL_TAG_OF_PRIMITIVE_SET           JSVAL_TAG_OBJECT
 #define JSVAL_UPPER_INCL_TAG_OF_NUMBER_SET              JSVAL_TAG_INT32
 #define JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET             JSVAL_TAG_STRING
 
-#define JSVAL_LOWER_INCL_SHIFTED_TAG_OF_OBJ_OR_NULL_SET  JSVAL_SHIFTED_TAG_NULL
 #define JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_PRIMITIVE_SET    JSVAL_SHIFTED_TAG_OBJECT
 #define JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_NUMBER_SET       JSVAL_SHIFTED_TAG_UNDEFINED
 #define JSVAL_LOWER_INCL_SHIFTED_TAG_OF_GCTHING_SET      JSVAL_SHIFTED_TAG_STRING
 
 #endif /* JS_PUNBOX64 */
 
 typedef enum JSWhyMagic
 {
@@ -532,22 +529,17 @@ class MOZ_NON_PARAM alignas(8) Value
 #if defined(JS_NUNBOX32)
         return uint32_t(toTag()) < uint32_t(JSVAL_UPPER_EXCL_TAG_OF_PRIMITIVE_SET);
 #elif defined(JS_PUNBOX64)
         return data.asBits < JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_PRIMITIVE_SET;
 #endif
     }
 
     bool isObjectOrNull() const {
-        MOZ_ASSERT(uint32_t(toTag()) <= uint32_t(JSVAL_TAG_OBJECT));
-#if defined(JS_NUNBOX32)
-        return uint32_t(toTag()) >= uint32_t(JSVAL_LOWER_INCL_TAG_OF_OBJ_OR_NULL_SET);
-#elif defined(JS_PUNBOX64)
-        return data.asBits >= JSVAL_LOWER_INCL_SHIFTED_TAG_OF_OBJ_OR_NULL_SET;
-#endif
+        return isObject() || isNull();
     }
 
     bool isGCThing() const {
 #if defined(JS_NUNBOX32)
         /* gcc sometimes generates signed < without explicit casts. */
         return uint32_t(toTag()) >= uint32_t(JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET);
 #elif defined(JS_PUNBOX64)
         return data.asBits >= JSVAL_LOWER_INCL_SHIFTED_TAG_OF_GCTHING_SET;
@@ -570,22 +562,18 @@ class MOZ_NON_PARAM alignas(8) Value
         return toTag() == JSVAL_TAG_MAGIC;
     }
 
     bool isMagic(JSWhyMagic why) const {
         MOZ_ASSERT_IF(isMagic(), data.s.payload.why == why);
         return isMagic();
     }
 
-    bool isMarkable() const {
-        return isGCThing() && !isNull();
-    }
-
     JS::TraceKind traceKind() const {
-        MOZ_ASSERT(isMarkable());
+        MOZ_ASSERT(isGCThing());
         static_assert((JSVAL_TAG_STRING & 0x03) == size_t(JS::TraceKind::String),
                       "Value type tags must correspond with JS::TraceKinds.");
         static_assert((JSVAL_TAG_SYMBOL & 0x03) == size_t(JS::TraceKind::Symbol),
                       "Value type tags must correspond with JS::TraceKinds.");
         static_assert((JSVAL_TAG_OBJECT & 0x03) == size_t(JS::TraceKind::Object),
                       "Value type tags must correspond with JS::TraceKinds.");
         if (MOZ_UNLIKELY(isPrivateGCThing()))
             return JS::GCThingTraceKind(toGCThing());
@@ -679,21 +667,16 @@ class MOZ_NON_PARAM alignas(8) Value
         return data.s.payload.cell;
 #elif defined(JS_PUNBOX64)
         uint64_t ptrBits = data.asBits & JSVAL_PAYLOAD_MASK;
         MOZ_ASSERT((ptrBits & 0x7) == 0);
         return reinterpret_cast<js::gc::Cell*>(ptrBits);
 #endif
     }
 
-    js::gc::Cell* toMarkablePointer() const {
-        MOZ_ASSERT(isMarkable());
-        return toGCThing();
-    }
-
     GCCellPtr toGCCellPtr() const {
         return GCCellPtr(toGCThing(), traceKind());
     }
 
     bool toBoolean() const {
         MOZ_ASSERT(isBoolean());
 #if defined(JS_NUNBOX32)
         return bool(data.s.payload.boo);
@@ -755,19 +738,19 @@ class MOZ_NON_PARAM alignas(8) Value
     uint32_t toPrivateUint32() const {
         return uint32_t(toInt32());
     }
 
     /*
      * Private GC Thing API
      *
      * Non-JSObject, JSString, and JS::Symbol cells may be put into the 64-bit
-     * payload as private GC things. Such Values are considered isMarkable()
-     * and isGCThing(), and as such, automatically marked. Their traceKind()
-     * is gotten via their cells.
+     * payload as private GC things. Such Values are considered isGCThing(), and
+     * as such, automatically marked. Their traceKind() is gotten via their
+     * cells.
      */
 
     void setPrivateGCThing(js::gc::Cell* cell) {
         MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::String,
                    "Private GC thing Values must not be strings. Make a StringValue instead.");
         MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::Symbol,
                    "Private GC thing Values must not be symbols. Make a SymbolValue instead.");
         MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::Object,
@@ -975,17 +958,17 @@ IsOptimizedPlaceholderMagicValue(const V
         return true;
     }
     return false;
 }
 
 static MOZ_ALWAYS_INLINE void
 ExposeValueToActiveJS(const Value& v)
 {
-    if (v.isMarkable())
+    if (v.isGCThing())
         js::gc::ExposeGCThingToActiveJS(GCCellPtr(v));
 }
 
 /************************************************************************/
 
 static inline Value
 NullValue()
 {
@@ -1293,17 +1276,17 @@ struct GCPolicy<JS::Value>
 } // namespace JS
 
 namespace js {
 
 template <>
 struct BarrierMethods<JS::Value>
 {
     static gc::Cell* asGCThingOrNull(const JS::Value& v) {
-        return v.isMarkable() ? v.toGCThing() : nullptr;
+        return v.isGCThing() ? v.toGCThing() : nullptr;
     }
     static void postBarrier(JS::Value* v, const JS::Value& prev, const JS::Value& next) {
         JS::HeapValuePostBarrier(v, prev, next);
     }
     static void exposeToJS(const JS::Value& v) {
         JS::ExposeValueToActiveJS(v);
     }
 };
@@ -1333,19 +1316,18 @@ class ValueOperations
     bool isInt32() const { return value().isInt32(); }
     bool isInt32(int32_t i32) const { return value().isInt32(i32); }
     bool isDouble() const { return value().isDouble(); }
     bool isString() const { return value().isString(); }
     bool isSymbol() const { return value().isSymbol(); }
     bool isObject() const { return value().isObject(); }
     bool isMagic() const { return value().isMagic(); }
     bool isMagic(JSWhyMagic why) const { return value().isMagic(why); }
-    bool isMarkable() const { return value().isMarkable(); }
+    bool isGCThing() const { return value().isGCThing(); }
     bool isPrimitive() const { return value().isPrimitive(); }
-    bool isGCThing() const { return value().isGCThing(); }
 
     bool isNullOrUndefined() const { return value().isNullOrUndefined(); }
     bool isObjectOrNull() const { return value().isObjectOrNull(); }
 
     bool toBoolean() const { return value().toBoolean(); }
     double toNumber() const { return value().toNumber(); }
     int32_t toInt32() const { return value().toInt32(); }
     double toDouble() const { return value().toDouble(); }
@@ -1480,17 +1462,17 @@ DispatchTyped(F f, const JS::Value& val,
     if (val.isString())
         return f(val.toString(), mozilla::Forward<Args>(args)...);
     if (val.isObject())
         return f(&val.toObject(), mozilla::Forward<Args>(args)...);
     if (val.isSymbol())
         return f(val.toSymbol(), mozilla::Forward<Args>(args)...);
     if (MOZ_UNLIKELY(val.isPrivateGCThing()))
         return DispatchTyped(f, val.toGCCellPtr(), mozilla::Forward<Args>(args)...);
-    MOZ_ASSERT(!val.isMarkable());
+    MOZ_ASSERT(!val.isGCThing());
     return F::defaultValue(val);
 }
 
 template <class S> struct VoidDefaultAdaptor { static void defaultValue(const S&) {} };
 template <class S> struct IdentityDefaultAdaptor { static S defaultValue(const S& v) {return v;} };
 template <class S, bool v> struct BoolDefaultAdaptor { static bool defaultValue(const S&) { return v; } };
 
 } // namespace js
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -1039,17 +1039,17 @@ class HasChildTracer : public JS::Callba
 
 static bool
 HasChild(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     RootedValue parent(cx, args.get(0));
     RootedValue child(cx, args.get(1));
 
-    if (!parent.isMarkable() || !child.isMarkable()) {
+    if (!parent.isGCThing() || !child.isGCThing()) {
         args.rval().setBoolean(false);
         return true;
     }
 
     HasChildTracer trc(cx, child);
     TraceChildren(&trc, parent.toGCThing(), parent.traceKind());
     args.rval().setBoolean(trc.found());
     return true;
--- a/js/src/gc/Barrier.cpp
+++ b/js/src/gc/Barrier.cpp
@@ -55,17 +55,17 @@ void
 HeapSlot::assertPreconditionForWriteBarrierPost(NativeObject* obj, Kind kind, uint32_t slot,
                                                 const Value& target) const
 {
     if (kind == Slot)
         MOZ_ASSERT(obj->getSlotAddressUnchecked(slot)->get() == target);
     else
         MOZ_ASSERT(static_cast<HeapSlot*>(obj->getDenseElements() + slot)->get() == target);
 
-    MOZ_ASSERT_IF(target.isMarkable() && IsMarkedBlack(obj),
+    MOZ_ASSERT_IF(target.isGCThing() && IsMarkedBlack(obj),
                   !JS::GCThingIsMarkedGray(JS::GCCellPtr(target)));
 }
 
 bool
 CurrentThreadIsIonCompiling()
 {
     return TlsPerThreadData.get()->ionCompiling;
 }
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -268,17 +268,17 @@ template <typename S> struct PreBarrierF
 
 template <typename S> struct ReadBarrierFunctor : public VoidDefaultAdaptor<S> {
     template <typename T> void operator()(T* t);
 };
 
 template <>
 struct InternalBarrierMethods<Value>
 {
-    static bool isMarkable(const Value& v) { return v.isMarkable(); }
+    static bool isMarkable(const Value& v) { return v.isGCThing(); }
     static bool isMarkableTaggedPointer(const Value& v) { return isMarkable(v); }
 
     static void preBarrier(const Value& v) {
         DispatchTyped(PreBarrierFunctor<Value>(), v);
     }
 
     static void postBarrier(Value* vp, const Value& prev, const Value& next) {
         MOZ_ASSERT(!CurrentThreadIsIonCompiling());
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -312,17 +312,17 @@ ShouldTraceCrossCompartment(JSTracer* tr
         }
         return zone->isGCMarkingGray();
     }
 }
 
 static bool
 ShouldTraceCrossCompartment(JSTracer* trc, JSObject* src, const Value& val)
 {
-    return val.isMarkable() && ShouldTraceCrossCompartment(trc, src, (Cell*)val.toGCThing());
+    return val.isGCThing() && ShouldTraceCrossCompartment(trc, src, val.toGCThing());
 }
 
 static void
 AssertZoneIsMarking(Cell* thing)
 {
     MOZ_ASSERT(TenuredCell::fromPointer(thing)->zone()->isGCMarking());
 }
 
@@ -1599,17 +1599,17 @@ ObjectDenseElementsMayBeMarkable(NativeO
     static const uint32_t flagMask =
         TYPE_FLAG_STRING | TYPE_FLAG_SYMBOL | TYPE_FLAG_LAZYARGS | TYPE_FLAG_ANYOBJECT;
     bool mayBeMarkable = typeSet->hasAnyFlag(flagMask) || typeSet->getObjectCount() != 0;
 
 #ifdef DEBUG
     if (!mayBeMarkable) {
         const Value* elements = nobj->getDenseElementsAllowCopyOnWrite();
         for (unsigned i = 0; i < nobj->getDenseInitializedLength(); i++)
-            MOZ_ASSERT(!elements[i].isMarkable());
+            MOZ_ASSERT(!elements[i].isGCThing());
     }
 #endif
 
     return mayBeMarkable;
 }
 
 inline void
 GCMarker::processMarkStackTop(SliceBudget& budget)
--- a/js/src/gc/Marking.h
+++ b/js/src/gc/Marking.h
@@ -396,17 +396,17 @@ bool
 IsAboutToBeFinalized(ReadBarrieredBase<T>* thingp);
 
 bool
 IsAboutToBeFinalizedDuringSweep(TenuredCell& tenured);
 
 inline Cell*
 ToMarkable(const Value& v)
 {
-    if (v.isMarkable())
+    if (v.isGCThing())
         return (Cell*)v.toGCThing();
     return nullptr;
 }
 
 inline Cell*
 ToMarkable(Cell* cell)
 {
     return cell;
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -2400,90 +2400,16 @@ UpdateExistingSetPropCallStubs(ICSetProp
                     foundMatchingStub = true;
             }
         }
     }
 
     return foundMatchingStub;
 }
 
-// Attach an optimized stub for a GETGNAME/CALLGNAME slot-read op.
-static bool
-TryAttachGlobalNameValueStub(JSContext* cx, HandleScript script, jsbytecode* pc,
-                             ICGetName_Fallback* stub,
-                             Handle<LexicalEnvironmentObject*> globalLexical,
-                             HandlePropertyName name, bool* attached)
-{
-    MOZ_ASSERT(globalLexical->isGlobal());
-    MOZ_ASSERT(!*attached);
-
-    RootedId id(cx, NameToId(name));
-
-    // The property must be found, and it must be found as a normal data property.
-    RootedShape shape(cx, globalLexical->lookup(cx, id));
-    RootedNativeObject current(cx, globalLexical);
-    while (true) {
-        shape = current->lookup(cx, id);
-        if (shape)
-            break;
-        if (current == globalLexical) {
-            current = &globalLexical->global();
-        } else {
-            JSObject* proto = current->staticPrototype();
-            if (!proto || !proto->is<NativeObject>())
-                return true;
-            current = &proto->as<NativeObject>();
-        }
-    }
-
-    // Instantiate this global property, for use during Ion compilation.
-    if (IsIonEnabled(cx))
-        EnsureTrackPropertyTypes(cx, current, id);
-
-    if (shape->hasDefaultGetter() && shape->hasSlot()) {
-
-        // TODO: if there's a previous stub discard it, or just update its Shape + slot?
-
-        ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
-        ICStub* newStub;
-        if (current == globalLexical) {
-            MOZ_ASSERT(shape->slot() >= current->numFixedSlots());
-            uint32_t slot = shape->slot() - current->numFixedSlots();
-
-            JitSpew(JitSpew_BaselineIC, "  Generating GetName(GlobalName lexical) stub");
-            ICGetName_GlobalLexical::Compiler compiler(cx, monitorStub, slot);
-            newStub = compiler.getStub(compiler.getStubSpace(script));
-        } else {
-            bool isFixedSlot;
-            uint32_t offset;
-            GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset);
-
-            // Check the prototype chain from the global to the current
-            // prototype. Ignore the global lexical scope as it doesn' figure
-            // into the prototype chain. We guard on the global lexical
-            // scope's shape independently.
-            if (!IsCacheableGetPropReadSlot(&globalLexical->global(), current, shape))
-                return true;
-
-            JitSpew(JitSpew_BaselineIC, "  Generating GetName(GlobalName non-lexical) stub");
-            ICGetPropNativeCompiler compiler(cx, ICStub::GetName_Global,
-                                             ICStubCompiler::Engine::Baseline, monitorStub,
-                                             globalLexical, current, name, isFixedSlot, offset,
-                                             /* inputDefinitelyObject = */ true);
-            newStub = compiler.getStub(compiler.getStubSpace(script));
-        }
-        if (!newStub)
-            return false;
-
-        stub->addNewStub(newStub);
-        *attached = true;
-    }
-    return true;
-}
-
 // Attach an optimized stub for a GETGNAME/CALLGNAME getter op.
 static bool
 TryAttachGlobalNameAccessorStub(JSContext* cx, HandleScript script, jsbytecode* pc,
                                 ICGetName_Fallback* stub,
                                 Handle<LexicalEnvironmentObject*> globalLexical,
                                 HandlePropertyName name, bool* attached,
                                 bool* isTemporarilyUnoptimizable)
 {
@@ -2610,21 +2536,17 @@ DoGetNameFallback(JSContext* cx, Baselin
         return true;
 
     // Add a type monitor stub for the resulting value.
     if (!stub->addMonitorStubForValue(cx, &info, res))
         return false;
     if (attached)
         return true;
 
-    if (IsGlobalOp(JSOp(*pc)) && !script->hasNonSyntacticScope()) {
-        Handle<LexicalEnvironmentObject*> globalLexical = envChain.as<LexicalEnvironmentObject>();
-        if (!TryAttachGlobalNameValueStub(cx, script, pc, stub, globalLexical, name, &attached))
-            return false;
-    } else if (!JitOptions.disableCacheIR) {
+    if (!JitOptions.disableCacheIR) {
         ICStubEngine engine = ICStubEngine::Baseline;
         GetNameIRGenerator gen(cx, pc, script, envChain, name);
         if (gen.tryAttachStub()) {
             ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
                                                         engine, info.outerScript(cx), stub);
             if (newStub) {
                 JitSpew(JitSpew_BaselineIC, "  Attached CacheIR stub");
                 attached = true;
@@ -2652,42 +2574,16 @@ ICGetName_Fallback::Compiler::generateSt
 
     masm.push(R0.scratchReg());
     masm.push(ICStubReg);
     pushStubPayload(masm, R0.scratchReg());
 
     return tailCallVM(DoGetNameFallbackInfo, masm);
 }
 
-bool
-ICGetName_GlobalLexical::Compiler::generateStubCode(MacroAssembler& masm)
-{
-    MOZ_ASSERT(engine_ == Engine::Baseline);
-
-    Label failure;
-    Register obj = R0.scratchReg();
-    Register scratch = R1.scratchReg();
-
-    // There's no need to guard on the shape. Lexical bindings are
-    // non-configurable, and this stub cannot be shared across globals.
-
-    // Load dynamic slot.
-    masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), obj);
-    masm.load32(Address(ICStubReg, ICGetName_GlobalLexical::offsetOfSlot()), scratch);
-    masm.loadValue(BaseIndex(obj, scratch, TimesEight), R0);
-
-    // Enter type monitor IC to type-check result.
-    EmitEnterTypeMonitorIC(masm);
-
-    // Failure case - jump to next stub
-    masm.bind(&failure);
-    EmitStubGuardFailure(masm);
-    return true;
-}
-
 //
 // BindName_Fallback
 //
 
 static bool
 DoBindNameFallback(JSContext* cx, BaselineFrame* frame, ICBindName_Fallback* stub,
                    HandleObject envChain, MutableHandleValue res)
 {
@@ -6844,48 +6740,25 @@ ICInNativeDoesNotExistCompiler::ICInNati
     MOZ_ASSERT(protoChainDepth_ <= ICIn_NativeDoesNotExist::MAX_PROTO_CHAIN_DEPTH);
 }
 
 ICIn_Dense::ICIn_Dense(JitCode* stubCode, HandleShape shape)
   : ICStub(In_Dense, stubCode),
     shape_(shape)
 { }
 
-ICGetName_GlobalLexical::ICGetName_GlobalLexical(JitCode* stubCode, ICStub* firstMonitorStub,
-                                                 uint32_t slot)
-  : ICMonitoredStub(GetName_GlobalLexical, stubCode, firstMonitorStub),
-    slot_(slot)
-{ }
-
 
 ICGetIntrinsic_Constant::ICGetIntrinsic_Constant(JitCode* stubCode, const Value& value)
   : ICStub(GetIntrinsic_Constant, stubCode),
     value_(value)
 { }
 
 ICGetIntrinsic_Constant::~ICGetIntrinsic_Constant()
 { }
 
-ICGetName_Global::ICGetName_Global(JitCode* stubCode, ICStub* firstMonitorStub,
-                                   ReceiverGuard guard, uint32_t offset,
-                                   JSObject* holder, Shape* holderShape, Shape* globalShape)
-  : ICGetPropNativePrototypeStub(GetName_Global, stubCode, firstMonitorStub, guard, offset,
-                                 holder, holderShape),
-    globalShape_(globalShape)
-{ }
-
-/* static */ ICGetName_Global*
-ICGetName_Global::Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub,
-                        ICGetName_Global& other)
-{
-    return New<ICGetName_Global>(cx, space, other.jitCode(), firstMonitorStub,
-                                 other.receiverGuard(), other.offset(),
-                                 other.holder(), other.holderShape(), other.globalShape());
-}
-
 ICInstanceOf_Function::ICInstanceOf_Function(JitCode* stubCode, Shape* shape,
                                              JSObject* prototypeObj, uint32_t slot)
   : ICStub(InstanceOf_Function, stubCode),
     shape_(shape),
     prototypeObj_(prototypeObj),
     slot_(slot)
 { }
 
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -968,51 +968,16 @@ class ICGetName_Fallback : public ICMoni
             ICGetName_Fallback* stub = newStub<ICGetName_Fallback>(space, getStubCode());
             if (!stub || !stub->initMonitoringChain(cx, space, engine_))
                 return nullptr;
             return stub;
         }
     };
 };
 
-// Optimized lexical GETGNAME stub.
-class ICGetName_GlobalLexical : public ICMonitoredStub
-{
-    friend class ICStubSpace;
-
-  protected: // Protected to silence Clang warning.
-    uint32_t slot_;
-
-    ICGetName_GlobalLexical(JitCode* stubCode, ICStub* firstMonitorStub, uint32_t slot);
-
-  public:
-    static size_t offsetOfSlot() {
-        return offsetof(ICGetName_GlobalLexical, slot_);
-    }
-
-    class Compiler : public ICStubCompiler {
-        ICStub* firstMonitorStub_;
-        uint32_t slot_;
-
-      protected:
-        MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
-
-      public:
-        Compiler(JSContext* cx, ICStub* firstMonitorStub, uint32_t slot)
-          : ICStubCompiler(cx, ICStub::GetName_GlobalLexical, Engine::Baseline),
-            firstMonitorStub_(firstMonitorStub),
-            slot_(slot)
-        {}
-
-        ICStub* getStub(ICStubSpace* space) {
-            return newStub<ICGetName_GlobalLexical>(space, getStubCode(), firstMonitorStub_, slot_);
-        }
-    };
-};
-
 // BindName
 //      JSOP_BINDNAME
 class ICBindName_Fallback : public ICFallbackStub
 {
     friend class ICStubSpace;
 
     explicit ICBindName_Fallback(JitCode* stubCode)
       : ICFallbackStub(ICStub::BindName_Fallback, stubCode)
--- a/js/src/jit/BaselineICList.h
+++ b/js/src/jit/BaselineICList.h
@@ -57,18 +57,16 @@ namespace jit {
                                                  \
     _(In_Fallback)                               \
     _(In_Native)                                 \
     _(In_NativePrototype)                        \
     _(In_NativeDoesNotExist)                     \
     _(In_Dense)                                  \
                                                  \
     _(GetName_Fallback)                          \
-    _(GetName_GlobalLexical)                     \
-    _(GetName_Global)                            \
                                                  \
     _(BindName_Fallback)                         \
                                                  \
     _(GetIntrinsic_Fallback)                     \
     _(GetIntrinsic_Constant)                     \
                                                  \
     _(SetProp_Fallback)                          \
     _(SetProp_Native)                            \
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -1169,29 +1169,102 @@ GetNameIRGenerator::GetNameIRGenerator(J
 bool
 GetNameIRGenerator::tryAttachStub()
 {
     MOZ_ASSERT(cacheKind_ == CacheKind::GetName);
 
     AutoAssertNoPendingException aanpe(cx_);
 
     ObjOperandId envId(writer.setInputOperandId(0));
-    if (tryAttachEnvironmentName(envId))
+    RootedId id(cx_, NameToId(name_));
+
+    if (tryAttachGlobalNameValue(envId, id))
+        return true;
+    if (tryAttachEnvironmentName(envId, id))
         return true;
 
     return false;
 }
 
 bool
-GetNameIRGenerator::tryAttachEnvironmentName(ObjOperandId objId)
+GetNameIRGenerator::tryAttachGlobalNameValue(ObjOperandId objId, HandleId id)
+{
+    if (!IsGlobalOp(JSOp(*pc_)) || script_->hasNonSyntacticScope())
+        return false;
+
+    Handle<LexicalEnvironmentObject*> globalLexical = env_.as<LexicalEnvironmentObject>();
+    MOZ_ASSERT(globalLexical->isGlobal());
+
+    // The property must be found, and it must be found as a normal data property.
+    RootedShape shape(cx_);
+    RootedNativeObject current(cx_, globalLexical);
+    while (true) {
+        shape = current->lookup(cx_, id);
+        if (shape)
+            break;
+        if (current == globalLexical) {
+            current = &globalLexical->global();
+        } else {
+            // In the browser the global prototype chain should be immutable.
+            if (!current->staticPrototypeIsImmutable())
+                return false;
+            JSObject* proto = current->staticPrototype();
+            if (!proto || !proto->is<NativeObject>())
+                return false;
+            current = &proto->as<NativeObject>();
+        }
+    }
+
+    if (!shape->hasDefaultGetter() || !shape->hasSlot())
+        return false;
+
+    // Instantiate this global property, for use during Ion compilation.
+    if (IsIonEnabled(cx_))
+        EnsureTrackPropertyTypes(cx_, current, id);
+
+    if (current == globalLexical) {
+        // There is no need to guard on the shape. Lexical bindings are
+        // non-configurable, and this stub cannot be shared across globals.
+        size_t dynamicSlotOffset = current->dynamicSlotIndex(shape->slot()) * sizeof(Value);
+        writer.loadDynamicSlotResult(objId, dynamicSlotOffset);
+    } else {
+        // Check the prototype chain from the global to the current
+        // prototype. Ignore the global lexical scope as it doesn't figure
+        // into the prototype chain. We guard on the global lexical
+        // scope's shape independently.
+        if (!IsCacheableGetPropReadSlotForIonOrCacheIR(&globalLexical->global(), current, shape))
+            return false;
+
+        // Shape guard for global lexical.
+        writer.guardShape(objId, globalLexical->lastProperty());
+
+        // Guard on the shape of the GlobalObject.
+        ObjOperandId globalId = writer.loadEnclosingEnvironment(objId);
+        writer.guardShape(globalId, globalLexical->global().lastProperty());
+
+        ObjOperandId holderId = globalId;
+        if (current != &globalLexical->global()) {
+            // Shape guard holder.
+            holderId = writer.loadObject(current);
+            writer.guardShape(holderId, current->lastProperty());
+        }
+
+        EmitLoadSlotResult(writer, holderId, current, shape);
+    }
+
+    writer.typeMonitorResult();
+    return true;
+}
+
+bool
+GetNameIRGenerator::tryAttachEnvironmentName(ObjOperandId objId, HandleId id)
 {
     if (IsGlobalOp(JSOp(*pc_)) || script_->hasNonSyntacticScope())
         return false;
 
-    RootedId id(cx_, NameToId(name_));
     RootedObject env(cx_, env_);
     RootedShape shape(cx_);
     RootedNativeObject holder(cx_);
 
     while (env) {
         if (env->is<GlobalObject>()) {
             shape = env->as<GlobalObject>().lookup(cx_, id);
             if (shape)
--- a/js/src/jit/CacheIR.h
+++ b/js/src/jit/CacheIR.h
@@ -775,17 +775,18 @@ class MOZ_RAII GetPropIRGenerator : publ
 
 // GetPropIRGenerator generates CacheIR for a GetName IC.
 class MOZ_RAII GetNameIRGenerator : public IRGenerator
 {
     HandleScript script_;
     HandleObject env_;
     HandlePropertyName name_;
 
-    bool tryAttachEnvironmentName(ObjOperandId objId);
+    bool tryAttachGlobalNameValue(ObjOperandId objId, HandleId id);
+    bool tryAttachEnvironmentName(ObjOperandId objId, HandleId id);
 
   public:
     GetNameIRGenerator(JSContext* cx, jsbytecode* pc, HandleScript script,
                        HandleObject env, HandlePropertyName name);
 
     bool tryAttachStub();
 };
 
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -8645,18 +8645,18 @@ template <typename T>
 static void
 StoreUnboxedPointer(MacroAssembler& masm, T address, MIRType type, const LAllocation* value,
                     bool preBarrier)
 {
     if (preBarrier)
         masm.patchableCallPreBarrier(address, type);
     if (value->isConstant()) {
         Value v = value->toConstant()->toJSValue();
-        if (v.isMarkable()) {
-            masm.storePtr(ImmGCPtr(v.toMarkablePointer()), address);
+        if (v.isGCThing()) {
+            masm.storePtr(ImmGCPtr(v.toGCThing()), address);
         } else {
             MOZ_ASSERT(v.isNull());
             masm.storePtr(ImmWord(0), address);
         }
     } else {
         masm.storePtr(ToRegister(value), address);
     }
 }
--- a/js/src/jit/JitFrames.cpp
+++ b/js/src/jit/JitFrames.cpp
@@ -1994,17 +1994,17 @@ SnapshotIterator::writeAllocationValuePa
 void
 SnapshotIterator::traceAllocation(JSTracer* trc)
 {
     RValueAllocation alloc = readAllocation();
     if (!allocationReadable(alloc, RM_AlwaysDefault))
         return;
 
     Value v = allocationValue(alloc, RM_AlwaysDefault);
-    if (!v.isMarkable())
+    if (!v.isGCThing())
         return;
 
     Value copy = v;
     TraceRoot(trc, &v, "ion-typed-reg");
     if (v != copy) {
         MOZ_ASSERT(SameType(v, copy));
         writeAllocationValuePayload(alloc, v);
     }
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -2691,17 +2691,17 @@ LIRGenerator::visitMonitorTypes(MMonitor
 // Returns true iff |def| is a constant that's either not a GC thing or is not
 // allocated in the nursery.
 static bool
 IsNonNurseryConstant(MDefinition* def)
 {
     if (!def->isConstant())
         return false;
     Value v = def->toConstant()->toJSValue();
-    return !v.isMarkable() || !IsInsideNursery(v.toMarkablePointer());
+    return !v.isGCThing() || !IsInsideNursery(v.toGCThing());
 }
 
 void
 LIRGenerator::visitPostWriteBarrier(MPostWriteBarrier* ins)
 {
     MOZ_ASSERT(ins->object()->type() == MIRType::Object);
 
     // LPostWriteBarrier assumes that if it has a constant object then that
--- a/js/src/jit/SharedIC.cpp
+++ b/js/src/jit/SharedIC.cpp
@@ -342,24 +342,16 @@ ICStub::trace(JSTracer* trc)
         }
         break;
       }
       case ICStub::In_Dense: {
         ICIn_Dense* inStub = toIn_Dense();
         TraceEdge(trc, &inStub->shape(), "baseline-in-dense-shape");
         break;
       }
-      case ICStub::GetName_Global: {
-        ICGetName_Global* globalStub = toGetName_Global();
-        globalStub->receiverGuard().trace(trc);
-        TraceEdge(trc, &globalStub->holder(), "baseline-global-stub-holder");
-        TraceEdge(trc, &globalStub->holderShape(), "baseline-global-stub-holdershape");
-        TraceEdge(trc, &globalStub->globalShape(), "baseline-global-stub-globalshape");
-        break;
-      }
       case ICStub::GetIntrinsic_Constant: {
         ICGetIntrinsic_Constant* constantStub = toGetIntrinsic_Constant();
         TraceEdge(trc, &constantStub->value(), "baseline-getintrinsic-constant-value");
         break;
       }
       case ICStub::GetProp_CallNativeGlobal: {
         ICGetProp_CallNativeGlobal* callStub = toGetProp_CallNativeGlobal();
         callStub->receiverGuard().trace(trc);
@@ -2395,35 +2387,16 @@ void
 ICGetProp_Fallback::Compiler::postGenerateStubCode(MacroAssembler& masm, Handle<JitCode*> code)
 {
     if (engine_ == Engine::Baseline) {
         void* address = code->raw() + returnOffset_;
         cx->compartment()->jitCompartment()->initBaselineGetPropReturnAddr(address);
     }
 }
 
-ICGetPropNativeStub*
-ICGetPropNativeCompiler::getStub(ICStubSpace* space)
-{
-    ReceiverGuard guard(obj_);
-
-    switch (kind) {
-      case ICStub::GetName_Global: {
-        MOZ_ASSERT(obj_ != holder_);
-        Shape* holderShape = holder_->as<NativeObject>().lastProperty();
-        Shape* globalShape = obj_->as<LexicalEnvironmentObject>().global().lastProperty();
-        return newStub<ICGetName_Global>(space, getStubCode(), firstMonitorStub_, guard,
-                                         offset_, holder_, holderShape, globalShape);
-      }
-
-      default:
-        MOZ_CRASH("Bad stub kind");
-    }
-}
-
 void
 GuardReceiverObject(MacroAssembler& masm, ReceiverGuard guard,
                     Register object, Register scratch,
                     size_t receiverGuardOffset, Label* failure)
 {
     Address groupAddress(ICStubReg, receiverGuardOffset + HeapReceiverGuard::offsetOfGroup());
     Address shapeAddress(ICStubReg, receiverGuardOffset + HeapReceiverGuard::offsetOfShape());
     Address expandoAddress(object, UnboxedPlainObject::offsetOfExpando());
@@ -2465,80 +2438,16 @@ GuardGlobalObject(MacroAssembler& masm, 
         return;
     masm.extractObject(Address(globalLexicalReg, EnvironmentObject::offsetOfEnclosingEnvironment()),
                        holderReg);
     masm.loadPtr(Address(ICStubReg, globalShapeOffset), scratch);
     masm.branchTestObjShape(Assembler::NotEqual, holderReg, scratch, failure);
 }
 
 bool
-ICGetPropNativeCompiler::generateStubCode(MacroAssembler& masm)
-{
-    Label failure;
-    AllocatableGeneralRegisterSet regs(availableGeneralRegs(0));
-    Register objReg = InvalidReg;
-
-    if (inputDefinitelyObject_) {
-        objReg = R0.scratchReg();
-    } else {
-        regs.take(R0);
-        // Guard input is an object and unbox.
-        masm.branchTestObject(Assembler::NotEqual, R0, &failure);
-        objReg = masm.extractObject(R0, ExtractTemp0);
-    }
-    regs.takeUnchecked(objReg);
-
-    Register scratch = regs.takeAnyExcluding(ICTailCallReg);
-
-    // Shape/group guard.
-    GuardReceiverObject(masm, ReceiverGuard(obj_), objReg, scratch,
-                        ICGetPropNativeStub::offsetOfReceiverGuard(), &failure);
-
-    MOZ_ASSERT(obj_ != holder_);
-    MOZ_ASSERT(kind == ICStub::GetName_Global);
-
-    Register holderReg = regs.takeAny();
-
-    // If we are generating a non-lexical GETGNAME stub, we must also
-    // guard on the shape of the GlobalObject.
-    MOZ_ASSERT(obj_->is<LexicalEnvironmentObject>() &&
-               obj_->as<LexicalEnvironmentObject>().isGlobal());
-    GuardGlobalObject(masm, holder_, objReg, holderReg, scratch,
-                      ICGetName_Global::offsetOfGlobalShape(), &failure);
-
-    // Shape guard holder.
-    masm.loadPtr(Address(ICStubReg, ICGetName_Global::offsetOfHolder()),
-                 holderReg);
-    masm.loadPtr(Address(ICStubReg, ICGetName_Global::offsetOfHolderShape()),
-                 scratch);
-    masm.branchTestObjShape(Assembler::NotEqual, holderReg, scratch, &failure);
-
-    if (!isFixedSlot_) {
-        // Don't overwrite actual holderReg if we need to load a dynamic slots object.
-        // May need to preserve object for noSuchMethod check later.
-        Register nextHolder = regs.takeAny();
-        masm.loadPtr(Address(holderReg, NativeObject::offsetOfSlots()), nextHolder);
-        holderReg = nextHolder;
-    }
-
-    masm.load32(Address(ICStubReg, ICGetPropNativeStub::offsetOfOffset()), scratch);
-    BaseIndex result(holderReg, scratch, TimesOne);
-
-    masm.loadValue(result, R0);
-
-    // Enter type monitor IC to type-check result.
-    EmitEnterTypeMonitorIC(masm);
-
-    // Failure case - jump to next stub
-    masm.bind(&failure);
-    EmitStubGuardFailure(masm);
-    return true;
-}
-
-bool
 GetProtoShapes(JSObject* obj, size_t protoChainDepth, MutableHandle<ShapeVector> shapes)
 {
     JSObject* curProto = obj->staticPrototype();
     for (size_t i = 0; i < protoChainDepth; i++) {
         if (!shapes.append(curProto->as<NativeObject>().lastProperty()))
             return false;
         curProto = curProto->staticPrototype();
     }
@@ -2764,33 +2673,16 @@ BaselineScript::noteAccessedGetter(uint3
 {
     ICEntry& entry = icEntryFromPCOffset(pcOffset);
     ICFallbackStub* stub = entry.fallbackStub();
 
     if (stub->isGetProp_Fallback())
         stub->toGetProp_Fallback()->noteAccessedGetter();
 }
 
-ICGetPropNativeStub::ICGetPropNativeStub(ICStub::Kind kind, JitCode* stubCode,
-                                         ICStub* firstMonitorStub,
-                                         ReceiverGuard guard, uint32_t offset)
-  : ICMonitoredStub(kind, stubCode, firstMonitorStub),
-    receiverGuard_(guard),
-    offset_(offset)
-{ }
-
-ICGetPropNativePrototypeStub::ICGetPropNativePrototypeStub(ICStub::Kind kind, JitCode* stubCode,
-                                                           ICStub* firstMonitorStub,
-                                                           ReceiverGuard guard, uint32_t offset,
-                                                           JSObject* holder, Shape* holderShape)
-  : ICGetPropNativeStub(kind, stubCode, firstMonitorStub, guard, offset),
-    holder_(holder),
-    holderShape_(holderShape)
-{ }
-
 ICGetPropCallGetter::ICGetPropCallGetter(Kind kind, JitCode* stubCode, ICStub* firstMonitorStub,
                                          ReceiverGuard receiverGuard, JSObject* holder,
                                          Shape* holderShape, JSFunction* getter,
                                          uint32_t pcOffset)
   : ICMonitoredStub(kind, stubCode, firstMonitorStub),
     receiverGuard_(receiverGuard),
     holder_(holder),
     holderShape_(holderShape),
--- a/js/src/jit/SharedIC.h
+++ b/js/src/jit/SharedIC.h
@@ -2351,146 +2351,16 @@ class ICGetProp_Generic : public ICMonit
         {}
 
         ICStub* getStub(ICStubSpace* space) {
             return newStub<ICGetProp_Generic>(space, getStubCode(), firstMonitorStub_);
         }
     };
 };
 
-// Base class for native GetProp stubs.
-class ICGetPropNativeStub : public ICMonitoredStub
-{
-    // Object shape/group.
-    HeapReceiverGuard receiverGuard_;
-
-    // Fixed or dynamic slot offset.
-    uint32_t offset_;
-
-  protected:
-    ICGetPropNativeStub(ICStub::Kind kind, JitCode* stubCode, ICStub* firstMonitorStub,
-                        ReceiverGuard guard, uint32_t offset);
-
-  public:
-    HeapReceiverGuard& receiverGuard() {
-        return receiverGuard_;
-    }
-    uint32_t offset() const {
-        return offset_;
-    }
-
-    void notePreliminaryObject() {
-        extra_ = 1;
-    }
-    bool hasPreliminaryObject() const {
-        return extra_;
-    }
-
-    static size_t offsetOfReceiverGuard() {
-        return offsetof(ICGetPropNativeStub, receiverGuard_);
-    }
-    static size_t offsetOfOffset() {
-        return offsetof(ICGetPropNativeStub, offset_);
-    }
-};
-
-class ICGetPropNativePrototypeStub : public ICGetPropNativeStub
-{
-    // Holder and its shape.
-    GCPtrObject holder_;
-    GCPtrShape holderShape_;
-
-  protected:
-    ICGetPropNativePrototypeStub(ICStub::Kind kind, JitCode* stubCode, ICStub* firstMonitorStub,
-                                 ReceiverGuard guard, uint32_t offset, JSObject* holder,
-                                 Shape* holderShape);
-
-  public:
-    GCPtrObject& holder() {
-        return holder_;
-    }
-    GCPtrShape& holderShape() {
-        return holderShape_;
-    }
-    static size_t offsetOfHolder() {
-        return offsetof(ICGetPropNativePrototypeStub, holder_);
-    }
-    static size_t offsetOfHolderShape() {
-        return offsetof(ICGetPropNativePrototypeStub, holderShape_);
-    }
-};
-
-// Stub for accessing a non-lexical global name. Semantically, it is really a
-// getprop: the name is either on the GlobalObject or its prototype chain. We
-// teleport to the object that has the name, but we also need to guard on the
-// shape of the global object.
-//
-// The receiver object is the global lexical scope.
-class ICGetName_Global : public ICGetPropNativePrototypeStub
-{
-    friend class ICStubSpace;
-
-  protected:
-    GCPtrShape globalShape_;
-
-    ICGetName_Global(JitCode* stubCode, ICStub* firstMonitorStub, ReceiverGuard guard,
-                     uint32_t slot, JSObject* holder, Shape* holderShape, Shape* globalShape);
-
-  public:
-    static ICGetName_Global* Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub,
-                                   ICGetName_Global& other);
-
-    GCPtrShape& globalShape() {
-        return globalShape_;
-    }
-    static size_t offsetOfGlobalShape() {
-        return offsetof(ICGetName_Global, globalShape_);
-    }
-};
-
-// Compiler for native GetProp stubs.
-class ICGetPropNativeCompiler : public ICStubCompiler
-{
-    ICStub* firstMonitorStub_;
-    HandleObject obj_;
-    HandleObject holder_;
-    HandlePropertyName propName_;
-    bool isFixedSlot_;
-    uint32_t offset_;
-    bool inputDefinitelyObject_;
-
-    MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
-
-  protected:
-    virtual int32_t getKey() const {
-        return static_cast<int32_t>(engine_) |
-              (static_cast<int32_t>(kind) << 1) |
-              (static_cast<int32_t>(isFixedSlot_) << 17) |
-              (static_cast<int32_t>(inputDefinitelyObject_) << 18) |
-              (HeapReceiverGuard::keyBits(obj_) << 19);
-    }
-
-  public:
-    ICGetPropNativeCompiler(JSContext* cx, ICStub::Kind kind, ICStubCompiler::Engine engine,
-                            ICStub* firstMonitorStub, HandleObject obj, HandleObject holder,
-                            HandlePropertyName propName, bool isFixedSlot, uint32_t offset,
-                            bool inputDefinitelyObject = false)
-      : ICStubCompiler(cx, kind, engine),
-        firstMonitorStub_(firstMonitorStub),
-        obj_(obj),
-        holder_(holder),
-        propName_(propName),
-        isFixedSlot_(isFixedSlot),
-        offset_(offset),
-        inputDefinitelyObject_(inputDefinitelyObject)
-    {}
-
-    ICGetPropNativeStub* getStub(ICStubSpace* space);
-};
-
 static uint32_t
 SimpleTypeDescrKey(SimpleTypeDescr* descr)
 {
     if (descr->is<ScalarTypeDescr>())
         return uint32_t(descr->as<ScalarTypeDescr>().type()) << 1;
     return (uint32_t(descr->as<ReferenceTypeDescr>().type()) << 1) | 1;
 }
 
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -3281,18 +3281,18 @@ MacroAssemblerARMCompat::extractTag(cons
     ma_alu(address.base, lsl(address.index, address.scale), scratch, OpAdd, LeaveCC);
     return extractTag(Address(scratch, address.offset), scratch);
 }
 
 void
 MacroAssemblerARMCompat::moveValue(const Value& val, Register type, Register data)
 {
     ma_mov(Imm32(val.toNunboxTag()), type);
-    if (val.isMarkable())
-        ma_mov(ImmGCPtr(val.toMarkablePointer()), data);
+    if (val.isGCThing())
+        ma_mov(ImmGCPtr(val.toGCThing()), data);
     else
         ma_mov(Imm32(val.toNunboxPayload()), data);
 }
 
 void
 MacroAssemblerARMCompat::moveValue(const Value& val, const ValueOperand& dest)
 {
     moveValue(val, dest.typeReg(), dest.payloadReg());
@@ -3457,18 +3457,18 @@ MacroAssemblerARMCompat::popValue(ValueO
 }
 
 void
 MacroAssemblerARMCompat::storePayload(const Value& val, const Address& dest)
 {
     ScratchRegisterScope scratch(asMasm());
     SecondScratchRegisterScope scratch2(asMasm());
 
-    if (val.isMarkable())
-        ma_mov(ImmGCPtr(val.toMarkablePointer()), scratch);
+    if (val.isGCThing())
+        ma_mov(ImmGCPtr(val.toGCThing()), scratch);
     else
         ma_mov(Imm32(val.toNunboxPayload()), scratch);
     ma_str(scratch, ToPayload(dest), scratch2);
 }
 
 void
 MacroAssemblerARMCompat::storePayload(Register src, const Address& dest)
 {
@@ -3479,18 +3479,18 @@ MacroAssemblerARMCompat::storePayload(Re
 void
 MacroAssemblerARMCompat::storePayload(const Value& val, const BaseIndex& dest)
 {
     unsigned shift = ScaleToShift(dest.scale);
 
     ScratchRegisterScope scratch(asMasm());
     SecondScratchRegisterScope scratch2(asMasm());
 
-    if (val.isMarkable())
-        ma_mov(ImmGCPtr(val.toMarkablePointer()), scratch);
+    if (val.isGCThing())
+        ma_mov(ImmGCPtr(val.toGCThing()), scratch);
     else
         ma_mov(Imm32(val.toNunboxPayload()), scratch);
 
     // If NUNBOX32_PAYLOAD_OFFSET is not zero, the memory operand [base + index
     // << shift + imm] cannot be encoded into a single instruction, and cannot
     // be integrated into the as_dtr call.
     JS_STATIC_ASSERT(NUNBOX32_PAYLOAD_OFFSET == 0);
 
@@ -5346,18 +5346,18 @@ MacroAssembler::branchTestValue(Conditio
     // b.tag. If the payloads are equal, compare the tags. If the payloads are
     // not equal, short circuit true (NotEqual).
     //
     // If cand == Equal, branch when a.payload == b.payload && a.tag == b.tag.
     // If the payloads are equal, compare the tags. If the payloads are not
     // equal, short circuit false (NotEqual).
     ScratchRegisterScope scratch(*this);
 
-    if (rhs.isMarkable())
-        ma_cmp(lhs.payloadReg(), ImmGCPtr(rhs.toMarkablePointer()), scratch);
+    if (rhs.isGCThing())
+        ma_cmp(lhs.payloadReg(), ImmGCPtr(rhs.toGCThing()), scratch);
     else
         ma_cmp(lhs.payloadReg(), Imm32(rhs.toNunboxPayload()), scratch);
     ma_cmp(lhs.typeReg(), Imm32(rhs.toNunboxTag()), scratch, Equal);
     ma_b(label, cond);
 }
 
 // ========================================================================
 // Memory access primitives.
--- a/js/src/jit/arm/MacroAssembler-arm.h
+++ b/js/src/jit/arm/MacroAssembler-arm.h
@@ -910,18 +910,18 @@ class MacroAssemblerARMCompat : public M
         ma_str(scratch, Address(dest.base, dest.offset + NUNBOX32_TYPE_OFFSET), scratch2);
     }
     void storeValue(const Value& val, const Address& dest) {
         ScratchRegisterScope scratch(asMasm());
         SecondScratchRegisterScope scratch2(asMasm());
 
         ma_mov(Imm32(val.toNunboxTag()), scratch);
         ma_str(scratch, ToType(dest), scratch2);
-        if (val.isMarkable())
-            ma_mov(ImmGCPtr(val.toMarkablePointer()), scratch);
+        if (val.isGCThing())
+            ma_mov(ImmGCPtr(val.toGCThing()), scratch);
         else
             ma_mov(Imm32(val.toNunboxPayload()), scratch);
         ma_str(scratch, ToPayload(dest), scratch2);
     }
     void storeValue(const Value& val, BaseIndex dest) {
         ScratchRegisterScope scratch(asMasm());
         SecondScratchRegisterScope scratch2(asMasm());
 
@@ -939,25 +939,25 @@ class MacroAssemblerARMCompat : public M
             ma_mov(Imm32(val.toNunboxTag()), scratch2);
             ma_str(scratch2, DTRAddr(scratch, DtrOffImm(0)));
             // Restore scratch for the payload store.
             ma_alu(dest.base, lsl(dest.index, dest.scale), scratch, OpAdd);
         }
 
         // Store the payload, marking if necessary.
         if (payloadoffset < 4096 && payloadoffset > -4096) {
-            if (val.isMarkable())
-                ma_mov(ImmGCPtr(val.toMarkablePointer()), scratch2);
+            if (val.isGCThing())
+                ma_mov(ImmGCPtr(val.toGCThing()), scratch2);
             else
                 ma_mov(Imm32(val.toNunboxPayload()), scratch2);
             ma_str(scratch2, DTRAddr(scratch, DtrOffImm(payloadoffset)));
         } else {
             ma_add(Imm32(payloadoffset), scratch, scratch2);
-            if (val.isMarkable())
-                ma_mov(ImmGCPtr(val.toMarkablePointer()), scratch2);
+            if (val.isGCThing())
+                ma_mov(ImmGCPtr(val.toGCThing()), scratch2);
             else
                 ma_mov(Imm32(val.toNunboxPayload()), scratch2);
             ma_str(scratch2, DTRAddr(scratch, DtrOffImm(0)));
         }
     }
     void storeValue(const Address& src, const Address& dest, Register temp) {
         load32(ToType(src), temp);
         store32(temp, ToType(dest));
@@ -972,18 +972,18 @@ class MacroAssemblerARMCompat : public M
     }
     void loadValue(const BaseIndex& addr, ValueOperand val);
     void tagValue(JSValueType type, Register payload, ValueOperand dest);
 
     void pushValue(ValueOperand val);
     void popValue(ValueOperand val);
     void pushValue(const Value& val) {
         push(Imm32(val.toNunboxTag()));
-        if (val.isMarkable())
-            push(ImmGCPtr(val.toMarkablePointer()));
+        if (val.isGCThing())
+            push(ImmGCPtr(val.toGCThing()));
         else
             push(Imm32(val.toNunboxPayload()));
     }
     void pushValue(JSValueType type, Register reg) {
         push(ImmTag(JSVAL_TYPE_TO_TAG(type)));
         ma_push(reg);
     }
     void pushValue(const Address& addr);
--- a/js/src/jit/arm64/MacroAssembler-arm64.h
+++ b/js/src/jit/arm64/MacroAssembler-arm64.h
@@ -301,17 +301,17 @@ class MacroAssemblerCompat : public vixl
         vixl::MacroAssembler::Push(ARMRegister(val.valueReg(), 64));
     }
     void popValue(ValueOperand val) {
         vixl::MacroAssembler::Pop(ARMRegister(val.valueReg(), 64));
     }
     void pushValue(const Value& val) {
         vixl::UseScratchRegisterScope temps(this);
         const Register scratch = temps.AcquireX().asUnsized();
-        if (val.isMarkable()) {
+        if (val.isGCThing()) {
             BufferOffset load = movePatchablePtr(ImmPtr(val.bitsAsPunboxPointer()), scratch);
             writeDataRelocation(val, load);
             push(scratch);
         } else {
             moveValue(val, scratch);
             push(scratch);
         }
     }
@@ -344,17 +344,17 @@ class MacroAssemblerCompat : public vixl
             return;
           case 1:
             store8(value.valueReg(), address);
             return;
           default: MOZ_CRASH("Bad payload width");
         }
     }
     void moveValue(const Value& val, Register dest) {
-        if (val.isMarkable()) {
+        if (val.isGCThing()) {
             BufferOffset load = movePatchablePtr(ImmPtr(val.bitsAsPunboxPointer()), dest);
             writeDataRelocation(val, load);
         } else {
             movePtr(ImmWord(val.asRawBits()), dest);
         }
     }
     void moveValue(const Value& src, const ValueOperand& dest) {
         moveValue(src, dest.valueReg());
@@ -1830,18 +1830,18 @@ class MacroAssemblerCompat : public vixl
     }
 
     // load: offset to the load instruction obtained by movePatchablePtr().
     void writeDataRelocation(ImmGCPtr ptr, BufferOffset load) {
         if (ptr.value)
             dataRelocations_.writeUnsigned(load.getOffset());
     }
     void writeDataRelocation(const Value& val, BufferOffset load) {
-        if (val.isMarkable()) {
-            gc::Cell* cell = val.toMarkablePointer();
+        if (val.isGCThing()) {
+            gc::Cell* cell = val.toGCThing();
             if (cell && gc::IsInsideNursery(cell))
                 embedsNurseryPointers_ = true;
             dataRelocations_.writeUnsigned(load.getOffset());
         }
     }
 
     void writePrebarrierOffset(CodeOffset label) {
         preBarriers_.writeUnsigned(label.offset());
--- a/js/src/jit/mips-shared/Assembler-mips-shared.cpp
+++ b/js/src/jit/mips-shared/Assembler-mips-shared.cpp
@@ -1739,8 +1739,17 @@ AssemblerMIPSShared::ToggleToCmp(CodeLoc
     // toggledJump is allways used for short jumps.
     MOZ_ASSERT(inst->extractOpcode() == ((uint32_t)op_beq >> OpcodeShift));
     // Replace "beq $zero, $zero, offset" with "andi $zero, $zero, offset"
     inst->setOpcode(op_andi);
 
     AutoFlushICache::flush(uintptr_t(inst), 4);
 }
 
+void
+AssemblerMIPSShared::UpdateLuiOriValue(Instruction* inst0, Instruction* inst1, uint32_t value)
+{
+    MOZ_ASSERT(inst0->extractOpcode() == ((uint32_t)op_lui >> OpcodeShift));
+    MOZ_ASSERT(inst1->extractOpcode() == ((uint32_t)op_ori >> OpcodeShift));
+
+    ((InstImm*) inst0)->setImm16(Imm16::Upper(Imm32(value)));
+    ((InstImm*) inst1)->setImm16(Imm16::Lower(Imm32(value)));
+}
--- a/js/src/jit/mips-shared/Assembler-mips-shared.h
+++ b/js/src/jit/mips-shared/Assembler-mips-shared.h
@@ -1268,16 +1268,18 @@ class AssemblerMIPSShared : public Assem
         return (offset + 1U) &~ 1U;
     }
 
     static uint8_t* NextInstruction(uint8_t* instruction, uint32_t* count = nullptr);
 
     static void ToggleToJmp(CodeLocationLabel inst_);
     static void ToggleToCmp(CodeLocationLabel inst_);
 
+    static void UpdateLuiOriValue(Instruction* inst0, Instruction* inst1, uint32_t value);
+
     void processCodeLabels(uint8_t* rawCode);
 
     bool bailed() {
         return m_buffer.bail();
     }
 
     void verifyHeapAccessDisassembly(uint32_t begin, uint32_t end,
                                      const Disassembler::HeapAccess& heapAccess)
--- a/js/src/jit/mips-shared/MacroAssembler-mips-shared.cpp
+++ b/js/src/jit/mips-shared/MacroAssembler-mips-shared.cpp
@@ -34,16 +34,27 @@ MacroAssemblerMIPSShared::ma_li(Register
     } else if (Imm16::Lower(imm).encode() == 0) {
         as_lui(dest, Imm16::Upper(imm).encode());
     } else {
         as_lui(dest, Imm16::Upper(imm).encode());
         as_ori(dest, dest, Imm16::Lower(imm).encode());
     }
 }
 
+// This method generates lui and ori instruction pair that can be modified by
+// UpdateLuiOriValue, either during compilation (eg. Assembler::bind), or
+// during execution (eg. jit::PatchJump).
+void
+MacroAssemblerMIPSShared::ma_liPatchable(Register dest, Imm32 imm)
+{
+    m_buffer.ensureSpace(2 * sizeof(uint32_t));
+    as_lui(dest, Imm16::Upper(imm).encode());
+    as_ori(dest, dest, Imm16::Lower(imm).encode());
+}
+
 // Shifts
 void
 MacroAssemblerMIPSShared::ma_sll(Register rd, Register rt, Imm32 shift)
 {
     as_sll(rd, rt, shift.value % 32);
 }
 void
 MacroAssemblerMIPSShared::ma_srl(Register rd, Register rt, Imm32 shift)
--- a/js/src/jit/mips-shared/MacroAssembler-mips-shared.h
+++ b/js/src/jit/mips-shared/MacroAssembler-mips-shared.h
@@ -60,16 +60,17 @@ class MacroAssemblerMIPSShared : public 
                               FPConditionBit fcc = FCC0);
 
   public:
     void ma_move(Register rd, Register rs);
 
     void ma_li(Register dest, ImmGCPtr ptr);
 
     void ma_li(Register dest, Imm32 imm);
+    void ma_liPatchable(Register dest, Imm32 imm);
 
     // Shift operations
     void ma_sll(Register rd, Register rt, Imm32 shift);
     void ma_srl(Register rd, Register rt, Imm32 shift);
     void ma_sra(Register rd, Register rt, Imm32 shift);
     void ma_ror(Register rd, Register rt, Imm32 shift);
     void ma_rol(Register rd, Register rt, Imm32 shift);
 
@@ -179,16 +180,22 @@ class MacroAssemblerMIPSShared : public 
 
     void ma_jump(ImmPtr dest);
 
     void ma_cmp_set(Register dst, Register lhs, Register rhs, Condition c);
     void ma_cmp_set(Register dst, Register lhs, Imm32 imm, Condition c);
     void ma_cmp_set_double(Register dst, FloatRegister lhs, FloatRegister rhs, DoubleCondition c);
     void ma_cmp_set_float32(Register dst, FloatRegister lhs, FloatRegister rhs, DoubleCondition c);
 
+    BufferOffset ma_BoundsCheck(Register bounded) {
+        BufferOffset bo = m_buffer.nextOffset();
+        ma_liPatchable(bounded, Imm32(0));
+        return bo;
+    }
+
     void moveToDoubleLo(Register src, FloatRegister dest) {
         as_mtc1(src, dest);
     }
     void moveFromDoubleLo(FloatRegister src, Register dest) {
         as_mfc1(dest, src);
     }
 
     void moveToFloat32(Register src, FloatRegister dest) {
--- a/js/src/jit/mips32/Assembler-mips32.cpp
+++ b/js/src/jit/mips32/Assembler-mips32.cpp
@@ -119,17 +119,17 @@ js::jit::SA(FloatRegister r)
 // Used to patch jumps created by MacroAssemblerMIPSCompat::jumpWithPatch.
 void
 jit::PatchJump(CodeLocationJump& jump_, CodeLocationLabel label, ReprotectCode reprotect)
 {
     Instruction* inst1 = (Instruction*)jump_.raw();
     Instruction* inst2 = inst1->next();
 
     MaybeAutoWritableJitCode awjc(inst1, 8, reprotect);
-    Assembler::UpdateLuiOriValue(inst1, inst2, (uint32_t)label.raw());
+    AssemblerMIPSShared::UpdateLuiOriValue(inst1, inst2, (uint32_t)label.raw());
 
     AutoFlushICache::flush(uintptr_t(inst1), 8);
 }
 
 // For more infromation about backedges look at comment in
 // MacroAssemblerMIPSCompat::backedgeJump()
 void
 jit::PatchBackedge(CodeLocationJump& jump, CodeLocationLabel label,
@@ -141,39 +141,39 @@ jit::PatchBackedge(CodeLocationJump& jum
 
     MOZ_ASSERT(branch->extractOpcode() == (uint32_t(op_beq) >> OpcodeShift));
 
     if (BOffImm16::IsInRange(targetAddr - sourceAddr)) {
         branch->setBOffImm16(BOffImm16(targetAddr - sourceAddr));
     } else {
         if (target == JitRuntime::BackedgeLoopHeader) {
             Instruction* lui = &branch[1];
-            Assembler::UpdateLuiOriValue(lui, lui->next(), targetAddr);
+            AssemblerMIPSShared::UpdateLuiOriValue(lui, lui->next(), targetAddr);
             // Jump to ori. The lui will be executed in delay slot.
             branch->setBOffImm16(BOffImm16(2 * sizeof(uint32_t)));
         } else {
             Instruction* lui = &branch[4];
-            Assembler::UpdateLuiOriValue(lui, lui->next(), targetAddr);
+            AssemblerMIPSShared::UpdateLuiOriValue(lui, lui->next(), targetAddr);
             branch->setBOffImm16(BOffImm16(4 * sizeof(uint32_t)));
         }
     }
 }
 
 void
 Assembler::executableCopy(uint8_t* buffer)
 {
     MOZ_ASSERT(isFinished);
     m_buffer.executableCopy(buffer);
 
     // Patch all long jumps during code copy.
     for (size_t i = 0; i < longJumps_.length(); i++) {
         Instruction* inst1 = (Instruction*) ((uint32_t)buffer + longJumps_[i]);
 
         uint32_t value = Assembler::ExtractLuiOriValue(inst1, inst1->next());
-        Assembler::UpdateLuiOriValue(inst1, inst1->next(), (uint32_t)buffer + value);
+        AssemblerMIPSShared::UpdateLuiOriValue(inst1, inst1->next(), (uint32_t)buffer + value);
     }
 
     AutoFlushICache::setRange(uintptr_t(buffer), m_buffer.size());
 }
 
 uintptr_t
 Assembler::GetPointer(uint8_t* instPtr)
 {
@@ -202,17 +202,17 @@ TraceOneDataRelocation(JSTracer* trc, In
 {
     void* ptr = (void*)Assembler::ExtractLuiOriValue(inst, inst->next());
     void* prior = ptr;
 
     // No barrier needed since these are constants.
     TraceManuallyBarrieredGenericPointerEdge(trc, reinterpret_cast<gc::Cell**>(&ptr),
                                                  "ion-masm-ptr");
     if (ptr != prior) {
-        Assembler::UpdateLuiOriValue(inst, inst->next(), uint32_t(ptr));
+        AssemblerMIPSShared::UpdateLuiOriValue(inst, inst->next(), uint32_t(ptr));
         AutoFlushICache::flush(uintptr_t(inst), 8);
     }
 }
 
 static void
 TraceDataRelocations(JSTracer* trc, uint8_t* buffer, CompactBufferReader& reader)
 {
     while (reader.more()) {
@@ -301,17 +301,17 @@ Assembler::trace(JSTracer* trc)
 }
 
 void
 Assembler::Bind(uint8_t* rawCode, CodeOffset* label, const void* address)
 {
     if (label->bound()) {
         intptr_t offset = label->offset();
         Instruction* inst = (Instruction*) (rawCode + offset);
-        Assembler::UpdateLuiOriValue(inst, inst->next(), (uint32_t)address);
+        AssemblerMIPSShared::UpdateLuiOriValue(inst, inst->next(), (uint32_t)address);
     }
 }
 
 void
 Assembler::bind(InstImm* inst, uintptr_t branch, uintptr_t target)
 {
     int32_t offset = target - branch;
     InstImm inst_bgezal = InstImm(op_regimm, zero, rt_bgezal, BOffImm16(0));
@@ -380,17 +380,17 @@ Assembler::bind(RepatchLabel* label)
         uint32_t offset = dest.getOffset() - label->offset();
 
         // If first instruction is lui, then this is a long jump.
         // If second instruction is lui, then this is a loop backedge.
         if (inst[0].extractOpcode() == (uint32_t(op_lui) >> OpcodeShift)) {
             // For unconditional long branches generated by ma_liPatchable,
             // such as under:
             //     jumpWithpatch
-            Assembler::UpdateLuiOriValue(inst, inst->next(), dest.getOffset());
+            AssemblerMIPSShared::UpdateLuiOriValue(inst, inst->next(), dest.getOffset());
         } else if (inst[1].extractOpcode() == (uint32_t(op_lui) >> OpcodeShift) ||
                    BOffImm16::IsInRange(offset))
         {
             // Handle code produced by:
             //     backedgeJump
             //     branchWithCode
             MOZ_ASSERT(BOffImm16::IsInRange(offset));
             MOZ_ASSERT(inst[0].extractOpcode() == (uint32_t(op_beq) >> OpcodeShift) ||
@@ -462,26 +462,16 @@ Assembler::ExtractLuiOriValue(Instructio
     MOZ_ASSERT(i1->extractOpcode() == ((uint32_t)op_ori >> OpcodeShift));
 
     uint32_t value = i0->extractImm16Value() << 16;
     value = value | i1->extractImm16Value();
     return value;
 }
 
 void
-Assembler::UpdateLuiOriValue(Instruction* inst0, Instruction* inst1, uint32_t value)
-{
-    MOZ_ASSERT(inst0->extractOpcode() == ((uint32_t)op_lui >> OpcodeShift));
-    MOZ_ASSERT(inst1->extractOpcode() == ((uint32_t)op_ori >> OpcodeShift));
-
-    ((InstImm*) inst0)->setImm16(Imm16::Upper(Imm32(value)));
-    ((InstImm*) inst1)->setImm16(Imm16::Lower(Imm32(value)));
-}
-
-void
 Assembler::WriteLuiOriInstructions(Instruction* inst0, Instruction* inst1,
                                    Register reg, uint32_t value)
 {
     *inst0 = InstImm(op_lui, zero, reg, Imm16::Upper(Imm32(value)));
     *inst1 = InstImm(op_ori, reg, reg, Imm16::Lower(Imm32(value)));
 }
 
 void
@@ -498,26 +488,26 @@ Assembler::PatchDataWithValueCheck(CodeL
 {
     Instruction* inst = (Instruction*) label.raw();
 
     // Extract old Value
     DebugOnly<uint32_t> value = Assembler::ExtractLuiOriValue(&inst[0], &inst[1]);
     MOZ_ASSERT(value == uint32_t(expectedValue.value));
 
     // Replace with new value
-    Assembler::UpdateLuiOriValue(inst, inst->next(), uint32_t(newValue.value));
+    AssemblerMIPSShared::UpdateLuiOriValue(inst, inst->next(), uint32_t(newValue.value));
 
     AutoFlushICache::flush(uintptr_t(inst), 8);
 }
 
 void
 Assembler::PatchInstructionImmediate(uint8_t* code, PatchedImmPtr imm)
 {
     InstImm* inst = (InstImm*)code;
-    Assembler::UpdateLuiOriValue(inst, inst->next(), (uint32_t)imm.value);
+    AssemblerMIPSShared::UpdateLuiOriValue(inst, inst->next(), (uint32_t)imm.value);
 }
 
 uint32_t
 Assembler::ExtractInstructionImmediate(uint8_t* code)
 {
     InstImm* inst = (InstImm*)code;
     return Assembler::ExtractLuiOriValue(inst, inst->next());
 }
--- a/js/src/jit/mips32/Assembler-mips32.h
+++ b/js/src/jit/mips32/Assembler-mips32.h
@@ -156,17 +156,16 @@ class Assembler : public AssemblerMIPSSh
 
     // Copy the assembly code to the given buffer, and perform any pending
     // relocations relying on the target address.
     void executableCopy(uint8_t* buffer);
 
     static uint32_t PatchWrite_NearCallSize();
 
     static uint32_t ExtractLuiOriValue(Instruction* inst0, Instruction* inst1);
-    static void UpdateLuiOriValue(Instruction* inst0, Instruction* inst1, uint32_t value);
     static void WriteLuiOriInstructions(Instruction* inst, Instruction* inst1,
                                         Register reg, uint32_t value);
 
     static void PatchWrite_NearCall(CodeLocationLabel start, CodeLocationLabel toCall);
     static void PatchDataWithValueCheck(CodeLocationLabel label, ImmPtr newValue,
                                         ImmPtr expectedValue);
     static void PatchDataWithValueCheck(CodeLocationLabel label, PatchedImmPtr newValue,
                                         PatchedImmPtr expectedValue);
--- a/js/src/jit/mips32/MacroAssembler-mips32-inl.h
+++ b/js/src/jit/mips32/MacroAssembler-mips32-inl.h
@@ -1037,17 +1037,17 @@ MacroAssembler::wasmBoundsCheck(Conditio
 void
 MacroAssembler::wasmPatchBoundsCheck(uint8_t* patchAt, uint32_t limit)
 {
     Instruction* inst = (Instruction*) patchAt;
     InstImm* i0 = (InstImm*) inst;
     InstImm* i1 = (InstImm*) i0->next();
 
     // Replace with new value
-    Assembler::UpdateLuiOriValue(i0, i1, limit);
+    AssemblerMIPSShared::UpdateLuiOriValue(i0, i1, limit);
 }
 
 //}}} check_macroassembler_style
 // ===============================================================
 
 void
 MacroAssemblerMIPSCompat::incrementInt32Value(const Address& addr)
 {
--- a/js/src/jit/mips32/MacroAssembler-mips32.cpp
+++ b/js/src/jit/mips32/MacroAssembler-mips32.cpp
@@ -197,27 +197,16 @@ MacroAssemblerMIPS::ma_li(Register dest,
 }
 
 void
 MacroAssemblerMIPS::ma_li(Register dest, ImmWord imm)
 {
     ma_li(dest, Imm32(uint32_t(imm.value)));
 }
 
-// This method generates lui and ori instruction pair that can be modified by
-// UpdateLuiOriValue, either during compilation (eg. Assembler::bind), or
-// during execution (eg. jit::PatchJump).
-void
-MacroAssemblerMIPS::ma_liPatchable(Register dest, Imm32 imm)
-{
-    m_buffer.ensureSpace(2 * sizeof(uint32_t));
-    as_lui(dest, Imm16::Upper(imm).encode());
-    as_ori(dest, dest, Imm16::Lower(imm).encode());
-}
-
 void
 MacroAssemblerMIPS::ma_liPatchable(Register dest, ImmPtr imm)
 {
     ma_liPatchable(dest, ImmWord(uintptr_t(imm.value)));
 }
 
 void
 MacroAssemblerMIPS::ma_liPatchable(Register dest, ImmWord imm)
@@ -1522,18 +1511,18 @@ uint32_t
 MacroAssemblerMIPSCompat::getType(const Value& val)
 {
     return val.toNunboxTag();
 }
 
 void
 MacroAssemblerMIPSCompat::moveData(const Value& val, Register data)
 {
-    if (val.isMarkable())
-        ma_li(data, ImmGCPtr(val.toMarkablePointer()));
+    if (val.isGCThing())
+        ma_li(data, ImmGCPtr(val.toGCThing()));
     else
         ma_li(data, Imm32(val.toNunboxPayload()));
 }
 
 void
 MacroAssemblerMIPSCompat::moveValue(const Value& val, Register type, Register data)
 {
     MOZ_ASSERT(type != data);
--- a/js/src/jit/mips32/MacroAssembler-mips32.h
+++ b/js/src/jit/mips32/MacroAssembler-mips32.h
@@ -50,17 +50,16 @@ class MacroAssemblerMIPS : public MacroA
     using MacroAssemblerMIPSShared::ma_sd;
     using MacroAssemblerMIPSShared::ma_load;
     using MacroAssemblerMIPSShared::ma_store;
     using MacroAssemblerMIPSShared::ma_cmp_set;
     using MacroAssemblerMIPSShared::ma_subTestOverflow;
 
     void ma_li(Register dest, CodeOffset* label);
 
-    void ma_liPatchable(Register dest, Imm32 imm);
     void ma_li(Register dest, ImmWord imm);
     void ma_liPatchable(Register dest, ImmPtr imm);
     void ma_liPatchable(Register dest, ImmWord imm);
 
     // load
     void ma_load(Register dest, Address address, LoadStoreSize size = SizeWord,
                  LoadStoreExtension extension = SignExtend);
 
@@ -475,18 +474,18 @@ class MacroAssemblerMIPSCompat : public 
     }
     void loadValue(const BaseIndex& addr, ValueOperand val);
     void tagValue(JSValueType type, Register payload, ValueOperand dest);
 
     void pushValue(ValueOperand val);
     void popValue(ValueOperand val);
     void pushValue(const Value& val) {
         push(Imm32(val.toNunboxTag()));
-        if (val.isMarkable())
-            push(ImmGCPtr(val.toMarkablePointer()));
+        if (val.isGCThing())
+            push(ImmGCPtr(val.toGCThing()));
         else
             push(Imm32(val.toNunboxPayload()));
     }
     void pushValue(JSValueType type, Register reg) {
         push(ImmTag(JSVAL_TYPE_TO_TAG(type)));
         ma_push(reg);
     }
     void pushValue(const Address& addr);
@@ -985,22 +984,16 @@ class MacroAssemblerMIPSCompat : public 
         as_jr(ra);
         as_nop();
     }
 
     void ma_storeImm(Imm32 imm, const Address& addr) {
         ma_sw(imm, addr);
     }
 
-    BufferOffset ma_BoundsCheck(Register bounded) {
-        BufferOffset bo = m_buffer.nextOffset();
-        ma_liPatchable(bounded, ImmWord(0));
-        return bo;
-    }
-
     void moveFloat32(FloatRegister src, FloatRegister dest) {
         as_movs(dest, src);
     }
     void loadWasmGlobalPtr(uint32_t globalDataOffset, Register dest) {
         loadPtr(Address(GlobalReg, globalDataOffset - WasmGlobalRegBias), dest);
     }
     void loadWasmPinnedRegsFromTls() {
         loadPtr(Address(WasmTlsReg, offsetof(wasm::TlsData, memoryBase)), HeapReg);
--- a/js/src/jit/mips64/MacroAssembler-mips64-inl.h
+++ b/js/src/jit/mips64/MacroAssembler-mips64-inl.h
@@ -701,18 +701,22 @@ MacroAssembler::wasmBoundsCheck(Conditio
     append(wasm::BoundsCheck(bo.getOffset()));
 
     ma_b(index, ScratchRegister, label, cond);
 }
 
 void
 MacroAssembler::wasmPatchBoundsCheck(uint8_t* patchAt, uint32_t limit)
 {
+    Instruction* inst = (Instruction*) patchAt;
+    InstImm* i0 = (InstImm*) inst;
+    InstImm* i1 = (InstImm*) i0->next();
+
     // Replace with new value
-    Assembler::UpdateLoad64Value((Instruction*) patchAt, limit);
+    AssemblerMIPSShared::UpdateLuiOriValue(i0, i1, limit);
 }
 
 //}}} check_macroassembler_style
 // ===============================================================
 
 // The specializations for cmpPtrSet are outside the braces because check_macroassembler_style can't yet
 // deal with specializations.
 
--- a/js/src/jit/mips64/MacroAssembler-mips64.cpp
+++ b/js/src/jit/mips64/MacroAssembler-mips64.cpp
@@ -1889,17 +1889,17 @@ MacroAssemblerMIPS64Compat::storeValue(J
     ma_dsll(SecondScratchReg, SecondScratchReg, Imm32(JSVAL_TAG_SHIFT));
     ma_dins(SecondScratchReg, reg, Imm32(0), Imm32(JSVAL_TAG_SHIFT));
     storePtr(SecondScratchReg, Address(dest.base, dest.offset));
 }
 
 void
 MacroAssemblerMIPS64Compat::storeValue(const Value& val, Address dest)
 {
-    if (val.isMarkable()) {
+    if (val.isGCThing()) {
         writeDataRelocation(val);
         movWithPatch(ImmWord(val.asRawBits()), SecondScratchReg);
     } else {
         ma_li(SecondScratchReg, ImmWord(val.asRawBits()));
     }
     storePtr(SecondScratchReg, Address(dest.base, dest.offset));
 }
 
--- a/js/src/jit/mips64/MacroAssembler-mips64.h
+++ b/js/src/jit/mips64/MacroAssembler-mips64.h
@@ -216,18 +216,18 @@ class MacroAssemblerMIPS64Compat : publi
     void mov(Register src, Address dest) {
         MOZ_CRASH("NYI-IC");
     }
     void mov(Address src, Register dest) {
         MOZ_CRASH("NYI-IC");
     }
 
     void writeDataRelocation(const Value& val) {
-        if (val.isMarkable()) {
-            gc::Cell* cell = val.toMarkablePointer();
+        if (val.isGCThing()) {
+            gc::Cell* cell = val.toGCThing();
             if (cell && gc::IsInsideNursery(cell))
                 embedsNurseryPointers_ = true;
             dataRelocations_.writeUnsigned(currentOffset());
         }
     }
 
     void branch(JitCode* c) {
         BufferOffset bo = m_buffer.nextOffset();
@@ -493,17 +493,17 @@ class MacroAssemblerMIPS64Compat : publi
         loadValue(dest.toAddress(), val);
     }
     void loadValue(const BaseIndex& addr, ValueOperand val);
     void tagValue(JSValueType type, Register payload, ValueOperand dest);
 
     void pushValue(ValueOperand val);
     void popValue(ValueOperand val);
     void pushValue(const Value& val) {
-        if (val.isMarkable()) {
+        if (val.isGCThing()) {
             writeDataRelocation(val);
             movWithPatch(ImmWord(val.asRawBits()), ScratchRegister);
             push(ScratchRegister);
         } else {
             push(ImmWord(val.asRawBits()));
         }
     }
     void pushValue(JSValueType type, Register reg) {
@@ -1004,22 +1004,16 @@ class MacroAssemblerMIPS64Compat : publi
         ma_daddu(dest, addr.baseReg(), Imm32(addr.disp()));
     }
 
     void abiret() {
         as_jr(ra);
         as_nop();
     }
 
-    BufferOffset ma_BoundsCheck(Register bounded) {
-        BufferOffset bo = m_buffer.nextOffset();
-        ma_liPatchable(bounded, ImmWord(0));
-        return bo;
-    }
-
     void moveFloat32(FloatRegister src, FloatRegister dest) {
         as_movs(dest, src);
     }
 
     void loadWasmGlobalPtr(uint32_t globalDataOffset, Register dest) {
         loadPtr(Address(GlobalReg, globalDataOffset - WasmGlobalRegBias), dest);
     }
     void loadWasmPinnedRegsFromTls() {
--- a/js/src/jit/x64/MacroAssembler-x64.h
+++ b/js/src/jit/x64/MacroAssembler-x64.h
@@ -53,18 +53,18 @@ class MacroAssemblerX64 : public MacroAs
     // The buffer is about to be linked, make sure any constant pools or excess
     // bookkeeping has been flushed to the instruction stream.
     void finish();
 
     /////////////////////////////////////////////////////////////////
     // X64 helpers.
     /////////////////////////////////////////////////////////////////
     void writeDataRelocation(const Value& val) {
-        if (val.isMarkable()) {
-            gc::Cell* cell = val.toMarkablePointer();
+        if (val.isGCThing()) {
+            gc::Cell* cell = val.toGCThing();
             if (cell && gc::IsInsideNursery(cell))
                 embedsNurseryPointers_ = true;
             dataRelocations_.writeUnsigned(masm.currentOffset());
         }
     }
 
     // Refers to the upper 32 bits of a 64-bit Value operand.
     // On x86_64, the upper 32 bits do not necessarily only contain the type.
@@ -127,17 +127,17 @@ class MacroAssemblerX64 : public MacroAs
             ScratchRegisterScope scratch(asMasm());
             boxValue(type, reg, scratch);
             movq(scratch, Operand(dest));
         }
     }
     template <typename T>
     void storeValue(const Value& val, const T& dest) {
         ScratchRegisterScope scratch(asMasm());
-        if (val.isMarkable()) {
+        if (val.isGCThing()) {
             movWithPatch(ImmWord(val.asRawBits()), scratch);
             writeDataRelocation(val);
         } else {
             mov(ImmWord(val.asRawBits()), scratch);
         }
         movq(scratch, Operand(dest));
     }
     void storeValue(ValueOperand val, BaseIndex dest) {
@@ -166,17 +166,17 @@ class MacroAssemblerX64 : public MacroAs
     }
     void pushValue(ValueOperand val) {
         push(val.valueReg());
     }
     void popValue(ValueOperand val) {
         pop(val.valueReg());
     }
     void pushValue(const Value& val) {
-        if (val.isMarkable()) {
+        if (val.isGCThing()) {
             ScratchRegisterScope scratch(asMasm());
             movWithPatch(ImmWord(val.asRawBits()), scratch);
             writeDataRelocation(val);
             push(scratch);
         } else {
             push(ImmWord(val.asRawBits()));
         }
     }
--- a/js/src/jit/x86/MacroAssembler-x86.cpp
+++ b/js/src/jit/x86/MacroAssembler-x86.cpp
@@ -520,18 +520,18 @@ MacroAssembler::branchValueIsNurseryObje
     bind(&done);
 }
 
 void
 MacroAssembler::branchTestValue(Condition cond, const ValueOperand& lhs,
                                 const Value& rhs, Label* label)
 {
     MOZ_ASSERT(cond == Equal || cond == NotEqual);
-    if (rhs.isMarkable())
-        cmpPtr(lhs.payloadReg(), ImmGCPtr(rhs.toMarkablePointer()));
+    if (rhs.isGCThing())
+        cmpPtr(lhs.payloadReg(), ImmGCPtr(rhs.toGCThing()));
     else
         cmpPtr(lhs.payloadReg(), ImmWord(rhs.toNunboxPayload()));
 
     if (cond == Equal) {
         Label done;
         j(NotEqual, &done);
         {
             cmp32(lhs.typeReg(), Imm32(rhs.toNunboxTag()));
--- a/js/src/jit/x86/MacroAssembler-x86.h
+++ b/js/src/jit/x86/MacroAssembler-x86.h
@@ -89,18 +89,18 @@ class MacroAssemblerX86 : public MacroAs
             MOZ_CRASH("unexpected operand kind");
         }
     }
     Address ToType(Address base) {
         return ToType(Operand(base)).toAddress();
     }
     void moveValue(const Value& val, Register type, Register data) {
         movl(Imm32(val.toNunboxTag()), type);
-        if (val.isMarkable())
-            movl(ImmGCPtr(val.toMarkablePointer()), data);
+        if (val.isGCThing())
+            movl(ImmGCPtr(val.toGCThing()), data);
         else
             movl(Imm32(val.toNunboxPayload()), data);
     }
     void moveValue(const Value& val, const ValueOperand& dest) {
         moveValue(val, dest.typeReg(), dest.payloadReg());
     }
     void moveValue(const ValueOperand& src, const ValueOperand& dest) {
         Register s0 = src.typeReg(), d0 = dest.typeReg(),
@@ -208,18 +208,18 @@ class MacroAssemblerX86 : public MacroAs
         push(val.payloadReg());
     }
     void popValue(ValueOperand val) {
         pop(val.payloadReg());
         pop(val.typeReg());
     }
     void pushValue(const Value& val) {
         push(Imm32(val.toNunboxTag()));
-        if (val.isMarkable())
-            push(ImmGCPtr(val.toMarkablePointer()));
+        if (val.isGCThing())
+            push(ImmGCPtr(val.toGCThing()));
         else
             push(Imm32(val.toNunboxPayload()));
     }
     void pushValue(JSValueType type, Register reg) {
         push(ImmTag(JSVAL_TYPE_TO_TAG(type)));
         push(reg);
     }
     void pushValue(const Address& addr) {
@@ -230,18 +230,18 @@ class MacroAssemblerX86 : public MacroAs
         push(src.high);
         push(src.low);
     }
     void pop64(Register64 dest) {
         pop(dest.low);
         pop(dest.high);
     }
     void storePayload(const Value& val, Operand dest) {
-        if (val.isMarkable())
-            movl(ImmGCPtr(val.toMarkablePointer()), ToPayload(dest));
+        if (val.isGCThing())
+            movl(ImmGCPtr(val.toGCThing()), ToPayload(dest));
         else
             movl(Imm32(val.toNunboxPayload()), ToPayload(dest));
     }
     void storePayload(Register src, Operand dest) {
         movl(src, ToPayload(dest));
     }
     void storeTypeTag(ImmTag tag, Operand dest) {
         movl(tag, ToType(dest));
--- a/js/src/jscompartmentinlines.h
+++ b/js/src/jscompartmentinlines.h
@@ -56,17 +56,17 @@ js::AutoCompartment::~AutoCompartment()
 {
     cx_->leaveCompartment(origin_, maybeLock_);
 }
 
 inline bool
 JSCompartment::wrap(JSContext* cx, JS::MutableHandleValue vp)
 {
     /* Only GC things have to be wrapped or copied. */
-    if (!vp.isMarkable())
+    if (!vp.isGCThing())
         return true;
 
     /*
      * Symbols are GC things, but never need to be wrapped or copied because
      * they are always allocated in the atoms compartment.
      */
     if (vp.isSymbol())
         return true;
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -756,17 +756,17 @@ GetReservedSlot(JSObject* obj, size_t sl
 JS_FRIEND_API(void)
 SetReservedOrProxyPrivateSlotWithBarrier(JSObject* obj, size_t slot, const JS::Value& value);
 
 inline void
 SetReservedSlot(JSObject* obj, size_t slot, const JS::Value& value)
 {
     MOZ_ASSERT(slot < JSCLASS_RESERVED_SLOTS(GetObjectClass(obj)));
     shadow::Object* sobj = reinterpret_cast<shadow::Object*>(obj);
-    if (sobj->slotRef(slot).isMarkable() || value.isMarkable())
+    if (sobj->slotRef(slot).isGCThing() || value.isGCThing())
         SetReservedOrProxyPrivateSlotWithBarrier(obj, slot, value);
     else
         sobj->slotRef(slot) = value;
 }
 
 JS_FRIEND_API(uint32_t)
 GetObjectSlotSpan(JSObject* obj);
 
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -813,17 +813,17 @@ JSFunction::initExtendedSlot(size_t whic
     MOZ_ASSERT(which < mozilla::ArrayLength(toExtended()->extendedSlots));
     toExtended()->extendedSlots[which].init(val);
 }
 
 inline void
 JSFunction::setExtendedSlot(size_t which, const js::Value& val)
 {
     MOZ_ASSERT(which < mozilla::ArrayLength(toExtended()->extendedSlots));
-    MOZ_ASSERT_IF(js::IsMarkedBlack(this) && val.isMarkable(),
+    MOZ_ASSERT_IF(js::IsMarkedBlack(this) && val.isGCThing(),
                   !JS::GCThingIsMarkedGray(JS::GCCellPtr(val)));
     toExtended()->extendedSlots[which] = val;
 }
 
 inline const js::Value&
 JSFunction::getExtendedSlot(size_t which) const
 {
     MOZ_ASSERT(which < mozilla::ArrayLength(toExtended()->extendedSlots));
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -3297,17 +3297,17 @@ js::detail::CopyScript(JSContext* cx, Ha
     dst->isAsync_ = src->asyncKind() == AsyncFunction;
     dst->hasRest_ = src->hasRest_;
     dst->isExprBody_ = src->isExprBody_;
 
     if (nconsts != 0) {
         GCPtrValue* vector = Rebase<GCPtrValue>(dst, src, src->consts()->vector);
         dst->consts()->vector = vector;
         for (unsigned i = 0; i < nconsts; ++i)
-            MOZ_ASSERT_IF(vector[i].isMarkable(), vector[i].toString()->isAtom());
+            MOZ_ASSERT_IF(vector[i].isGCThing(), vector[i].toString()->isAtom());
     }
     if (nobjects != 0) {
         GCPtrObject* vector = Rebase<GCPtrObject>(dst, src, src->objects()->vector);
         dst->objects()->vector = vector;
         for (unsigned i = 0; i < nobjects; ++i)
             vector[i].init(&objects[i]->as<NativeObject>());
     }
     {
--- a/js/src/vm/ProxyObject.cpp
+++ b/js/src/vm/ProxyObject.cpp
@@ -39,17 +39,17 @@ ProxyObject::New(JSContext* cx, const Ba
         if (!JSObject::setNewGroupUnknown(cx, clasp, protoObj))
             return nullptr;
     }
 
     // Ensure that the wrapper has the same lifetime assumptions as the
     // wrappee. Prefer to allocate in the nursery, when possible.
     NewObjectKind newKind = NurseryAllocatedProxy;
     if (options.singleton()) {
-        MOZ_ASSERT(priv.isGCThing() && priv.toGCThing()->isTenured());
+        MOZ_ASSERT(priv.isNull() || (priv.isGCThing() && priv.toGCThing()->isTenured()));
         newKind = SingletonObject;
     } else if ((priv.isGCThing() && priv.toGCThing()->isTenured()) ||
                !handler->canNurseryAllocate() ||
                !handler->finalizeInBackground(priv))
     {
         newKind = TenuredObject;
     }
 
--- a/js/xpconnect/src/XPCVariant.cpp
+++ b/js/xpconnect/src/XPCVariant.cpp
@@ -50,27 +50,27 @@ XPCVariant::XPCVariant(JSContext* cx, co
     } else
         mReturnRawObject = false;
 }
 
 XPCTraceableVariant::~XPCTraceableVariant()
 {
     Value val = GetJSValPreserveColor();
 
-    MOZ_ASSERT(val.isGCThing(), "Must be traceable or unlinked");
+    MOZ_ASSERT(val.isGCThing() || val.isNull(), "Must be traceable or unlinked");
 
     mData.Cleanup();
 
     if (!val.isNull())
         RemoveFromRootSet();
 }
 
 void XPCTraceableVariant::TraceJS(JSTracer* trc)
 {
-    MOZ_ASSERT(GetJSValPreserveColor().isMarkable());
+    MOZ_ASSERT(GetJSValPreserveColor().isGCThing());
     JS::TraceEdge(trc, &mJSVal, "XPCTraceableVariant::mJSVal");
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(XPCVariant)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(XPCVariant)
     JS::Value val = tmp->GetJSValPreserveColor();
     if (val.isObject()) {
@@ -81,30 +81,30 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
     tmp->mData.Traverse(cb);
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(XPCVariant)
     JS::Value val = tmp->GetJSValPreserveColor();
 
     tmp->mData.Cleanup();
 
-    if (val.isMarkable()) {
+    if (val.isGCThing()) {
         XPCTraceableVariant* v = static_cast<XPCTraceableVariant*>(tmp);
         v->RemoveFromRootSet();
     }
     tmp->mJSVal = JS::NullValue();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 // static
 already_AddRefed<XPCVariant>
 XPCVariant::newVariant(JSContext* cx, const Value& aJSVal)
 {
     RefPtr<XPCVariant> variant;
 
-    if (!aJSVal.isMarkable())
+    if (!aJSVal.isGCThing())
         variant = new XPCVariant(cx, aJSVal);
     else
         variant = new XPCTraceableVariant(cx, aJSVal);
 
     if (!variant->InitializeData(cx))
         return nullptr;
 
     return variant.forget();
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -2,17 +2,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "base/basictypes.h"
 
 #include "nsLayoutStatics.h"
 #include "nscore.h"
 
-#include "DateTimeFormat.h"
 #include "nsAttrValue.h"
 #include "nsAutoCopyListener.h"
 #include "nsColorNames.h"
 #include "nsComputedDOMStyle.h"
 #include "nsContentDLF.h"
 #include "nsContentUtils.h"
 #include "nsCSSAnonBoxes.h"
 #include "mozilla/css/ErrorReporter.h"
@@ -419,18 +418,16 @@ nsLayoutStatics::Shutdown()
 
   HTMLInputElement::DestroyUploadLastDir();
 
   nsLayoutUtils::Shutdown();
 
   nsHyphenationManager::Shutdown();
   nsDOMMutationObserver::Shutdown();
 
-  DateTimeFormat::Shutdown();
-
   ContentParent::ShutDown();
 
   DisplayItemClip::Shutdown();
 
   CustomElementRegistry::XPCOMShutdown();
 
   CacheObserver::Shutdown();
 
--- a/layout/generic/nsSimplePageSequenceFrame.cpp
+++ b/layout/generic/nsSimplePageSequenceFrame.cpp
@@ -1,16 +1,15 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsSimplePageSequenceFrame.h"
 
-#include "DateTimeFormat.h"
 #include "nsCOMPtr.h"
 #include "nsDeviceContext.h"
 #include "nsPresContext.h"
 #include "gfxContext.h"
 #include "nsRenderingContext.h"
 #include "nsGkAtoms.h"
 #include "nsIPresShell.h"
 #include "nsIPrintSettings.h"
@@ -18,16 +17,17 @@
 #include "nsSubDocumentFrame.h"
 #include "nsRegion.h"
 #include "nsCSSFrameConstructor.h"
 #include "nsContentUtils.h"
 #include "nsDisplayList.h"
 #include "nsHTMLCanvasFrame.h"
 #include "mozilla/dom/HTMLCanvasElement.h"
 #include "nsICanvasRenderingContextInternal.h"
+#include "nsIDateTimeFormat.h"
 #include "nsServiceManagerUtils.h"
 #include <algorithm>
 
 #define OFFSET_NOT_SET -1
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
@@ -294,20 +294,28 @@ nsSimplePageSequenceFrame::Reflow(nsPres
     MOZ_ASSERT(e.get()->GetType() == nsGkAtoms::pageFrame,
                "only expecting nsPageFrame children. Other children will make "
                "this static_cast bogus & probably violate other assumptions");
     nsPageFrame* pf = static_cast<nsPageFrame*>(e.get());
     pf->SetPageNumInfo(pageNum, pageTot);
     pageNum++;
   }
 
+  // Create current Date/Time String
+  if (!mDateFormatter) {
+    mDateFormatter = nsIDateTimeFormat::Create();
+  }
+  if (!mDateFormatter) {
+    return;
+  }
   nsAutoString formattedDateString;
   time_t ltime;
   time( &ltime );
-  if (NS_SUCCEEDED(DateTimeFormat::FormatTime(kDateFormatShort,
+  if (NS_SUCCEEDED(mDateFormatter->FormatTime(nullptr /* nsILocale* locale */,
+                                              kDateFormatShort,
                                               kTimeFormatNoSeconds,
                                               ltime,
                                               formattedDateString))) {
     SetDateTimeStr(formattedDateString);
   }
 
   // Return our desired size
   // Adjust the reflow size by PrintPreviewScale so the scrollbars end up the
--- a/layout/generic/nsSimplePageSequenceFrame.h
+++ b/layout/generic/nsSimplePageSequenceFrame.h
@@ -5,16 +5,18 @@
 #ifndef nsSimplePageSequenceFrame_h___
 #define nsSimplePageSequenceFrame_h___
 
 #include "mozilla/Attributes.h"
 #include "nsIPageSequenceFrame.h"
 #include "nsContainerFrame.h"
 #include "nsIPrintSettings.h"
 
+class nsIDateTimeFormat;
+
 namespace mozilla {
 namespace dom {
 
 class HTMLCanvasElement;
 
 } // namespace dom
 } // namespace mozilla
 
@@ -132,16 +134,19 @@ protected:
                                  const nsMargin& aChildPhysicalMargin);
 
 
   void DetermineWhetherToPrintPage();
   nsIFrame* GetCurrentPageFrame();
 
   nsMargin mMargin;
 
+  // I18N date formatter service which we'll want to cache locally.
+  nsCOMPtr<nsIDateTimeFormat> mDateFormatter;
+
   nsSize       mSize;
   nsSharedPageData* mPageData; // data shared by all the nsPageFrames
 
   // Asynch Printing
   int32_t      mPageNum;
   int32_t      mTotalPages;
   int32_t      mPrintRangeType;
   int32_t      mFromPageNum;
--- a/layout/reftests/w3c-css/submitted/text-decor-3/text-emphasis-style-property-003-ref.html
+++ b/layout/reftests/w3c-css/submitted/text-decor-3/text-emphasis-style-property-003-ref.html
@@ -1,8 +1,8 @@
-<!DOCTYPE html>
-<meta charset="utf-8">
-<title>CSS Reference: text-emphasis-style: filled sesame, vertical</title>
-<link rel="author" title="Xidorn Quan" href="https://www.upsuper.org">
-<link rel="author" title="Mozilla" href="https://www.mozilla.org">
-<style> rt { font-variant-east-asian: normal; } </style>
-<p>Pass if there is a '&#xFE45;' to the right of every character below:</p>
-<div style="writing-mode: vertical-rl; line-height: 5;"><ruby>試<rt>&#xFE45;</rt>験<rt>&#xFE45;</rt>テ<rt>&#xFE45;</rt>ス<rt>&#xFE45;</rt>ト<rt>&#xFE45;</rt></ruby></div>
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Reference: text-emphasis-style: filled sesame, vertical</title>
+<link rel="author" title="Xidorn Quan" href="https://www.upsuper.org">
+<link rel="author" title="Mozilla" href="https://www.mozilla.org">
+<style> rt { font-variant-east-asian: normal; } </style>
+<p>Pass if there is a '&#xFE45;' to the right of every character below:</p>
+<div style="writing-mode: vertical-rl; line-height: 5;"><ruby>試<rt>&#xFE45;</rt>験<rt>&#xFE45;</rt>テ<rt>&#xFE45;</rt>ス<rt>&#xFE45;</rt>ト<rt>&#xFE45;</rt></ruby></div>
--- a/layout/reftests/w3c-css/submitted/text-decor-3/text-emphasis-style-property-004-ref.html
+++ b/layout/reftests/w3c-css/submitted/text-decor-3/text-emphasis-style-property-004-ref.html
@@ -1,8 +1,8 @@
-<!DOCTYPE html>
-<meta charset="utf-8">
-<title>CSS Reference: text-emphasis-style: open sesame, vertical</title>
-<link rel="author" title="Xidorn Quan" href="https://www.upsuper.org">
-<link rel="author" title="Mozilla" href="https://www.mozilla.org">
-<style> rt { font-variant-east-asian: normal; } </style>
-<p>Pass if there is a '&#xFE46;' to the right of every character below:</p>
-<div style="writing-mode: vertical-rl; line-height: 5;"><ruby>試<rt>&#xFE46;</rt>験<rt>&#xFE46;</rt>テ<rt>&#xFE46;</rt>ス<rt>&#xFE46;</rt>ト<rt>&#xFE46;</rt></ruby></div>
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Reference: text-emphasis-style: open sesame, vertical</title>
+<link rel="author" title="Xidorn Quan" href="https://www.upsuper.org">
+<link rel="author" title="Mozilla" href="https://www.mozilla.org">
+<style> rt { font-variant-east-asian: normal; } </style>
+<p>Pass if there is a '&#xFE46;' to the right of every character below:</p>
+<div style="writing-mode: vertical-rl; line-height: 5;"><ruby>試<rt>&#xFE46;</rt>験<rt>&#xFE46;</rt>テ<rt>&#xFE46;</rt>ス<rt>&#xFE46;</rt>ト<rt>&#xFE46;</rt></ruby></div>
--- a/layout/reftests/w3c-css/submitted/text-decor-3/text-emphasis-style-property-004.html
+++ b/layout/reftests/w3c-css/submitted/text-decor-3/text-emphasis-style-property-004.html
@@ -1,10 +1,10 @@
-<!DOCTYPE html>
-<meta charset="utf-8">
-<title>CSS Test: text-emphasis-style: open, vertical</title>
-<link rel="author" title="Xidorn Quan" href="https://www.upsuper.org">
-<link rel="author" title="Mozilla" href="https://www.mozilla.org">
-<link rel="help" href="https://drafts.csswg.org/css-text-decor-3/#text-emphasis-style-property">
-<meta name="assert" content="'text-emphasis: open sesame' produces U+FE46 as emphasis marks.">
-<link rel="match" href="text-emphasis-style-property-004-ref.html">
-<p>Pass if there is a '&#xFE46;' to the right of every character below:</p>
-<div style="writing-mode: vertical-rl; line-height: 5; text-emphasis-style: open">試験テスト</div>
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Test: text-emphasis-style: open, vertical</title>
+<link rel="author" title="Xidorn Quan" href="https://www.upsuper.org">
+<link rel="author" title="Mozilla" href="https://www.mozilla.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-decor-3/#text-emphasis-style-property">
+<meta name="assert" content="'text-emphasis: open sesame' produces U+FE46 as emphasis marks.">
+<link rel="match" href="text-emphasis-style-property-004-ref.html">
+<p>Pass if there is a '&#xFE46;' to the right of every character below:</p>
+<div style="writing-mode: vertical-rl; line-height: 5; text-emphasis-style: open">試験テスト</div>
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -2260,17 +2260,17 @@ nsStyleImage::ComputeActualCropRect(nsIn
 bool
 nsStyleImage::StartDecoding() const
 {
   if (mType == eStyleImageType_Image) {
     imgRequestProxy* req = GetImageData();
     if (!req) {
       return false;
     }
-    return req->StartDecodingWithResult(imgIContainer::FLAG_NONE);
+    return req->StartDecodingWithResult(imgIContainer::FLAG_ASYNC_NOTIFY);
   }
   // null image types always return false from IsComplete, so we do the same here.
   return mType != eStyleImageType_Null ? true : false;
 }
 
 bool
 nsStyleImage::IsOpaque() const
 {
--- a/netwerk/streamconv/converters/nsIndexedToHTML.cpp
+++ b/netwerk/streamconv/converters/nsIndexedToHTML.cpp
@@ -1,29 +1,29 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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 "DateTimeFormat.h"
 #include "nsIndexedToHTML.h"
 #include "mozilla/dom/EncodingUtils.h"
 #include "nsNetUtil.h"
 #include "netCore.h"
 #include "nsStringStream.h"
 #include "nsIFile.h"
 #include "nsIFileURL.h"
 #include "nsEscape.h"
 #include "nsIDirIndex.h"
 #include "nsURLHelper.h"
 #include "nsIPlatformCharset.h"
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
 #include "nsIPrefLocalizedString.h"
 #include "nsIChromeRegistry.h"
+#include "nsIDateTimeFormat.h"
 #include "nsIStringBundle.h"
 #include "nsITextToSubURI.h"
 #include "nsXPIDLString.h"
 #include <algorithm>
 #include "nsIChannel.h"
 
 NS_IMPL_ISUPPORTS(nsIndexedToHTML,
                   nsIDirIndexListener,
@@ -64,16 +64,20 @@ nsIndexedToHTML::Create(nsISupports *aOu
 }
 
 nsresult
 nsIndexedToHTML::Init(nsIStreamListener* aListener) {
     nsresult rv = NS_OK;
 
     mListener = aListener;
 
+    mDateTime = nsIDateTimeFormat::Create();
+    if (!mDateTime)
+      return NS_ERROR_FAILURE;
+
     nsCOMPtr<nsIStringBundleService> sbs =
         do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
     if (NS_FAILED(rv)) return rv;
     rv = sbs->CreateBundle(NECKO_MSGS_URL, getter_AddRefs(mBundle));
 
     mExpectAbsLoc = false;
 
     return rv;
@@ -826,26 +830,28 @@ nsIndexedToHTML::OnIndexAvailable(nsIReq
 
     if (t == -1LL) {
         pushBuffer.AppendLiteral("></td>\n <td>");
     } else {
         pushBuffer.AppendLiteral(" sortable-data=\"");
         pushBuffer.AppendInt(static_cast<int64_t>(t));
         pushBuffer.AppendLiteral("\">");
         nsAutoString formatted;
-        mozilla::DateTimeFormat::FormatPRTime(kDateFormatShort,
-                                              kTimeFormatNone,
-                                              t,
-                                              formatted);
+        mDateTime->FormatPRTime(nullptr,
+                                kDateFormatShort,
+                                kTimeFormatNone,
+                                t,
+                                formatted);
         AppendNonAsciiToNCR(formatted, pushBuffer);
         pushBuffer.AppendLiteral("</td>\n <td>");
-        mozilla::DateTimeFormat::FormatPRTime(kDateFormatNone,
-                                              kTimeFormatSeconds,
-                                              t,
-                                              formatted);
+        mDateTime->FormatPRTime(nullptr,
+                                kDateFormatNone,
+                                kTimeFormatSeconds,
+                                t,
+                                formatted);
         // use NCR to show date in any doc charset
         AppendNonAsciiToNCR(formatted, pushBuffer);
     }
 
     pushBuffer.AppendLiteral("</td>\n</tr>");
 
     return SendToListener(aRequest, aCtxt, pushBuffer);
 }
--- a/netwerk/streamconv/converters/nsIndexedToHTML.h
+++ b/netwerk/streamconv/converters/nsIndexedToHTML.h
@@ -9,16 +9,17 @@
 #include "nsCOMPtr.h"
 #include "nsString.h"
 #include "nsIStreamConverter.h"
 #include "nsIDirIndexListener.h"
 
 #define NS_NSINDEXEDTOHTMLCONVERTER_CID \
 { 0xcf0f71fd, 0xfafd, 0x4e2b, {0x9f, 0xdc, 0x13, 0x4d, 0x97, 0x2e, 0x16, 0xe2} }
 
+class nsIDateTimeFormat;
 class nsIStringBundle;
 class nsITextToSubURI;
 
 class nsIndexedToHTML : public nsIStreamConverter,
                         public nsIDirIndexListener
 {
 public:
     NS_DECL_ISUPPORTS
@@ -41,16 +42,17 @@ protected:
     // Helper to properly implement OnStartRequest
     nsresult DoOnStartRequest(nsIRequest* request, nsISupports *aContext,
                               nsCString& aBuffer);
 
 protected:
     nsCOMPtr<nsIDirIndexParser>     mParser;
     nsCOMPtr<nsIStreamListener>     mListener; // final listener (consumer)
 
+    nsCOMPtr<nsIDateTimeFormat> mDateTime;
     nsCOMPtr<nsIStringBundle> mBundle;
 
     nsCOMPtr<nsITextToSubURI> mTextToSubURI;
 
 private:
     // Expecting absolute locations, given by 201 lines.
     bool mExpectAbsLoc;
 
--- a/security/manager/ssl/TransportSecurityInfo.cpp
+++ b/security/manager/ssl/TransportSecurityInfo.cpp
@@ -1,22 +1,22 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "TransportSecurityInfo.h"
 
-#include "DateTimeFormat.h"
 #include "PSMRunnable.h"
 #include "mozilla/Casting.h"
 #include "nsComponentManagerUtils.h"
 #include "nsIArray.h"
 #include "nsICertOverrideService.h"
+#include "nsIDateTimeFormat.h"
 #include "nsIObjectInputStream.h"
 #include "nsIObjectOutputStream.h"
 #include "nsIWebProgressListener.h"
 #include "nsIX509CertValidity.h"
 #include "nsNSSCertHelper.h"
 #include "nsNSSCertificate.h"
 #include "nsNSSComponent.h"
 #include "nsReadableUtils.h"
@@ -793,19 +793,24 @@ GetDateBoundary(nsIX509Cert* ix509,
   PRTime now = PR_Now();
   if (now > notAfter) {
     timeToUse = notAfter;
   } else {
     timeToUse = notBefore;
     trueExpired_falseNotYetValid = false;
   }
 
-  DateTimeFormat::FormatPRTime(kDateFormatLong, kTimeFormatNoSeconds,
+  nsCOMPtr<nsIDateTimeFormat> dateTimeFormat = nsIDateTimeFormat::Create();
+  if (!dateTimeFormat) {
+    return;
+  }
+
+  dateTimeFormat->FormatPRTime(nullptr, kDateFormatLong, kTimeFormatNoSeconds,
                                timeToUse, formattedDate);
-  DateTimeFormat::FormatPRTime(kDateFormatLong, kTimeFormatNoSeconds,
+  dateTimeFormat->FormatPRTime(nullptr, kDateFormatLong, kTimeFormatNoSeconds,
                                now, nowDate);
 }
 
 static void
 AppendErrorTextTime(nsIX509Cert* ix509,
                     nsINSSComponent *component,
                     nsString &returnedMessage)
 {
--- a/security/manager/ssl/nsNSSCertHelper.cpp
+++ b/security/manager/ssl/nsNSSCertHelper.cpp
@@ -1,24 +1,25 @@
 /* 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 "nsNSSCertHelper.h"
 
 #include <algorithm>
 
-#include "DateTimeFormat.h"
 #include "ScopedNSSTypes.h"
 #include "mozilla/Casting.h"
 #include "mozilla/NotNull.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/UniquePtr.h"
 #include "nsCOMPtr.h"
 #include "nsComponentManagerUtils.h"
+#include "nsDateTimeFormatCID.h"
+#include "nsIDateTimeFormat.h"
 #include "nsNSSASN1Object.h"
 #include "nsNSSCertTrust.h"
 #include "nsNSSCertValidity.h"
 #include "nsNSSCertificate.h"
 #include "nsNSSComponent.h"
 #include "nsServiceManagerUtils.h"
 #include "prerror.h"
 #include "secder.h"
@@ -1608,35 +1609,40 @@ ProcessSECAlgorithmID(SECAlgorithmID *al
   sequence.forget(retSequence);
   return NS_OK;
 }
 
 static nsresult
 ProcessTime(PRTime dispTime, const char16_t* displayName,
             nsIASN1Sequence* parentSequence)
 {
+  nsCOMPtr<nsIDateTimeFormat> dateFormatter = nsIDateTimeFormat::Create();
+  if (!dateFormatter) {
+    return NS_ERROR_FAILURE;
+  }
+
   nsString text;
   nsString tempString;
 
   PRExplodedTime explodedTime;
   PR_ExplodeTime(dispTime, PR_LocalTimeParameters, &explodedTime);
 
-  DateTimeFormat::FormatPRExplodedTime(kDateFormatLong,
-                                       kTimeFormatSeconds, &explodedTime,
-                                       tempString);
+  dateFormatter->FormatPRExplodedTime(nullptr, kDateFormatLong,
+                                      kTimeFormatSeconds, &explodedTime,
+                                      tempString);
 
   text.Append(tempString);
   text.AppendLiteral("\n(");
 
   PRExplodedTime explodedTimeGMT;
   PR_ExplodeTime(dispTime, PR_GMTParameters, &explodedTimeGMT);
 
-  DateTimeFormat::FormatPRExplodedTime(kDateFormatLong,
-                                       kTimeFormatSeconds, &explodedTimeGMT,
-                                       tempString);
+  dateFormatter->FormatPRExplodedTime(nullptr, kDateFormatLong,
+                                      kTimeFormatSeconds, &explodedTimeGMT,
+                                      tempString);
 
   text.Append(tempString);
   text.AppendLiteral(" GMT)");
 
   nsCOMPtr<nsIASN1PrintableItem> printableItem = new nsNSSASN1PrintableItem();
 
   printableItem->SetDisplayValue(text);
   printableItem->SetDisplayName(nsDependentString(displayName));
--- a/security/manager/ssl/nsNSSCertValidity.cpp
+++ b/security/manager/ssl/nsNSSCertValidity.cpp
@@ -57,21 +57,26 @@ nsresult
 nsX509CertValidity::FormatTime(const PRTime& aTimeDate,
                                PRTimeParamFn aParamFn,
                                const nsTimeFormatSelector aTimeFormatSelector,
                                nsAString& aFormattedTimeDate)
 {
   if (!mTimesInitialized)
     return NS_ERROR_FAILURE;
 
+  nsCOMPtr<nsIDateTimeFormat> dateFormatter = nsIDateTimeFormat::Create();
+  if (!dateFormatter) {
+    return NS_ERROR_FAILURE;
+  }
+
   PRExplodedTime explodedTime;
   PR_ExplodeTime(const_cast<PRTime&>(aTimeDate), aParamFn, &explodedTime);
-  return mozilla::DateTimeFormat::FormatPRExplodedTime(kDateFormatLong,
-					                                             aTimeFormatSelector,
-					                                             &explodedTime, aFormattedTimeDate);
+  return dateFormatter->FormatPRExplodedTime(nullptr, kDateFormatLong,
+					     aTimeFormatSelector,
+					     &explodedTime, aFormattedTimeDate);
 }
 
 NS_IMETHODIMP
 nsX509CertValidity::GetNotBeforeLocalTime(nsAString& aNotBeforeLocalTime)
 {
   return FormatTime(mNotBefore, PR_LocalTimeParameters,
                     kTimeFormatSeconds, aNotBeforeLocalTime);
 }
--- a/security/manager/ssl/nsNSSCertValidity.h
+++ b/security/manager/ssl/nsNSSCertValidity.h
@@ -1,17 +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/. */
 
 #ifndef nsNSSCertValidity_h
 #define nsNSSCertValidity_h
 
-#include "DateTimeFormat.h"
 #include "ScopedNSSTypes.h"
+#include "nsIDateTimeFormat.h"
 #include "nsIX509CertValidity.h"
 #include "nsNSSShutDown.h"
 
 class nsX509CertValidity : public nsIX509CertValidity
                          , public nsNSSShutDownObject
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
--- a/toolkit/components/filepicker/nsFileView.cpp
+++ b/toolkit/components/filepicker/nsFileView.cpp
@@ -1,25 +1,25 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "DateTimeFormat.h"
 #include "nsIFileView.h"
 #include "nsITreeView.h"
 #include "mozilla/ModuleUtils.h"
 #include "nsITreeSelection.h"
 #include "nsITreeColumns.h"
 #include "nsITreeBoxObject.h"
 #include "nsIFile.h"
 #include "nsString.h"
 #include "nsReadableUtils.h"
 #include "nsCRT.h"
 #include "nsPrintfCString.h"
+#include "nsIDateTimeFormat.h"
 #include "nsQuickSort.h"
 #include "nsIAtom.h"
 #include "nsIAutoCompleteResult.h"
 #include "nsIAutoCompleteSearch.h"
 #include "nsISimpleEnumerator.h"
 #include "nsAutoPtr.h"
 #include "nsIMutableArray.h"
 #include "nsTArray.h"
@@ -229,16 +229,17 @@ protected:
 
   nsTArray<nsCOMPtr<nsIFile> > mFileList;
   nsTArray<nsCOMPtr<nsIFile> > mDirList;
   nsTArray<nsCOMPtr<nsIFile> > mFilteredFiles;
 
   nsCOMPtr<nsIFile> mDirectoryPath;
   nsCOMPtr<nsITreeBoxObject> mTree;
   nsCOMPtr<nsITreeSelection> mSelection;
+  nsCOMPtr<nsIDateTimeFormat> mDateFormatter;
 
   int16_t mSortType;
   int32_t mTotalRows;
 
   nsTArray<char16_t*> mCurrentFilters;
 
   bool mShowHiddenFiles;
   bool mDirectoryFilter;
@@ -285,16 +286,20 @@ nsFileView::~nsFileView()
   uint32_t count = mCurrentFilters.Length();
   for (uint32_t i = 0; i < count; ++i)
     free(mCurrentFilters[i]);
 }
 
 nsresult
 nsFileView::Init()
 {
+  mDateFormatter = nsIDateTimeFormat::Create();
+  if (!mDateFormatter)
+    return NS_ERROR_OUT_OF_MEMORY;
+
   return NS_OK;
 }
 
 // nsISupports implementation
 
 NS_IMPL_ISUPPORTS(nsFileView, nsITreeView, nsIFileView)
 
 // nsIFileView implementation
@@ -720,18 +725,18 @@ nsFileView::GetCellText(int32_t aRow, ns
   aCol->GetIdConst(&colID);
   if (NS_LITERAL_STRING("FilenameColumn").Equals(colID)) {
     curFile->GetLeafName(aCellText);
   } else if (NS_LITERAL_STRING("LastModifiedColumn").Equals(colID)) {
     PRTime lastModTime;
     curFile->GetLastModifiedTime(&lastModTime);
     // XXX FormatPRTime could take an nsAString&
     nsAutoString temp;
-    mozilla::DateTimeFormat::FormatPRTime(kDateFormatShort, kTimeFormatSeconds,
-                                          lastModTime * 1000, temp);
+    mDateFormatter->FormatPRTime(nullptr, kDateFormatShort, kTimeFormatSeconds,
+                                 lastModTime * 1000, temp);
     aCellText = temp;
   } else {
     // file size
     if (isDirectory)
       aCellText.SetCapacity(0);
     else {
       int64_t fileSize;
       curFile->GetFileSize(&fileSize);
--- a/toolkit/components/telemetry/TelemetryReportingPolicy.jsm
+++ b/toolkit/components/telemetry/TelemetryReportingPolicy.jsm
@@ -42,16 +42,19 @@ const PREF_DATA_SUBMISSION_ENABLED = PRE
 const PREF_CURRENT_POLICY_VERSION = PREF_BRANCH + "currentPolicyVersion";
 // This indicates the minimum required policy version. If the accepted policy version
 // is lower than this, the notification bar must be showed again.
 const PREF_MINIMUM_POLICY_VERSION = PREF_BRANCH + "minimumPolicyVersion";
 // The version of the accepted policy.
 const PREF_ACCEPTED_POLICY_VERSION = PREF_BRANCH + "dataSubmissionPolicyAcceptedVersion";
 // The date user accepted the policy.
 const PREF_ACCEPTED_POLICY_DATE = PREF_BRANCH + "dataSubmissionPolicyNotifiedTime";
+// URL of privacy policy to be opened in a background tab on first run instead of showing the
+// data choices infobar.
+const PREF_FIRST_RUN_URL = PREF_BRANCH + "firstRunURL";
 // The following preferences are deprecated and will be purged during the preferences
 // migration process.
 const DEPRECATED_FHR_PREFS = [
   PREF_BRANCH + "dataSubmissionPolicyAccepted",
   PREF_BRANCH + "dataSubmissionPolicyBypassAcceptance",
   PREF_BRANCH + "dataSubmissionPolicyResponseType",
   PREF_BRANCH + "dataSubmissionPolicyResponseTime"
 ];
@@ -91,17 +94,17 @@ function NotifyPolicyRequest(aLog) {
   this._log = aLog;
 }
 
 NotifyPolicyRequest.prototype = Object.freeze({
   /**
    * Called when the user is notified of the policy.
    */
   onUserNotifyComplete: function() {
-    return TelemetryReportingPolicyImpl._infobarShownCallback();
+    return TelemetryReportingPolicyImpl._userNotified();
    },
 
   /**
    * Called when there was an error notifying the user about the policy.
    *
    * @param error
    *        (Error) Explains what went wrong.
    */
@@ -155,17 +158,17 @@ this.TelemetryReportingPolicy = {
   testIsUserNotified: function() {
     return TelemetryReportingPolicyImpl.isUserNotifiedOfCurrentPolicy;
   },
 
   /**
    * Test only method, used to simulate the infobar being shown in xpcshell tests.
    */
   testInfobarShown: function() {
-    return TelemetryReportingPolicyImpl._infobarShownCallback();
+    return TelemetryReportingPolicyImpl._userNotified();
   },
 };
 
 var TelemetryReportingPolicyImpl = {
   _logger: null,
   // Keep track of the notification status if user wasn't notified already.
   _notificationInProgress: false,
   // The timer used to show the datachoices notification at startup.
@@ -382,44 +385,110 @@ var TelemetryReportingPolicyImpl = {
 
     this._log.trace("_showInfobar - User not notified, notifying now.");
     this._notificationInProgress = true;
     let request = new NotifyPolicyRequest(this._log);
     Observers.notify("datareporting:notify-data-policy:request", request);
   },
 
   /**
-   * Called when the user is notified with the infobar.
+   * Called when the user is notified with the infobar or otherwise.
    */
-  _infobarShownCallback: function() {
-    this._log.trace("_infobarShownCallback");
+  _userNotified() {
+    this._log.trace("_userNotified");
     this._recordNotificationData();
     TelemetrySend.notifyCanUpload();
   },
 
   /**
    * Record date and the version of the accepted policy.
    */
   _recordNotificationData: function() {
     this._log.trace("_recordNotificationData");
     this.dataSubmissionPolicyNotifiedDate = Policy.now();
     this.dataSubmissionPolicyAcceptedVersion = this.currentPolicyVersion;
     // The user was notified and the notification data saved: the notification
     // is no longer in progress.
     this._notificationInProgress = false;
   },
 
+  /**
+   * Try to open the privacy policy in a background tab instead of showing the infobar.
+   */
+  _openFirstRunPage() {
+    let firstRunPolicyURL = Preferences.get(PREF_FIRST_RUN_URL, "");
+    if (!firstRunPolicyURL) {
+      return false;
+    }
+    firstRunPolicyURL = Services.urlFormatter.formatURL(firstRunPolicyURL);
+
+    let win;
+    try {
+      const { RecentWindow } = Cu.import("resource:///modules/RecentWindow.jsm", {});
+      win = RecentWindow.getMostRecentBrowserWindow();
+    } catch (e) {}
+
+    if (!win) {
+      this._log.info("Couldn't find browser window to open first-run page. Falling back to infobar.");
+      return false;
+    }
+
+    // We'll consider the user notified once the privacy policy has been loaded
+    // in a background tab even if that tab hasn't been selected.
+    let progressListener = {};
+    progressListener.onStateChange =
+      (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) => {
+        if (aWebProgress.isTopLevel &&
+            aBrowser == tab.linkedBrowser &&
+            aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+            aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK) {
+          let uri = aBrowser.documentURI;
+          if (uri && !/^about:(blank|neterror|certerror|blocked)/.test(uri.spec)) {
+            this._userNotified();
+          } else {
+            this._log.info("Failed to load first-run page. Falling back to infobar.");
+            this._showInfobar();
+          }
+          removeListeners();
+        }
+      };
+
+    let removeListeners = () => {
+      win.removeEventListener("unload", removeListeners);
+      win.gBrowser.removeTabsProgressListener(progressListener);
+    };
+
+    win.addEventListener("unload", removeListeners);
+    win.gBrowser.addTabsProgressListener(progressListener);
+
+    let tab = win.gBrowser.loadOneTab(firstRunPolicyURL, { inBackground: true });
+
+    return true;
+  },
+
   observe: function(aSubject, aTopic, aData) {
     if (aTopic != "sessionstore-windows-restored") {
       return;
     }
 
     const isFirstRun = Preferences.get(PREF_FIRST_RUN, true);
+    if (isFirstRun) {
+      // We're performing the first run, flip firstRun preference for subsequent runs.
+      Preferences.set(PREF_FIRST_RUN, false);
+
+      try {
+        if (this._openFirstRunPage()) {
+          return;
+        }
+      } catch (e) {
+        this._log.error("Failed to open privacy policy tab: " + e);
+      }
+    }
+
+    // Show the info bar.
     const delay =
       isFirstRun ? NOTIFICATION_DELAY_FIRST_RUN_MSEC : NOTIFICATION_DELAY_NEXT_RUNS_MSEC;
 
     this._startupNotificationTimerId = Policy.setShowInfobarTimeout(
         // Calling |canUpload| eventually shows the infobar, if needed.
         () => this._showInfobar(), delay);
-    // We performed at least a run, flip the firstRun preference.
-    Preferences.set(PREF_FIRST_RUN, false);
   },
 };
--- a/toolkit/components/telemetry/datareporting-prefs.js
+++ b/toolkit/components/telemetry/datareporting-prefs.js
@@ -4,8 +4,9 @@
 
 pref("datareporting.policy.dataSubmissionEnabled", true);
 pref("datareporting.policy.dataSubmissionPolicyNotifiedTime", "0");
 pref("datareporting.policy.dataSubmissionPolicyAcceptedVersion", 0);
 pref("datareporting.policy.dataSubmissionPolicyBypassNotification", false);
 pref("datareporting.policy.currentPolicyVersion", 2);
 pref("datareporting.policy.minimumPolicyVersion", 1);
 pref("datareporting.policy.minimumPolicyVersion.channel-beta", 2);
+pref("datareporting.policy.firstRunURL", "");
--- a/toolkit/components/telemetry/docs/internals/preferences.rst
+++ b/toolkit/components/telemetry/docs/internals/preferences.rst
@@ -52,16 +52,20 @@ Preferences
 
 Data-choices notification
 -------------------------
 
 ``toolkit.telemetry.reportingpolicy.firstRun``
 
   This preference is not present until the first run. After, its value is set to false. This is used to show the infobar with a more aggressive timeout if it wasn't shown yet.
 
+``datareporting.policy.firstRunURL``
+
+  If set, a browser tab will be opened on first run instead of the infobar.
+
 ``datareporting.policy.dataSubmissionEnabled``
 
   This is the data submission master kill switch. If disabled, no policy is shown or upload takes place, ever.
 
 ``datareporting.policy.dataSubmissionPolicyNotifiedTime``
 
   Records the date user was shown the policy. This preference is also used on Android.
 
--- a/widget/windows/WinPointerEvents.cpp
+++ b/widget/windows/WinPointerEvents.cpp
@@ -5,16 +5,17 @@
 
 /*
  * WinPointerEvents - Helper functions to retrieve PointerEvent's attributes
  */
 
 #include "nscore.h"
 #include "WinPointerEvents.h"
 #include "mozilla/MouseEvents.h"
+#include "mozilla/WindowsVersion.h"
 
 using namespace mozilla;
 using namespace mozilla::widget;
 
 const wchar_t WinPointerEvents::kPointerLibraryName[] =  L"user32.dll";
 HMODULE WinPointerEvents::sLibraryHandle = nullptr;
 WinPointerEvents::GetPointerTypePtr WinPointerEvents::getPointerType = nullptr;
 WinPointerEvents::GetPointerInfoPtr WinPointerEvents::getPointerInfo = nullptr;
--- a/widget/windows/WinPointerEvents.h
+++ b/widget/windows/WinPointerEvents.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 WinPointerEvents_h__
 #define WinPointerEvents_h__
 
 #include "mozilla/MouseEvents.h"
+#include "nsWindowBase.h"
 
 // Define PointerEvent related macros and structures when building code on
 // Windows version before Win8.
 #if WINVER < 0x0602
 
 // These definitions are copied from WinUser.h. Some of them are not used but
 // keep them here for future usage.
 #define WM_NCPOINTERUPDATE              0x0241
--- a/xpcom/base/nsCycleCollector.cpp
+++ b/xpcom/base/nsCycleCollector.cpp
@@ -2685,17 +2685,17 @@ public:
   {
     return !mObjects.IsEmpty();
   }
 
   virtual void Trace(JS::Heap<JS::Value>* aValue, const char* aName,
                      void* aClosure) const override
   {
     const JS::Value& val = aValue->unbarrieredGet();
-    if (val.isMarkable() && ValueIsGrayCCThing(val)) {
+    if (val.isGCThing() && ValueIsGrayCCThing(val)) {
       MOZ_ASSERT(!js::gc::IsInsideNursery(val.toGCThing()));
       mCollector->GetJSPurpleBuffer()->mValues.InfallibleAppend(val);
     }
   }
 
   virtual void Trace(JS::Heap<jsid>* aId, const char* aName,
                      void* aClosure) const override
   {
--- a/xpcom/base/nsCycleCollectorTraceJSHelpers.cpp
+++ b/xpcom/base/nsCycleCollectorTraceJSHelpers.cpp
@@ -31,17 +31,17 @@ nsScriptObjectTracer::NoteJSChild(JS::GC
     cb->NoteJSChild(aGCThing);
   }
 }
 
 void
 TraceCallbackFunc::Trace(JS::Heap<JS::Value>* aPtr, const char* aName,
                          void* aClosure) const
 {
-  if (aPtr->unbarrieredGet().isMarkable()) {
+  if (aPtr->unbarrieredGet().isGCThing()) {
     mCallback(JS::GCCellPtr(aPtr->unbarrieredGet()), aName, aClosure);
   }
 }
 
 void
 TraceCallbackFunc::Trace(JS::Heap<jsid>* aPtr, const char* aName,
                          void* aClosure) const
 {