Back out changeset 2f614bcd9405 (bug 1221822) for test_conformance__extensions__ext-sRGB.html and test_webgl2_alpha_luminance.html failures
authorPhil Ringnalda <philringnalda@gmail.com>
Mon, 23 Nov 2015 21:55:59 -0800
changeset 295582 e82fd82346cb17e9cf78724df0546c66cc8804fd
parent 295581 227989411ad6d6ec0c59f06644f9cb69db10b0c7
child 295583 3d0bf42bd24a2971a62a097145a91161cf4c401d
push id8824
push userraliiev@mozilla.com
push dateMon, 14 Dec 2015 20:18:56 +0000
treeherdermozilla-aurora@e2031358e2a6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1221822
milestone45.0a1
backs out2f614bcd940570cc63a6b36eade04549a531af6f
Back out changeset 2f614bcd9405 (bug 1221822) for test_conformance__extensions__ext-sRGB.html and test_webgl2_alpha_luminance.html failures
dom/base/nsDOMWindowUtils.cpp
dom/canvas/CanvasRenderingContext2D.cpp
dom/canvas/ImageBitmap.cpp
dom/canvas/TexUnpackBlob.cpp
dom/canvas/TexUnpackBlob.h
dom/canvas/WebGL1Context.cpp
dom/canvas/WebGL1Context.h
dom/canvas/WebGL2Context.cpp
dom/canvas/WebGL2Context.h
dom/canvas/WebGL2ContextFramebuffers.cpp
dom/canvas/WebGL2ContextState.cpp
dom/canvas/WebGL2ContextTextures.cpp
dom/canvas/WebGLBuffer.h
dom/canvas/WebGLContext.cpp
dom/canvas/WebGLContext.h
dom/canvas/WebGLContextDraw.cpp
dom/canvas/WebGLContextExtensions.cpp
dom/canvas/WebGLContextFramebufferOperations.cpp
dom/canvas/WebGLContextGL.cpp
dom/canvas/WebGLContextState.cpp
dom/canvas/WebGLContextTextures.cpp
dom/canvas/WebGLContextUtils.cpp
dom/canvas/WebGLContextUtils.h
dom/canvas/WebGLContextValidate.cpp
dom/canvas/WebGLContextVertices.cpp
dom/canvas/WebGLExtensionColorBufferFloat.cpp
dom/canvas/WebGLExtensionColorBufferHalfFloat.cpp
dom/canvas/WebGLExtensionCompressedTextureATC.cpp
dom/canvas/WebGLExtensionCompressedTextureETC1.cpp
dom/canvas/WebGLExtensionCompressedTexturePVRTC.cpp
dom/canvas/WebGLExtensionCompressedTextureS3TC.cpp
dom/canvas/WebGLExtensionDepthTexture.cpp
dom/canvas/WebGLExtensionDrawBuffers.cpp
dom/canvas/WebGLExtensionSRGB.cpp
dom/canvas/WebGLExtensionTextureFloat.cpp
dom/canvas/WebGLExtensionTextureFloatLinear.cpp
dom/canvas/WebGLExtensionTextureHalfFloat.cpp
dom/canvas/WebGLExtensionTextureHalfFloatLinear.cpp
dom/canvas/WebGLExtensions.h
dom/canvas/WebGLFormats.cpp
dom/canvas/WebGLFormats.h
dom/canvas/WebGLFramebuffer.cpp
dom/canvas/WebGLFramebuffer.h
dom/canvas/WebGLObjectModel.h
dom/canvas/WebGLProgram.cpp
dom/canvas/WebGLProgram.h
dom/canvas/WebGLQuery.h
dom/canvas/WebGLRenderbuffer.cpp
dom/canvas/WebGLRenderbuffer.h
dom/canvas/WebGLSampler.cpp
dom/canvas/WebGLShader.cpp
dom/canvas/WebGLShader.h
dom/canvas/WebGLShaderValidator.cpp
dom/canvas/WebGLStrongTypes.h
dom/canvas/WebGLSync.cpp
dom/canvas/WebGLTexelConversions.cpp
dom/canvas/WebGLTexelConversions.h
dom/canvas/WebGLTexture.cpp
dom/canvas/WebGLTexture.h
dom/canvas/WebGLTextureUpload.cpp
dom/canvas/WebGLTimerQuery.cpp
dom/canvas/WebGLTransformFeedback.cpp
dom/canvas/WebGLTypes.h
dom/canvas/WebGLVertexArray.h
dom/canvas/moz.build
dom/canvas/test/webgl-conformance/conformance/extensions/ext-sRGB.html
dom/canvas/test/webgl-conformance/conformance/extensions/oes-texture-float.html
dom/canvas/test/webgl-conformance/conformance/misc/object-deletion-behaviour.html
dom/canvas/test/webgl-conformance/conformance/more/functions/texImage2DBadArgs.html
dom/canvas/test/webgl-conformance/conformance/more/functions/texSubImage2DBadArgs.html
dom/canvas/test/webgl-conformance/conformance/more/util.js
dom/canvas/test/webgl-conformance/conformance/renderbuffers/framebuffer-test.html
dom/canvas/test/webgl-conformance/conformance/resources/webgl-test-utils.js
dom/canvas/test/webgl-conformance/conformance/textures/tex-input-validation.html
dom/canvas/test/webgl-conformance/conformance/textures/texture-formats-test.html
dom/canvas/test/webgl-mochitest/test_implicit_color_buffer_float.html
dom/imptests/html/mochitest.ini
dom/webidl/WebGL2RenderingContext.webidl
dom/webidl/WebGLRenderingContext.webidl
gfx/2d/2D.h
gfx/2d/DataSurfaceHelpers.cpp
gfx/2d/Factory.cpp
gfx/2d/SourceSurfaceRawData.cpp
gfx/2d/SourceSurfaceRawData.h
gfx/2d/moz.build
gfx/angle/src/libANGLE/renderer/d3d/TextureD3D.cpp
gfx/angle/src/libANGLE/renderer/d3d/d3d9/Blit9.cpp
gfx/gl/GLBlitHelper.cpp
gfx/gl/GLBlitHelper.h
gfx/gl/GLContext.h
gfx/gl/GLContextFeatures.cpp
gfx/gl/GLContextProviderCGL.mm
gfx/gl/ScopedGLHelpers.cpp
gfx/gl/ScopedGLHelpers.h
gfx/thebes/gfxPrefs.h
layout/base/nsCSSRendering.cpp
layout/base/nsIPresShell.h
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
modules/libpref/init/all.js
testing/web-platform/tests/webgl/compressedTexSubImage2D.html
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -978,17 +978,17 @@ nsDOMWindowUtils::SendKeyEvent(const nsA
                                int32_t aKeyCode,
                                int32_t aCharCode,
                                int32_t aModifiers,
                                uint32_t aAdditionalFlags,
                                bool* aDefaultActionTaken)
 {
   // get the widget to send the event to
   nsCOMPtr<nsIWidget> widget = GetWidget();
-
+  
   return nsContentUtils::SendKeyEvent(widget, aType, aKeyCode, aCharCode,
                                       aModifiers, aAdditionalFlags,
                                       aDefaultActionTaken);
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::SendNativeKeyEvent(int32_t aNativeKeyboardLayout,
                                      int32_t aNativeKeyCode,
@@ -1307,17 +1307,17 @@ nsDOMWindowUtils::NodesFromRect(float aX
                                 float aBottomSize, float aLeftSize,
                                 bool aIgnoreRootScrollFrame,
                                 bool aFlushLayout,
                                 nsIDOMNodeList** aReturn)
 {
   nsCOMPtr<nsIDocument> doc = GetDocument();
   NS_ENSURE_STATE(doc);
 
-  return doc->NodesFromRectHelper(aX, aY, aTopSize, aRightSize, aBottomSize, aLeftSize,
+  return doc->NodesFromRectHelper(aX, aY, aTopSize, aRightSize, aBottomSize, aLeftSize, 
                                   aIgnoreRootScrollFrame, aFlushLayout, aReturn);
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::GetTranslationNodes(nsIDOMNode* aRoot,
                                       nsITranslationNodeList** aRetVal)
 {
   NS_ENSURE_ARG_POINTER(aRetVal);
@@ -1403,19 +1403,17 @@ CanvasToDataSourceSurface(nsIDOMHTMLCanv
     return nullptr;
   }
 
   MOZ_ASSERT(node->IsElement(),
              "An nsINode that implements nsIDOMHTMLCanvasElement should "
              "be an element.");
   nsLayoutUtils::SurfaceFromElementResult result =
     nsLayoutUtils::SurfaceFromElement(node->AsElement());
-
-  MOZ_ASSERT(result.GetSourceSurface());
-  return result.GetSourceSurface()->GetDataSurface();
+  return result.mSourceSurface->GetDataSurface();
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::CompareCanvases(nsIDOMHTMLCanvasElement *aCanvas1,
                                   nsIDOMHTMLCanvasElement *aCanvas2,
                                   uint32_t* aMaxDifference,
                                   uint32_t* retVal)
 {
@@ -1746,17 +1744,17 @@ nsDOMWindowUtils::GetFullZoom(float* aFu
   if (!presContext) {
     return NS_OK;
   }
 
   *aFullZoom = presContext->DeviceContext()->GetFullZoom();
 
   return NS_OK;
 }
-
+ 
 NS_IMETHODIMP
 nsDOMWindowUtils::DispatchDOMEventViaPresShell(nsIDOMNode* aTarget,
                                                nsIDOMEvent* aEvent,
                                                bool aTrusted,
                                                bool* aRetVal)
 {
   NS_ENSURE_STATE(aEvent);
   aEvent->SetTrusted(aTrusted);
@@ -2449,17 +2447,17 @@ nsDOMWindowUtils::RenderDocument(const n
     // Get Primary Shell
     nsCOMPtr<nsIPresShell> presShell = doc->GetShell();
     NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
 
     // Render Document
     return presShell->RenderDocument(aRect, aFlags, aBackgroundColor, aThebesContext);
 }
 
-NS_IMETHODIMP
+NS_IMETHODIMP 
 nsDOMWindowUtils::GetCursorType(int16_t *aCursor)
 {
   NS_ENSURE_ARG_POINTER(aCursor);
 
   nsIDocument* doc = GetDocument();
   NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
 
   bool isSameDoc = false;
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -1204,18 +1204,18 @@ bool CanvasRenderingContext2D::SwitchRen
   }
 
 #ifdef USE_SKIA_GPU
   if (mRenderingMode == RenderingMode::OpenGLBackendMode) {
     if (mVideoTexture) {
       gfxPlatform::GetPlatform()->GetSkiaGLGlue()->GetGLContext()->MakeCurrent();
       gfxPlatform::GetPlatform()->GetSkiaGLGlue()->GetGLContext()->fDeleteTextures(1, &mVideoTexture);
     }
-    mCurrentVideoSize.width = 0;
-    mCurrentVideoSize.height = 0;
+	  mCurrentVideoSize.width = 0;
+	  mCurrentVideoSize.height = 0;
   }
 #endif
 
   RefPtr<SourceSurface> snapshot;
   Matrix transform;
 
   if (mTarget) {
     snapshot = mTarget->Snapshot();
@@ -2108,24 +2108,25 @@ CanvasRenderingContext2D::CreatePattern(
   EnsureTarget();
 
   // The canvas spec says that createPattern should use the first frame
   // of animated images
   nsLayoutUtils::SurfaceFromElementResult res =
     nsLayoutUtils::SurfaceFromElement(htmlElement,
       nsLayoutUtils::SFE_WANT_FIRST_FRAME, mTarget);
 
-  if (!res.GetSourceSurface()) {
+  if (!res.mSourceSurface) {
     error.Throw(NS_ERROR_NOT_AVAILABLE);
     return nullptr;
   }
 
-  RefPtr<CanvasPattern> pat = new CanvasPattern(this, res.GetSourceSurface(), repeatMode,
-                                                res.mPrincipal, res.mIsWriteOnly,
-                                                res.mCORSUsed);
+  RefPtr<CanvasPattern> pat =
+    new CanvasPattern(this, res.mSourceSurface, repeatMode, res.mPrincipal,
+                             res.mIsWriteOnly, res.mCORSUsed);
+
   return pat.forget();
 }
 
 //
 // shadows
 //
 void
 CanvasRenderingContext2D::SetShadowColor(const nsAString& shadowColor)
@@ -5401,19 +5402,19 @@ CanvasRenderingContext2D::PutImageData_e
 
   uint32_t copyX = dirtyRect.x - x;
   uint32_t copyY = dirtyRect.y - y;
   //uint8_t *src = aArray->Data();
   uint8_t *dst = imgsurf->Data();
   uint8_t* srcLine = aArray->Data() + copyY * (w * 4) + copyX * 4;
 #if 0
   printf("PutImageData_explicit: dirty x=%d y=%d w=%d h=%d copy x=%d y=%d w=%d h=%d ext x=%d y=%d w=%d h=%d\n",
-       dirtyRect.x, dirtyRect.y, copyWidth, copyHeight,
-       copyX, copyY, copyWidth, copyHeight,
-       x, y, w, h);
+	     dirtyRect.x, dirtyRect.y, copyWidth, copyHeight,
+	     copyX, copyY, copyWidth, copyHeight,
+	     x, y, w, h);
 #endif
   for (uint32_t j = 0; j < copyHeight; j++) {
     uint8_t *src = srcLine;
     for (uint32_t i = 0; i < copyWidth; i++) {
       uint8_t r = *src++;
       uint8_t g = *src++;
       uint8_t b = *src++;
       uint8_t a = *src++;
--- a/dom/canvas/ImageBitmap.cpp
+++ b/dom/canvas/ImageBitmap.cpp
@@ -350,23 +350,22 @@ GetSurfaceFromElement(nsIGlobalObject* a
     nsLayoutUtils::SurfaceFromElement(&aElement, nsLayoutUtils::SFE_WANT_FIRST_FRAME);
 
   // check origin-clean
   if (!CheckSecurityForHTMLElements(res)) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return nullptr;
   }
 
-  RefPtr<SourceSurface> surface = res.GetSourceSurface();
-
-  if (NS_WARN_IF(!surface)) {
+  if (NS_WARN_IF(!res.mSourceSurface)) {
     aRv.Throw(NS_ERROR_NOT_AVAILABLE);
     return nullptr;
   }
 
+  RefPtr<SourceSurface> surface(res.mSourceSurface);
   return surface.forget();
 }
 
 /*
  * The specification doesn't allow to create an ImegeBitmap from a vector image.
  * This function is used to check if the given HTMLImageElement contains a
  * raster image.
  */
deleted file mode 100644
--- a/dom/canvas/TexUnpackBlob.cpp
+++ /dev/null
@@ -1,773 +0,0 @@
-/* -*- Mode: C++; tab-width: 20; 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 "TexUnpackBlob.h"
-
-#include "GLBlitHelper.h"
-#include "GLContext.h"
-#include "GLDefs.h"
-#include "mozilla/dom/Element.h"
-#include "mozilla/dom/HTMLCanvasElement.h"
-#include "mozilla/RefPtr.h"
-#include "nsLayoutUtils.h"
-#include "WebGLContext.h"
-#include "WebGLTexelConversions.h"
-#include "WebGLTexture.h"
-
-namespace mozilla {
-namespace webgl {
-
-static GLenum
-DoTexOrSubImage(bool isSubImage, gl::GLContext* gl, TexImageTarget target, GLint level,
-                const DriverUnpackInfo* dui, GLint xOffset, GLint yOffset, GLint zOffset,
-                GLsizei width, GLsizei height, GLsizei depth, const void* data)
-{
-    if (isSubImage) {
-        return DoTexSubImage(gl, target, level, xOffset, yOffset, zOffset, width, height,
-                             depth, dui->ToPacking(), data);
-    } else {
-        return DoTexImage(gl, target, level, dui, width, height, depth, data);
-    }
-}
-
-/*static*/ void
-TexUnpackBlob::OriginsForDOM(WebGLContext* webgl, gl::OriginPos* const out_src,
-                             gl::OriginPos* const out_dst)
-{
-    // Our surfaces are TopLeft.
-    *out_src = gl::OriginPos::TopLeft;
-
-    // WebGL specs the default as passing DOM elements top-left first.
-    // Thus y-flip would give us bottom-left.
-    *out_dst = webgl->mPixelStore_FlipY ? gl::OriginPos::BottomLeft
-                                        : gl::OriginPos::TopLeft;
-}
-
-//////////////////////////////////////////////////////////////////////////////////////////
-// TexUnpackBytes
-
-bool
-TexUnpackBytes::ValidateUnpack(WebGLContext* webgl, const char* funcName, bool isFunc3D,
-                               const webgl::PackingInfo& pi)
-{
-    if (!mBytes)
-        return true;
-
-    const auto bytesPerPixel = webgl::BytesPerPixel(pi);
-    const auto bytesNeeded = webgl->GetUnpackSize(isFunc3D, mWidth, mHeight, mDepth,
-                                                  bytesPerPixel);
-    if (!bytesNeeded.isValid()) {
-        webgl->ErrorInvalidOperation("%s: Overflow while computing the needed buffer"
-                                     " size.",
-                                     funcName);
-        return false;
-    }
-
-    if (mByteCount < bytesNeeded.value()) {
-        webgl->ErrorInvalidOperation("%s: Provided buffer is too small. (needs %u, has"
-                                     " %u)",
-                                     funcName, bytesNeeded.value(), mByteCount);
-        return false;
-    }
-
-    return true;
-}
-
-static bool
-UnpackFormatHasAlpha(GLenum unpackFormat)
-{
-    switch (unpackFormat) {
-    case LOCAL_GL_ALPHA:
-    case LOCAL_GL_LUMINANCE_ALPHA:
-    case LOCAL_GL_RGBA:
-        return true;
-
-    default:
-        return false;
-    }
-}
-
-static WebGLTexelFormat
-FormatFromPacking(const webgl::PackingInfo& pi)
-{
-    switch (pi.type) {
-    case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
-        return WebGLTexelFormat::RGB565;
-
-    case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
-        return WebGLTexelFormat::RGBA5551;
-
-    case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
-        return WebGLTexelFormat::RGBA4444;
-
-    case LOCAL_GL_UNSIGNED_BYTE:
-        switch (pi.format) {
-        case LOCAL_GL_LUMINANCE:        return WebGLTexelFormat::R8;
-        case LOCAL_GL_ALPHA:            return WebGLTexelFormat::A8;
-        case LOCAL_GL_LUMINANCE_ALPHA:  return WebGLTexelFormat::RA8;
-        case LOCAL_GL_RGB:              return WebGLTexelFormat::RGB8;
-        case LOCAL_GL_SRGB:             return WebGLTexelFormat::RGB8;
-        case LOCAL_GL_RGBA:             return WebGLTexelFormat::RGBA8;
-        case LOCAL_GL_SRGB_ALPHA:       return WebGLTexelFormat::RGBA8;
-        }
-
-    case LOCAL_GL_HALF_FLOAT:
-    case LOCAL_GL_HALF_FLOAT_OES:
-        switch (pi.format) {
-        case LOCAL_GL_LUMINANCE:        return WebGLTexelFormat::R16F;
-        case LOCAL_GL_ALPHA:            return WebGLTexelFormat::A16F;
-        case LOCAL_GL_LUMINANCE_ALPHA:  return WebGLTexelFormat::RA16F;
-        case LOCAL_GL_RGB:              return WebGLTexelFormat::RGB16F;
-        case LOCAL_GL_RGBA:             return WebGLTexelFormat::RGBA16F;
-        }
-
-    case LOCAL_GL_FLOAT:
-        switch (pi.format) {
-        case LOCAL_GL_LUMINANCE:        return WebGLTexelFormat::R32F;
-        case LOCAL_GL_ALPHA:            return WebGLTexelFormat::A32F;
-        case LOCAL_GL_LUMINANCE_ALPHA:  return WebGLTexelFormat::RA32F;
-        case LOCAL_GL_RGB:              return WebGLTexelFormat::RGB32F;
-        case LOCAL_GL_RGBA:             return WebGLTexelFormat::RGBA32F;
-        }
-    }
-
-    return WebGLTexelFormat::FormatNotSupportingAnyConversion;
-}
-
-void
-TexUnpackBytes::TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName,
-                              WebGLTexture* tex, TexImageTarget target, GLint level,
-                              const webgl::DriverUnpackInfo* dui, GLint xOffset,
-                              GLint yOffset, GLint zOffset, GLenum* const out_glError)
-{
-    WebGLContext* webgl = tex->mContext;
-    gl::GLContext* gl = webgl->gl;
-
-    const void* uploadBytes = mBytes;
-    UniqueBuffer tempBuffer;
-
-    do {
-        if (!webgl->mPixelStore_FlipY && !webgl->mPixelStore_PremultiplyAlpha)
-            break;
-
-        if (!mBytes || !mWidth || !mHeight || !mDepth)
-            break;
-
-        if (webgl->IsWebGL2())
-            break;
-        MOZ_ASSERT(mDepth == 1);
-
-        // This is literally the worst.
-        webgl->GenerateWarning("%s: Uploading ArrayBuffers with FLIP_Y or"
-                               " PREMULTIPLY_ALPHA is slow.",
-                               funcName);
-
-        tempBuffer = malloc(mByteCount);
-        if (!tempBuffer) {
-            *out_glError = LOCAL_GL_OUT_OF_MEMORY;
-            return;
-        }
-
-        const webgl::PackingInfo pi = { dui->unpackFormat, dui->unpackType };
-
-        const auto bytesPerPixel           = webgl::BytesPerPixel(pi);
-        const auto rowByteAlignment        = webgl->mPixelStore_UnpackAlignment;
-
-        const size_t bytesPerRow = bytesPerPixel * mWidth;
-        const size_t rowStride = RoundUpToMultipleOf(bytesPerRow, rowByteAlignment);
-
-        const bool needsYFlip = webgl->mPixelStore_FlipY;
-
-        bool needsAlphaPremult = webgl->mPixelStore_PremultiplyAlpha;
-        if (!UnpackFormatHasAlpha(pi.format))
-            needsAlphaPremult = false;
-
-        if (!needsAlphaPremult) {
-            if (!webgl->mPixelStore_FlipY)
-                break;
-
-            const uint8_t* src = (const uint8_t*)mBytes;
-            const uint8_t* const srcEnd = src + rowStride * mHeight;
-
-            uint8_t* dst = (uint8_t*)tempBuffer.get() + rowStride * (mHeight - 1);
-
-            while (src != srcEnd) {
-                memcpy(dst, src, bytesPerRow);
-                src += rowStride;
-                dst -= rowStride;
-            }
-
-            uploadBytes = tempBuffer.get();
-            break;
-        }
-
-        const auto texelFormat = FormatFromPacking(pi);
-        if (texelFormat == WebGLTexelFormat::FormatNotSupportingAnyConversion) {
-            MOZ_ASSERT(false, "Bad texelFormat from pi.");
-            *out_glError = LOCAL_GL_OUT_OF_MEMORY;
-            return;
-        }
-
-        const auto srcOrigin = gl::OriginPos::BottomLeft;
-        const auto dstOrigin = (needsYFlip ? gl::OriginPos::TopLeft
-                                           : gl::OriginPos::BottomLeft);
-
-        const bool srcPremultiplied = false;
-        const bool dstPremultiplied = needsAlphaPremult; // Always true here.
-        // And go!:
-        if (!ConvertImage(mWidth, mHeight,
-                          mBytes, rowStride, srcOrigin, texelFormat, srcPremultiplied,
-                          tempBuffer.get(), rowStride, dstOrigin, texelFormat,
-                          dstPremultiplied))
-        {
-            MOZ_ASSERT(false, "ConvertImage failed unexpectedly.");
-            *out_glError = LOCAL_GL_OUT_OF_MEMORY;
-            return;
-        }
-
-        uploadBytes = tempBuffer.get();
-    } while (false);
-
-    GLenum error = DoTexOrSubImage(isSubImage, gl, target, level, dui, xOffset, yOffset,
-                                   zOffset, mWidth, mHeight, mDepth, uploadBytes);
-    *out_glError = error;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////////
-// TexUnpackImage
-
-TexUnpackImage::TexUnpackImage(const RefPtr<layers::Image>& image, bool isAlphaPremult)
-    : TexUnpackBlob(image->GetSize().width, image->GetSize().height, 1, true)
-    , mImage(image)
-    , mIsAlphaPremult(isAlphaPremult)
-{ }
-
-TexUnpackImage::~TexUnpackImage()
-{ }
-
-void
-TexUnpackImage::TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName,
-                              WebGLTexture* tex, TexImageTarget target, GLint level,
-                              const webgl::DriverUnpackInfo* dui, GLint xOffset,
-                              GLint yOffset, GLint zOffset, GLenum* const out_glError)
-{
-    MOZ_ASSERT_IF(needsRespec, !isSubImage);
-    *out_glError = 0;
-
-    WebGLContext* webgl = tex->mContext;
-
-    gl::GLContext* gl = webgl->GL();
-    gl->MakeCurrent();
-
-    if (needsRespec) {
-        GLenum error = DoTexOrSubImage(isSubImage, gl, target.get(), level, dui, xOffset,
-                                       yOffset, zOffset, mWidth, mHeight, mDepth,
-                                       nullptr);
-        if (error) {
-            MOZ_ASSERT(!error);
-            *out_glError = LOCAL_GL_OUT_OF_MEMORY;
-            return;
-        }
-    }
-
-    do {
-        if (dui->unpackFormat != LOCAL_GL_RGB && dui->unpackFormat != LOCAL_GL_RGBA)
-            break;
-
-        if (dui->unpackType != LOCAL_GL_UNSIGNED_BYTE)
-            break;
-
-        gl::ScopedFramebuffer scopedFB(gl);
-        gl::ScopedBindFramebuffer bindFB(gl, scopedFB.FB());
-
-        {
-            gl::GLContext::LocalErrorScope errorScope(*gl);
-
-            gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0,
-                                      target.get(), tex->mGLName, level);
-
-            if (errorScope.GetError())
-                break;
-        }
-
-        const GLenum status = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
-        if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE)
-            break;
-
-        gl::OriginPos srcOrigin, dstOrigin;
-        OriginsForDOM(webgl, &srcOrigin, &dstOrigin);
-
-        const gfx::IntSize destSize(mWidth, mHeight);
-        if (!gl->BlitHelper()->BlitImageToFramebuffer(mImage, destSize, scopedFB.FB(),
-                                                      dstOrigin))
-        {
-            break;
-        }
-
-        return; // Blitting was successful, so we're done!
-    } while (false);
-
-    TexUnpackSurface surfBlob(mImage->GetAsSourceSurface(), mIsAlphaPremult);
-
-    surfBlob.TexOrSubImage(isSubImage, needsRespec, funcName, tex, target, level, dui,
-                           xOffset, yOffset, zOffset, out_glError);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////////
-// TexUnpackSurface
-
-static bool
-GuessAlignment(const void* data, size_t bytesPerRow, size_t stride, size_t maxAlignment,
-               size_t* const out_alignment)
-{
-    size_t alignmentGuess = maxAlignment;
-    while (alignmentGuess) {
-        size_t guessStride = RoundUpToMultipleOf(bytesPerRow, alignmentGuess);
-        if (guessStride == stride &&
-            uintptr_t(data) % alignmentGuess == 0)
-        {
-            *out_alignment = alignmentGuess;
-            return true;
-        }
-        alignmentGuess /= 2;
-    }
-    return false;
-}
-
-static bool
-SupportsBGRA(gl::GLContext* gl)
-{
-    if (gl->IsANGLE())
-        return true;
-
-    return false;
-}
-
-/*static*/ bool
-TexUnpackSurface::UploadDataSurface(bool isSubImage, WebGLContext* webgl,
-                                    TexImageTarget target, GLint level,
-                                    const webgl::DriverUnpackInfo* dui, GLint xOffset,
-                                    GLint yOffset, GLint zOffset, GLsizei width,
-                                    GLsizei height, gfx::DataSourceSurface* surf,
-                                    bool isSurfAlphaPremult, GLenum* const out_glError)
-{
-    gl::GLContext* gl = webgl->GL();
-    MOZ_ASSERT(gl->IsCurrent());
-    *out_glError = 0;
-
-    if (isSurfAlphaPremult != webgl->mPixelStore_PremultiplyAlpha)
-        return false;
-
-    gl::OriginPos srcOrigin, dstOrigin;
-    OriginsForDOM(webgl, &srcOrigin, &dstOrigin);
-    if (srcOrigin != dstOrigin)
-        return false;
-
-    // This differs from the raw-data upload in that we choose how we do the unpack.
-    // (alignment, etc.)
-
-    // Uploading RGBX as RGBA and blitting to RGB is faster than repacking RGBX into
-    // RGB on the CPU. However, this is optimization is out-of-scope for now.
-
-    static const webgl::DriverUnpackInfo kInfoBGRA = {
-        LOCAL_GL_BGRA,
-        LOCAL_GL_BGRA,
-        LOCAL_GL_UNSIGNED_BYTE,
-    };
-
-    const webgl::DriverUnpackInfo* chosenDUI = nullptr;
-
-    switch (surf->GetFormat()) {
-    case gfx::SurfaceFormat::B8G8R8A8:
-        if (SupportsBGRA(gl) &&
-            dui->internalFormat == LOCAL_GL_RGBA &&
-            dui->unpackFormat == LOCAL_GL_RGBA &&
-            dui->unpackType == LOCAL_GL_UNSIGNED_BYTE)
-        {
-            chosenDUI = &kInfoBGRA;
-        }
-        break;
-
-    case gfx::SurfaceFormat::R8G8B8A8:
-        if (dui->unpackFormat == LOCAL_GL_RGBA &&
-            dui->unpackType == LOCAL_GL_UNSIGNED_BYTE)
-        {
-            chosenDUI = dui;
-        }
-        break;
-
-    case gfx::SurfaceFormat::R5G6B5_UINT16:
-        if (dui->unpackFormat == LOCAL_GL_RGB &&
-            dui->unpackType == LOCAL_GL_UNSIGNED_SHORT_5_6_5)
-        {
-            chosenDUI = dui;
-        }
-        break;
-
-    default:
-        break;
-    }
-
-    if (!chosenDUI)
-        return false;
-
-    gfx::DataSourceSurface::ScopedMap map(surf, gfx::DataSourceSurface::MapType::READ);
-    if (!map.IsMapped())
-        return false;
-
-    const webgl::PackingInfo pi = {chosenDUI->unpackFormat, chosenDUI->unpackType};
-    const auto bytesPerPixel = webgl::BytesPerPixel(pi);
-    const size_t bytesPerRow = width * bytesPerPixel;
-
-    const GLint kMaxUnpackAlignment = 8;
-    size_t unpackAlignment;
-    if (!GuessAlignment(map.GetData(), bytesPerRow, map.GetStride(), kMaxUnpackAlignment,
-                        &unpackAlignment))
-    {
-        return false;
-        // TODO: Consider using UNPACK_ settings to set the stride based on the too-large
-        // alignment used for some SourceSurfaces. (D2D allegedy likes alignment=16)
-    }
-
-    MOZ_ALWAYS_TRUE( webgl->gl->MakeCurrent() );
-    ScopedUnpackReset scopedReset(webgl);
-    gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, unpackAlignment);
-
-    const GLsizei depth = 1;
-    GLenum error = DoTexOrSubImage(isSubImage, gl, target.get(), level, chosenDUI,
-                                   xOffset, yOffset, zOffset, width, height, depth,
-                                   map.GetData());
-    if (error) {
-        *out_glError = error;
-        return false;
-    }
-
-    return true;
-}
-
-////////////////////
-
-static bool
-GetFormatForSurf(gfx::SourceSurface* surf, WebGLTexelFormat* const out_texelFormat)
-{
-    gfx::SurfaceFormat surfFormat = surf->GetFormat();
-
-    switch (surfFormat) {
-    case gfx::SurfaceFormat::B8G8R8A8:
-        *out_texelFormat = WebGLTexelFormat::BGRA8;
-        return true;
-
-    case gfx::SurfaceFormat::B8G8R8X8:
-        *out_texelFormat = WebGLTexelFormat::BGRX8;
-        return true;
-
-    case gfx::SurfaceFormat::R8G8B8A8:
-        *out_texelFormat = WebGLTexelFormat::RGBA8;
-        return true;
-
-    case gfx::SurfaceFormat::R8G8B8X8:
-        *out_texelFormat = WebGLTexelFormat::RGBX8;
-        return true;
-
-    case gfx::SurfaceFormat::R5G6B5_UINT16:
-        *out_texelFormat = WebGLTexelFormat::RGB565;
-        return true;
-
-    case gfx::SurfaceFormat::A8:
-        *out_texelFormat = WebGLTexelFormat::A8;
-        return true;
-
-    case gfx::SurfaceFormat::YUV:
-        // Ugh...
-        NS_ERROR("We don't handle uploads from YUV sources yet.");
-        // When we want to, check out gfx/ycbcr/YCbCrUtils.h. (specifically
-        // GetYCbCrToRGBDestFormatAndSize and ConvertYCbCrToRGB)
-        return false;
-
-    default:
-        return false;
-    }
-}
-
-static bool
-GetFormatForPackingTuple(GLenum packingFormat, GLenum packingType,
-                         WebGLTexelFormat* const out_texelFormat)
-{
-    switch (packingType) {
-    case LOCAL_GL_UNSIGNED_BYTE:
-        switch (packingFormat) {
-        case LOCAL_GL_RED:
-        case LOCAL_GL_LUMINANCE:
-            *out_texelFormat = WebGLTexelFormat::R8;
-            return true;
-
-        case LOCAL_GL_ALPHA:
-            *out_texelFormat = WebGLTexelFormat::A8;
-            return true;
-
-        case LOCAL_GL_LUMINANCE_ALPHA:
-            *out_texelFormat = WebGLTexelFormat::RA8;
-            return true;
-
-        case LOCAL_GL_RGB:
-            *out_texelFormat = WebGLTexelFormat::RGB8;
-            return true;
-
-        case LOCAL_GL_RGBA:
-            *out_texelFormat = WebGLTexelFormat::RGBA8;
-            return true;
-
-        default:
-            break;
-        }
-        break;
-
-    case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
-        switch (packingFormat) {
-        case LOCAL_GL_RGB:
-            *out_texelFormat = WebGLTexelFormat::RGB565;
-            return true;
-
-        default:
-            break;
-        }
-        break;
-
-    case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
-        switch (packingFormat) {
-        case LOCAL_GL_RGBA:
-            *out_texelFormat = WebGLTexelFormat::RGBA5551;
-            return true;
-
-        default:
-            break;
-        }
-        break;
-
-    case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
-        switch (packingFormat) {
-        case LOCAL_GL_RGBA:
-            *out_texelFormat = WebGLTexelFormat::RGBA4444;
-            return true;
-
-        default:
-            break;
-        }
-        break;
-
-    case LOCAL_GL_HALF_FLOAT:
-    case LOCAL_GL_HALF_FLOAT_OES:
-        switch (packingFormat) {
-        case LOCAL_GL_RED:
-        case LOCAL_GL_LUMINANCE:
-            *out_texelFormat = WebGLTexelFormat::R16F;
-            return true;
-
-        case LOCAL_GL_ALPHA:
-            *out_texelFormat = WebGLTexelFormat::A16F;
-            return true;
-
-        case LOCAL_GL_LUMINANCE_ALPHA:
-            *out_texelFormat = WebGLTexelFormat::RA16F;
-            return true;
-
-        case LOCAL_GL_RGB:
-            *out_texelFormat = WebGLTexelFormat::RGB16F;
-            return true;
-
-        case LOCAL_GL_RGBA:
-            *out_texelFormat = WebGLTexelFormat::RGBA16F;
-            return true;
-
-        default:
-            break;
-        }
-        break;
-
-    case LOCAL_GL_FLOAT:
-        switch (packingFormat) {
-        case LOCAL_GL_RED:
-        case LOCAL_GL_LUMINANCE:
-            *out_texelFormat = WebGLTexelFormat::R32F;
-            return true;
-
-        case LOCAL_GL_ALPHA:
-            *out_texelFormat = WebGLTexelFormat::A32F;
-            return true;
-
-        case LOCAL_GL_LUMINANCE_ALPHA:
-            *out_texelFormat = WebGLTexelFormat::RA32F;
-            return true;
-
-        case LOCAL_GL_RGB:
-            *out_texelFormat = WebGLTexelFormat::RGB32F;
-            return true;
-
-        case LOCAL_GL_RGBA:
-            *out_texelFormat = WebGLTexelFormat::RGBA32F;
-            return true;
-
-        default:
-            break;
-        }
-        break;
-
-    default:
-        break;
-    }
-
-    NS_ERROR("Unsupported EffectiveFormat dest format for DOM element upload.");
-    return false;
-}
-
-/*static*/ bool
-TexUnpackSurface::ConvertSurface(WebGLContext* webgl, const webgl::DriverUnpackInfo* dui,
-                                 gfx::DataSourceSurface* surf, bool isSurfAlphaPremult,
-                                 UniqueBuffer* const out_convertedBuffer,
-                                 uint8_t* const out_convertedAlignment,
-                                 bool* const out_outOfMemory)
-{
-    *out_outOfMemory = false;
-
-    const size_t width = surf->GetSize().width;
-    const size_t height = surf->GetSize().height;
-
-    // Source args:
-
-    // After we call this, on OSX, our GLContext will no longer be current.
-    gfx::DataSourceSurface::ScopedMap srcMap(surf, gfx::DataSourceSurface::MapType::READ);
-    if (!srcMap.IsMapped())
-        return false;
-
-    const void* const srcBegin = srcMap.GetData();
-    const size_t srcStride = srcMap.GetStride();
-
-    WebGLTexelFormat srcFormat;
-    if (!GetFormatForSurf(surf, &srcFormat))
-        return false;
-
-    const bool srcPremultiplied = isSurfAlphaPremult;
-
-    // Dest args:
-
-    WebGLTexelFormat dstFormat;
-    if (!GetFormatForPackingTuple(dui->unpackFormat, dui->unpackType, &dstFormat))
-        return false;
-
-    const auto bytesPerPixel = webgl::BytesPerPixel({dui->unpackFormat, dui->unpackType});
-    const size_t dstRowBytes = bytesPerPixel * width;
-
-    const size_t dstAlignment = 8; // Just use the max!
-    const size_t dstStride = RoundUpToMultipleOf(dstRowBytes, dstAlignment);
-
-    CheckedUint32 checkedDstSize = dstStride;
-    checkedDstSize *= height;
-    if (!checkedDstSize.isValid()) {
-        *out_outOfMemory = true;
-        return false;
-    }
-
-    const size_t dstSize = checkedDstSize.value();
-
-    UniqueBuffer dstBuffer = malloc(dstSize);
-    if (!dstBuffer) {
-        *out_outOfMemory = true;
-        return false;
-    }
-    void* const dstBegin = dstBuffer.get();
-
-    gl::OriginPos srcOrigin, dstOrigin;
-    OriginsForDOM(webgl, &srcOrigin, &dstOrigin);
-
-    const bool dstPremultiplied = webgl->mPixelStore_PremultiplyAlpha;
-
-    // And go!:
-    if (!ConvertImage(width, height,
-                      srcBegin, srcStride, srcOrigin, srcFormat, srcPremultiplied,
-                      dstBegin, dstStride, dstOrigin, dstFormat, dstPremultiplied))
-    {
-        MOZ_ASSERT(false, "ConvertImage failed unexpectedly.");
-        NS_ERROR("ConvertImage failed unexpectedly.");
-        *out_outOfMemory = true;
-        return false;
-    }
-
-    *out_convertedBuffer = Move(dstBuffer);
-    *out_convertedAlignment = dstAlignment;
-    return true;
-}
-
-
-////////////////////
-
-TexUnpackSurface::TexUnpackSurface(const RefPtr<gfx::SourceSurface>& surf,
-                                   bool isAlphaPremult)
-    : TexUnpackBlob(surf->GetSize().width, surf->GetSize().height, 1, true)
-    , mSurf(surf)
-    , mIsAlphaPremult(isAlphaPremult)
-{ }
-
-TexUnpackSurface::~TexUnpackSurface()
-{ }
-
-void
-TexUnpackSurface::TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName,
-                                WebGLTexture* tex, TexImageTarget target, GLint level,
-                                const webgl::DriverUnpackInfo* dui, GLint xOffset,
-                                GLint yOffset, GLint zOffset, GLenum* const out_glError)
-{
-    *out_glError = 0;
-
-    WebGLContext* webgl = tex->mContext;
-
-    // MakeCurrent is a big mess in here, because mapping (and presumably unmapping) on
-    // OSX can lose our MakeCurrent. Therefore it's easiest to MakeCurrent just before we
-    // call into GL, instead of trying to keep MakeCurrent-ed.
-
-    RefPtr<gfx::DataSourceSurface> dataSurf = mSurf->GetDataSurface();
-    MOZ_ASSERT(dataSurf);
-
-    GLenum error;
-    if (UploadDataSurface(isSubImage, webgl, target, level, dui, xOffset, yOffset,
-                          zOffset, mWidth, mHeight, dataSurf, mIsAlphaPremult, &error))
-    {
-        return;
-    }
-    if (error == LOCAL_GL_OUT_OF_MEMORY) {
-        *out_glError = LOCAL_GL_OUT_OF_MEMORY;
-        return;
-    }
-
-    // CPU conversion. (++numCopies)
-
-    UniqueBuffer convertedBuffer;
-    uint8_t convertedAlignment;
-    bool outOfMemory;
-    if (!ConvertSurface(webgl, dui, dataSurf, mIsAlphaPremult, &convertedBuffer,
-                        &convertedAlignment, &outOfMemory))
-    {
-        if (outOfMemory) {
-            *out_glError = LOCAL_GL_OUT_OF_MEMORY;
-        } else {
-            NS_ERROR("Failed to convert surface.");
-            *out_glError = LOCAL_GL_OUT_OF_MEMORY;
-        }
-        return;
-    }
-
-    MOZ_ALWAYS_TRUE( webgl->gl->MakeCurrent() );
-    ScopedUnpackReset scopedReset(webgl);
-    webgl->gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, convertedAlignment);
-
-    error = DoTexOrSubImage(isSubImage, webgl->gl, target.get(), level, dui, xOffset,
-                            yOffset, zOffset, mWidth, mHeight, mDepth,
-                            convertedBuffer.get());
-    *out_glError = error;
-}
-
-} // namespace webgl
-} // namespace mozilla
deleted file mode 100644
--- a/dom/canvas/TexUnpackBlob.h
+++ /dev/null
@@ -1,162 +0,0 @@
-/* -*- 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/. */
-
-#ifndef TEX_UNPACK_BLOB_H_
-#define TEX_UNPACK_BLOB_H_
-
-#include "GLContextTypes.h"
-#include "GLTypes.h"
-#include "WebGLStrongTypes.h"
-
-
-template <class T>
-class RefPtr;
-
-namespace mozilla {
-
-class UniqueBuffer;
-class WebGLContext;
-class WebGLTexture;
-
-namespace dom {
-class Element;
-class HTMLCanvasElement;
-class HTMLVideoElement;
-} // namespace dom
-
-namespace gfx {
-class DataSourceSurface;
-} // namespace gfx
-
-namespace gl {
-class GLContext;
-} // namespace gl
-
-namespace layers {
-class Image;
-class ImageContainer;
-} // namespace layers
-
-namespace webgl {
-
-struct PackingInfo;
-struct DriverUnpackInfo;
-
-class TexUnpackBlob
-{
-public:
-    const GLsizei mWidth;
-    const GLsizei mHeight;
-    const GLsizei mDepth;
-    const bool mHasData;
-
-protected:
-    TexUnpackBlob(GLsizei width, GLsizei height, GLsizei depth, bool hasData)
-        : mWidth(width)
-        , mHeight(height)
-        , mDepth(depth)
-        , mHasData(hasData)
-    { }
-
-public:
-    virtual ~TexUnpackBlob() {}
-
-    virtual bool ValidateUnpack(WebGLContext* webgl, const char* funcName, bool isFunc3D,
-                                const webgl::PackingInfo& pi) = 0;
-
-    virtual void TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName,
-                               WebGLTexture* tex, TexImageTarget target, GLint level,
-                               const webgl::DriverUnpackInfo* dui, GLint xOffset,
-                               GLint yOffset, GLint zOffset,
-                               GLenum* const out_glError) = 0;
-
-    static void OriginsForDOM(WebGLContext* webgl, gl::OriginPos* const out_src,
-                              gl::OriginPos* const out_dst);
-};
-
-class TexUnpackBytes : public TexUnpackBlob
-{
-public:
-    const size_t mByteCount;
-    const void* const mBytes;
-
-    TexUnpackBytes(GLsizei width, GLsizei height, GLsizei depth, size_t byteCount,
-                    const void* bytes)
-        : TexUnpackBlob(width, height, depth, bool(bytes))
-        , mByteCount(byteCount)
-        , mBytes(bytes)
-    { }
-
-    virtual bool ValidateUnpack(WebGLContext* webgl, const char* funcName, bool isFunc3D,
-                                const webgl::PackingInfo& pi) override;
-
-    virtual void TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName,
-                               WebGLTexture* tex, TexImageTarget target, GLint level,
-                               const webgl::DriverUnpackInfo* dui, GLint xOffset,
-                               GLint yOffset, GLint zOffset,
-                               GLenum* const out_glError) override;
-};
-
-class TexUnpackImage : public TexUnpackBlob
-{
-public:
-    const RefPtr<layers::Image> mImage;
-    const bool mIsAlphaPremult;
-
-    TexUnpackImage(const RefPtr<layers::Image>& image, bool isAlphaPremult);
-    virtual ~TexUnpackImage() override;
-
-    virtual bool ValidateUnpack(WebGLContext* webgl, const char* funcName, bool isFunc3D,
-                                const webgl::PackingInfo& pi) override
-    {
-        return true;
-    }
-
-    virtual void TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName,
-                               WebGLTexture* tex, TexImageTarget target, GLint level,
-                               const webgl::DriverUnpackInfo* dui, GLint xOffset,
-                               GLint yOffset, GLint zOffset,
-                               GLenum* const out_glError) override;
-};
-
-class TexUnpackSurface : public TexUnpackBlob
-{
-public:
-    const RefPtr<gfx::SourceSurface> mSurf;
-    const bool mIsAlphaPremult;
-
-    TexUnpackSurface(const RefPtr<gfx::SourceSurface>& surf, bool isAlphaPremult);
-    virtual ~TexUnpackSurface() override;
-
-    virtual bool ValidateUnpack(WebGLContext* webgl, const char* funcName, bool isFunc3D,
-                                const webgl::PackingInfo& pi) override
-    {
-        return true;
-    }
-
-    virtual void TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName,
-                               WebGLTexture* tex, TexImageTarget target, GLint level,
-                               const webgl::DriverUnpackInfo* dui, GLint xOffset,
-                               GLint yOffset, GLint zOffset,
-                               GLenum* const out_glError) override;
-
-protected:
-    static bool ConvertSurface(WebGLContext* webgl, const webgl::DriverUnpackInfo* dui,
-                               gfx::DataSourceSurface* surf, bool isSurfAlphaPremult,
-                               UniqueBuffer* const out_convertedBuffer,
-                               uint8_t* const out_convertedAlignment,
-                               bool* const out_outOfMemory);
-    static bool UploadDataSurface(bool isSubImage, WebGLContext* webgl,
-                                  TexImageTarget target, GLint level,
-                                  const webgl::DriverUnpackInfo* dui, GLint xOffset,
-                                  GLint yOffset, GLint zOffset, GLsizei width,
-                                  GLsizei height, gfx::DataSourceSurface* surf,
-                                  bool isSurfAlphaPremult, GLenum* const out_glError);
-};
-
-} // namespace webgl
-} // namespace mozilla
-
-#endif // TEX_UNPACK_BLOB_H_
--- a/dom/canvas/WebGL1Context.cpp
+++ b/dom/canvas/WebGL1Context.cpp
@@ -22,19 +22,19 @@ WebGL1Context::WebGL1Context()
 {
 }
 
 WebGL1Context::~WebGL1Context()
 {
 }
 
 UniquePtr<webgl::FormatUsageAuthority>
-WebGL1Context::CreateFormatUsage(gl::GLContext* gl) const
+WebGL1Context::CreateFormatUsage() const
 {
-    return webgl::FormatUsageAuthority::CreateForWebGL1(gl);
+    return webgl::FormatUsageAuthority::CreateForWebGL1();
 }
 
 JSObject*
 WebGL1Context::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto)
 {
     return dom::WebGLRenderingContextBinding::Wrap(cx, this, givenProto);
 }
 
--- a/dom/canvas/WebGL1Context.h
+++ b/dom/canvas/WebGL1Context.h
@@ -13,19 +13,17 @@ namespace mozilla {
 class WebGL1Context
     : public WebGLContext
 {
 public:
     static WebGL1Context* Create();
 
 private:
     WebGL1Context();
-
-    virtual UniquePtr<webgl::FormatUsageAuthority>
-    CreateFormatUsage(gl::GLContext* gl) const override;
+    virtual UniquePtr<webgl::FormatUsageAuthority> CreateFormatUsage() const override;
 
 public:
     virtual ~WebGL1Context();
 
     virtual bool IsWebGL2() const override {
         return false;
     }
 
--- a/dom/canvas/WebGL2Context.cpp
+++ b/dom/canvas/WebGL2Context.cpp
@@ -24,19 +24,19 @@ WebGL2Context::WebGL2Context()
 }
 
 WebGL2Context::~WebGL2Context()
 {
 
 }
 
 UniquePtr<webgl::FormatUsageAuthority>
-WebGL2Context::CreateFormatUsage(gl::GLContext* gl) const
+WebGL2Context::CreateFormatUsage() const
 {
-    return webgl::FormatUsageAuthority::CreateForWebGL2(gl);
+    return webgl::FormatUsageAuthority::CreateForWebGL2();
 }
 
 /*static*/ bool
 WebGL2Context::IsSupported()
 {
     return Preferences::GetBool("webgl.enable-prototype-webgl2", false);
 }
 
@@ -50,16 +50,30 @@ JSObject*
 WebGL2Context::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto)
 {
     return dom::WebGL2RenderingContextBinding::Wrap(cx, this, givenProto);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // WebGL 2 initialisation
 
+// These WebGL 1 extensions are natively supported by WebGL 2.
+static const WebGLExtensionID kNativelySupportedExtensions[] = {
+    WebGLExtensionID::ANGLE_instanced_arrays,
+    WebGLExtensionID::EXT_blend_minmax,
+    WebGLExtensionID::EXT_sRGB,
+    WebGLExtensionID::OES_element_index_uint,
+    WebGLExtensionID::OES_standard_derivatives,
+    WebGLExtensionID::OES_texture_float_linear,
+    WebGLExtensionID::OES_texture_half_float_linear,
+    WebGLExtensionID::OES_vertex_array_object,
+    WebGLExtensionID::WEBGL_depth_texture,
+    WebGLExtensionID::WEBGL_draw_buffers
+};
+
 static const gl::GLFeature kRequiredFeatures[] = {
     gl::GLFeature::blend_minmax,
     gl::GLFeature::clear_buffers,
     gl::GLFeature::copy_buffer,
     gl::GLFeature::depth_texture,
     gl::GLFeature::draw_instanced,
     gl::GLFeature::draw_range_elements,
     gl::GLFeature::element_index_uint,
@@ -132,31 +146,41 @@ WebGLContext::InitWebGL2()
             exts.AppendLiteral("\n  ");
             exts.Append(gl::GLContext::GetFeatureName(*itr));
         }
         GenerateWarning("WebGL 2 unavailable. The following required features are"
                         " unavailible: %s", exts.BeginReading());
         return false;
     }
 
+    // ok WebGL 2 is compatible, we can enable natively supported extensions.
+    for (size_t i = 0; i < ArrayLength(kNativelySupportedExtensions); i++) {
+        EnableExtension(kNativelySupportedExtensions[i]);
+
+        MOZ_ASSERT(IsExtensionEnabled(kNativelySupportedExtensions[i]));
+    }
+
     // we initialise WebGL 2 related stuff.
     gl->GetUIntegerv(LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS,
                      &mGLMaxTransformFeedbackSeparateAttribs);
     gl->GetUIntegerv(LOCAL_GL_MAX_UNIFORM_BUFFER_BINDINGS,
                      &mGLMaxUniformBufferBindings);
 
+    if (MinCapabilityMode()) {
+        mGLMax3DTextureSize = MINVALUE_GL_MAX_3D_TEXTURE_SIZE;
+        mGLMaxArrayTextureLayers = MINVALUE_GL_MAX_ARRAY_TEXTURE_LAYERS;
+    } else {
+        gl->fGetIntegerv(LOCAL_GL_MAX_3D_TEXTURE_SIZE,
+                         (GLint*) &mGLMax3DTextureSize);
+        gl->fGetIntegerv(LOCAL_GL_MAX_ARRAY_TEXTURE_LAYERS,
+                         (GLint*) &mGLMaxArrayTextureLayers);
+    }
+
     mBoundTransformFeedbackBuffers.SetLength(mGLMaxTransformFeedbackSeparateAttribs);
     mBoundUniformBuffers.SetLength(mGLMaxUniformBufferBindings);
 
     mDefaultTransformFeedback = new WebGLTransformFeedback(this, 0);
     mBoundTransformFeedback = mDefaultTransformFeedback;
 
-    if (!gl->IsGLES()) {
-        // Desktop OpenGL requires the following to be enabled in order to
-        // support sRGB operations on framebuffers.
-        gl->MakeCurrent();
-        gl->fEnable(LOCAL_GL_FRAMEBUFFER_SRGB_EXT);
-    }
-
     return true;
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGL2Context.h
+++ b/dom/canvas/WebGL2Context.h
@@ -3,16 +3,22 @@
  * 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 WEBGL2CONTEXT_H_
 #define WEBGL2CONTEXT_H_
 
 #include "WebGLContext.h"
 
+/*
+ * Minimum value constants define in 6.2 State Tables of OpenGL ES - 3.0.4
+ */
+#define MINVALUE_GL_MAX_3D_TEXTURE_SIZE             256
+#define MINVALUE_GL_MAX_ARRAY_TEXTURE_LAYERS        256
+
 namespace mozilla {
 
 class ErrorResult;
 class WebGLSampler;
 class WebGLSync;
 class WebGLTransformFeedback;
 class WebGLVertexArrayObject;
 namespace dom {
@@ -85,57 +91,47 @@ public:
                                     ErrorResult& rv);
     void RenderbufferStorageMultisample(GLenum target, GLsizei samples, GLenum internalformat,
                                         GLsizei width, GLsizei height);
 
 
     // -------------------------------------------------------------------------
     // Texture objects - WebGL2ContextTextures.cpp
 
-    void TexStorage2D(GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width,
-                      GLsizei height);
-    void TexStorage3D(GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width,
-                      GLsizei height, GLsizei depth);
-    void TexImage3D(GLenum target, GLint level, GLenum internalFormat, GLsizei width,
-                    GLsizei height, GLsizei depth, GLint border, GLenum unpackFormat,
-                    GLenum unpackType,
-                    const dom::Nullable<dom::ArrayBufferViewOrSharedArrayBufferView>& pixels);
-    void TexSubImage3D(GLenum target, GLint level, GLint xOffset, GLint yOffset,
-                       GLint zOffset, GLsizei width, GLsizei height, GLsizei depth,
-                       GLenum unpackFormat, GLenum unpackType,
-                       const dom::Nullable<dom::ArrayBufferViewOrSharedArrayBufferView>& pixels,
-                       ErrorResult& out_rv);
-    void TexSubImage3D(GLenum target, GLint level, GLint xOffset, GLint yOffset,
-                       GLint zOffset, GLenum unpackFormat, GLenum unpackType,
-                       dom::ImageData* data, ErrorResult& out_rv);
-protected:
-    void TexSubImage3D(GLenum target, GLint level, GLint xOffset, GLint yOffset,
-                       GLint zOffset, GLenum unpackFormat, GLenum unpackType,
-                       dom::Element* elem, ErrorResult* const out_rv);
-public:
-    template<class T>
-    inline void
-    TexSubImage3D(GLenum target, GLint level, GLint xOffset, GLint yOffset, GLint zOffset,
-                  GLenum unpackFormat, GLenum unpackType, T& elem, ErrorResult& out_rv)
-    {
-        TexSubImage3D(target, level, xOffset, yOffset, zOffset, unpackFormat, unpackType,
-                      &elem, &out_rv);
-    }
+    void TexStorage2D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height);
+    void TexStorage3D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height,
+                      GLsizei depth);
+    void TexImage3D(GLenum target, GLint level, GLenum internalformat,
+                    GLsizei width, GLsizei height, GLsizei depth,
+                    GLint border, GLenum format, GLenum type,
+                    const dom::Nullable<dom::ArrayBufferViewOrSharedArrayBufferView>& pixels,
+                    ErrorResult& rv);
+    void TexSubImage3D(GLenum target, GLint level,
+                       GLint xoffset, GLint yoffset, GLint zoffset,
+                       GLsizei width, GLsizei height, GLsizei depth,
+                       GLenum format, GLenum type, const dom::Nullable<dom::ArrayBufferViewOrSharedArrayBufferView>& pixels,
+                       ErrorResult& rv);
+    void TexSubImage3D(GLenum target, GLint level,
+                       GLint xoffset, GLint yoffset, GLint zoffset,
+                       GLenum format, GLenum type, dom::ImageData* data,
+                       ErrorResult& rv);
+    template<class ElementType>
+    void TexSubImage3D(GLenum target, GLint level,
+                       GLint xoffset, GLint yoffset, GLint zoffset,
+                       GLenum format, GLenum type, ElementType& elt, ErrorResult& rv)
+    {}
 
-    void CopyTexSubImage3D(GLenum target, GLint level, GLint xOffset, GLint yOffset,
-                           GLint zOffset, GLint x, GLint y, GLsizei width,
-                           GLsizei height);
-    void CompressedTexImage3D(GLenum target, GLint level, GLenum internalFormat,
+    void CopyTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset,
+                           GLint x, GLint y, GLsizei width, GLsizei height);
+    void CompressedTexImage3D(GLenum target, GLint level, GLenum internalformat,
                               GLsizei width, GLsizei height, GLsizei depth,
-                              GLint border,
-                              const dom::ArrayBufferViewOrSharedArrayBufferView& data);
-    void CompressedTexSubImage3D(GLenum target, GLint level, GLint xOffset, GLint yOffset,
-                                 GLint zOffset, GLsizei width, GLsizei height,
-                                 GLsizei depth, GLenum sizedUnpackFormat,
-                                 const dom::ArrayBufferViewOrSharedArrayBufferView& data);
+                              GLint border, GLsizei imageSize, const dom::ArrayBufferViewOrSharedArrayBufferView& data);
+    void CompressedTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset,
+                                 GLsizei width, GLsizei height, GLsizei depth,
+                                 GLenum format, GLsizei imageSize, const dom::ArrayBufferViewOrSharedArrayBufferView& data);
 
 
     // -------------------------------------------------------------------------
     // Programs and shaders - WebGL2ContextPrograms.cpp
     GLint GetFragDataLocation(WebGLProgram* program, const nsAString& name);
 
 
     // -------------------------------------------------------------------------
@@ -382,18 +378,17 @@ public:
     already_AddRefed<WebGLVertexArrayObject> CreateVertexArray();
     void DeleteVertexArray(WebGLVertexArrayObject* vertexArray);
     bool IsVertexArray(WebGLVertexArrayObject* vertexArray);
     void BindVertexArray(WebGLVertexArrayObject* vertexArray);
 */
 
 private:
     WebGL2Context();
-    virtual UniquePtr<webgl::FormatUsageAuthority>
-    CreateFormatUsage(gl::GLContext* gl) const override;
+    virtual UniquePtr<webgl::FormatUsageAuthority> CreateFormatUsage() const override;
 
     virtual bool IsTexParamValid(GLenum pname) const override;
 
     void UpdateBoundQuery(GLenum target, WebGLQuery* query);
 
     // CreateVertexArrayImpl is assumed to be infallible.
     virtual WebGLVertexArray* CreateVertexArrayImpl() override;
     virtual bool ValidateAttribPointerType(bool integerMode, GLenum type, GLsizei* alignment, const char* info) override;
--- a/dom/canvas/WebGL2ContextFramebuffers.cpp
+++ b/dom/canvas/WebGL2ContextFramebuffers.cpp
@@ -8,83 +8,156 @@
 #include "GLContext.h"
 #include "GLScreenBuffer.h"
 #include "WebGLContextUtils.h"
 #include "WebGLFormats.h"
 #include "WebGLFramebuffer.h"
 
 namespace mozilla {
 
+using gl::GLContext;
+using gl::GLFormats;
+using webgl::EffectiveFormat;
+using webgl::FormatInfo;
+using webgl::ComponentType;
+
+// Returns one of FLOAT, INT, UNSIGNED_INT.
+// Fixed-points (normalized ints) are considered FLOAT.
+static GLenum
+ValueTypeForFormat(GLenum internalFormat)
+{
+    switch (internalFormat) {
+    // Fixed-point
+    case LOCAL_GL_R8:
+    case LOCAL_GL_RG8:
+    case LOCAL_GL_RGB565:
+    case LOCAL_GL_RGB8:
+    case LOCAL_GL_RGBA4:
+    case LOCAL_GL_RGB5_A1:
+    case LOCAL_GL_RGBA8:
+    case LOCAL_GL_RGB10_A2:
+    case LOCAL_GL_ALPHA8:
+    case LOCAL_GL_LUMINANCE8:
+    case LOCAL_GL_LUMINANCE8_ALPHA8:
+    case LOCAL_GL_SRGB8:
+    case LOCAL_GL_SRGB8_ALPHA8:
+    case LOCAL_GL_R8_SNORM:
+    case LOCAL_GL_RG8_SNORM:
+    case LOCAL_GL_RGB8_SNORM:
+    case LOCAL_GL_RGBA8_SNORM:
+
+    // Floating-point
+    case LOCAL_GL_R16F:
+    case LOCAL_GL_RG16F:
+    case LOCAL_GL_RGB16F:
+    case LOCAL_GL_RGBA16F:
+    case LOCAL_GL_ALPHA16F_EXT:
+    case LOCAL_GL_LUMINANCE16F_EXT:
+    case LOCAL_GL_LUMINANCE_ALPHA16F_EXT:
+
+    case LOCAL_GL_R32F:
+    case LOCAL_GL_RG32F:
+    case LOCAL_GL_RGB32F:
+    case LOCAL_GL_RGBA32F:
+    case LOCAL_GL_ALPHA32F_EXT:
+    case LOCAL_GL_LUMINANCE32F_EXT:
+    case LOCAL_GL_LUMINANCE_ALPHA32F_EXT:
+
+    case LOCAL_GL_R11F_G11F_B10F:
+    case LOCAL_GL_RGB9_E5:
+        return LOCAL_GL_FLOAT;
+
+    // Int
+    case LOCAL_GL_R8I:
+    case LOCAL_GL_RG8I:
+    case LOCAL_GL_RGB8I:
+    case LOCAL_GL_RGBA8I:
+
+    case LOCAL_GL_R16I:
+    case LOCAL_GL_RG16I:
+    case LOCAL_GL_RGB16I:
+    case LOCAL_GL_RGBA16I:
+
+    case LOCAL_GL_R32I:
+    case LOCAL_GL_RG32I:
+    case LOCAL_GL_RGB32I:
+    case LOCAL_GL_RGBA32I:
+        return LOCAL_GL_INT;
+
+    // Unsigned int
+    case LOCAL_GL_R8UI:
+    case LOCAL_GL_RG8UI:
+    case LOCAL_GL_RGB8UI:
+    case LOCAL_GL_RGBA8UI:
+
+    case LOCAL_GL_R16UI:
+    case LOCAL_GL_RG16UI:
+    case LOCAL_GL_RGB16UI:
+    case LOCAL_GL_RGBA16UI:
+
+    case LOCAL_GL_R32UI:
+    case LOCAL_GL_RG32UI:
+    case LOCAL_GL_RGB32UI:
+    case LOCAL_GL_RGBA32UI:
+
+    case LOCAL_GL_RGB10_A2UI:
+        return LOCAL_GL_UNSIGNED_INT;
+
+    default:
+        MOZ_CRASH("Bad `internalFormat`.");
+    }
+}
+
+// -------------------------------------------------------------------------
+// Framebuffer objects
+
 static bool
 GetFBInfoForBlit(const WebGLFramebuffer* fb, WebGLContext* webgl,
                  const char* const fbInfo, GLsizei* const out_samples,
-                 const webgl::FormatInfo** const out_colorFormat,
-                 const webgl::FormatInfo** const out_depthFormat,
-                 const webgl::FormatInfo** const out_stencilFormat)
+                 GLenum* const out_colorFormat, GLenum* const out_depthFormat,
+                 GLenum* const out_stencilFormat)
 {
     auto status = fb->PrecheckFramebufferStatus();
     if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
         webgl->ErrorInvalidOperation("blitFramebuffer: %s is not"
                                      " framebuffer-complete.", fbInfo);
         return false;
     }
 
     *out_samples = 1; // TODO
-    *out_colorFormat = nullptr;
-    *out_depthFormat = nullptr;
-    *out_stencilFormat = nullptr;
 
     if (fb->ColorAttachment(0).IsDefined()) {
-        const auto& attachment = fb->ColorAttachment(0);
-        *out_colorFormat = attachment.Format()->format;
+        const auto& attachement = fb->ColorAttachment(0);
+        *out_colorFormat = attachement.EffectiveInternalFormat().get();
+    } else {
+        *out_colorFormat = 0;
     }
 
     if (fb->DepthStencilAttachment().IsDefined()) {
-        const auto& attachment = fb->DepthStencilAttachment();
-        *out_depthFormat = attachment.Format()->format;
+        const auto& attachement = fb->DepthStencilAttachment();
+        *out_depthFormat = attachement.EffectiveInternalFormat().get();
         *out_stencilFormat = *out_depthFormat;
     } else {
         if (fb->DepthAttachment().IsDefined()) {
-            const auto& attachment = fb->DepthAttachment();
-            *out_depthFormat = attachment.Format()->format;
+            const auto& attachement = fb->DepthAttachment();
+            *out_depthFormat = attachement.EffectiveInternalFormat().get();
+        } else {
+            *out_depthFormat = 0;
         }
 
         if (fb->StencilAttachment().IsDefined()) {
-            const auto& attachment = fb->StencilAttachment();
-            *out_stencilFormat = attachment.Format()->format;
+            const auto& attachement = fb->StencilAttachment();
+            *out_stencilFormat = attachement.EffectiveInternalFormat().get();
+        } else {
+            *out_stencilFormat = 0;
         }
     }
     return true;
 }
 
-static void
-GetBackbufferFormats(const WebGLContextOptions& options,
-                     const webgl::FormatInfo** const out_color,
-                     const webgl::FormatInfo** const out_depth,
-                     const webgl::FormatInfo** const out_stencil)
-{
-    const auto effFormat = options.alpha ? webgl::EffectiveFormat::RGBA8
-                                          : webgl::EffectiveFormat::RGB8;
-    *out_color = webgl::GetFormat(effFormat);
-
-    *out_depth = nullptr;
-    *out_stencil = nullptr;
-    if (options.depth && options.stencil) {
-        *out_depth = webgl::GetFormat(webgl::EffectiveFormat::DEPTH24_STENCIL8);
-        *out_stencil = *out_depth;
-    } else {
-        if (options.depth) {
-            *out_depth = webgl::GetFormat(webgl::EffectiveFormat::DEPTH_COMPONENT16);
-        }
-        if (options.stencil) {
-            *out_stencil = webgl::GetFormat(webgl::EffectiveFormat::STENCIL_INDEX8);
-        }
-    }
-}
-
 void
 WebGL2Context::BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
                                GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
                                GLbitfield mask, GLenum filter)
 {
     const GLbitfield validBits = LOCAL_GL_COLOR_BUFFER_BIT |
                                  LOCAL_GL_DEPTH_BUFFER_BIT |
                                  LOCAL_GL_STENCIL_BUFFER_BIT;
@@ -118,82 +191,91 @@ WebGL2Context::BlitFramebuffer(GLint src
         // the underlying buffers are not the same, not the framebuffers
         // themselves.
         ErrorInvalidOperation("blitFramebuffer: Source and destination must"
                               " differ.");
         return;
     }
 
     GLsizei srcSamples;
-    const webgl::FormatInfo* srcColorFormat = nullptr;
-    const webgl::FormatInfo* srcDepthFormat = nullptr;
-    const webgl::FormatInfo* srcStencilFormat = nullptr;
+    GLenum srcColorFormat = 0;
+    GLenum srcDepthFormat = 0;
+    GLenum srcStencilFormat = 0;
 
     if (mBoundReadFramebuffer) {
         if (!GetFBInfoForBlit(mBoundReadFramebuffer, this, "READ_FRAMEBUFFER",
                               &srcSamples, &srcColorFormat, &srcDepthFormat,
                               &srcStencilFormat))
         {
             return;
         }
     } else {
         srcSamples = 1; // Always 1.
 
-        GetBackbufferFormats(mOptions, &srcColorFormat, &srcDepthFormat,
-                             &srcStencilFormat);
+        // TODO: Don't hardcode these.
+        srcColorFormat = mOptions.alpha ? LOCAL_GL_RGBA8 : LOCAL_GL_RGB8;
+
+        if (mOptions.depth && mOptions.stencil) {
+            srcDepthFormat = LOCAL_GL_DEPTH24_STENCIL8;
+            srcStencilFormat = srcDepthFormat;
+        } else {
+            if (mOptions.depth) {
+                srcDepthFormat = LOCAL_GL_DEPTH_COMPONENT16;
+            }
+            if (mOptions.stencil) {
+                srcStencilFormat = LOCAL_GL_STENCIL_INDEX8;
+            }
+        }
     }
 
     GLsizei dstSamples;
-    const webgl::FormatInfo* dstColorFormat = nullptr;
-    const webgl::FormatInfo* dstDepthFormat = nullptr;
-    const webgl::FormatInfo* dstStencilFormat = nullptr;
+    GLenum dstColorFormat = 0;
+    GLenum dstDepthFormat = 0;
+    GLenum dstStencilFormat = 0;
 
     if (mBoundDrawFramebuffer) {
         if (!GetFBInfoForBlit(mBoundDrawFramebuffer, this, "DRAW_FRAMEBUFFER",
                               &dstSamples, &dstColorFormat, &dstDepthFormat,
                               &dstStencilFormat))
         {
             return;
         }
     } else {
         dstSamples = gl->Screen()->Samples();
 
-        GetBackbufferFormats(mOptions, &dstColorFormat, &dstDepthFormat,
-                             &dstStencilFormat);
+        // TODO: Don't hardcode these.
+        dstColorFormat = mOptions.alpha ? LOCAL_GL_RGBA8 : LOCAL_GL_RGB8;
+
+        if (mOptions.depth && mOptions.stencil) {
+            dstDepthFormat = LOCAL_GL_DEPTH24_STENCIL8;
+            dstStencilFormat = dstDepthFormat;
+        } else {
+            if (mOptions.depth) {
+                dstDepthFormat = LOCAL_GL_DEPTH_COMPONENT16;
+            }
+            if (mOptions.stencil) {
+                dstStencilFormat = LOCAL_GL_STENCIL_INDEX8;
+            }
+        }
     }
 
+
     if (mask & LOCAL_GL_COLOR_BUFFER_BIT) {
-        const auto fnSignlessType = [](const webgl::FormatInfo* format)
-                                    -> webgl::ComponentType
-        {
-            if (!format)
-                return webgl::ComponentType::None;
-
-            switch (format->componentType) {
-            case webgl::ComponentType::UInt:
-                return webgl::ComponentType::Int;
-
-            case webgl::ComponentType::NormUInt:
-                return webgl::ComponentType::NormInt;
-
-            default:
-                return format->componentType;
-            }
-        };
-
-        const auto srcType = fnSignlessType(srcColorFormat);
-        const auto dstType = fnSignlessType(dstColorFormat);
-
-        if (srcType != dstType) {
-            ErrorInvalidOperation("blitFramebuffer: Color buffer format component type"
+        const GLenum srcColorType = srcColorFormat ? ValueTypeForFormat(srcColorFormat)
+                                                   : 0;
+        const GLenum dstColorType = dstColorFormat ? ValueTypeForFormat(dstColorFormat)
+                                                   : 0;
+        if (dstColorType != srcColorType) {
+            ErrorInvalidOperation("blitFramebuffer: Color buffer value type"
                                   " mismatch.");
             return;
         }
 
-        const bool srcIsInt = (srcType == webgl::ComponentType::Int);
+        const bool srcIsInt = srcColorType == LOCAL_GL_INT ||
+                              srcColorType == LOCAL_GL_UNSIGNED_INT;
         if (srcIsInt && filter != LOCAL_GL_NEAREST) {
             ErrorInvalidOperation("blitFramebuffer: Integer read buffers can only"
                                   " be filtered with NEAREST.");
             return;
         }
     }
 
     /* GLES 3.0.4, p199:
@@ -291,35 +373,38 @@ WebGL2Context::FramebufferTextureLayer(G
         if (texture->IsDeleted()) {
             return ErrorInvalidValue("framebufferTextureLayer: texture must be a valid "
                                      "texture object.");
         }
 
         if (level < 0)
             return ErrorInvalidValue("framebufferTextureLayer: layer must be >= 0.");
 
-        switch (texture->Target().get()) {
+        switch (texture->Target()) {
         case LOCAL_GL_TEXTURE_3D:
-            if (uint32_t(layer) >= mImplMax3DTextureSize) {
+            if ((GLuint) layer >= mGLMax3DTextureSize) {
                 return ErrorInvalidValue("framebufferTextureLayer: layer must be < "
                                          "MAX_3D_TEXTURE_SIZE");
             }
             break;
 
         case LOCAL_GL_TEXTURE_2D_ARRAY:
-            if (uint32_t(layer) >= mImplMaxArrayTextureLayers) {
+            if ((GLuint) layer >= mGLMaxArrayTextureLayers) {
                 return ErrorInvalidValue("framebufferTextureLayer: layer must be < "
                                          "MAX_ARRAY_TEXTURE_LAYERS");
             }
             break;
 
         default:
             return ErrorInvalidOperation("framebufferTextureLayer: texture must be an "
                                          "existing 3D texture, or a 2D texture array.");
         }
+    } else {
+        return ErrorInvalidOperation("framebufferTextureLayer: texture must be an "
+                                     "existing 3D texture, or a 2D texture array.");
     }
 
     WebGLFramebuffer* fb;
     switch (target) {
     case LOCAL_GL_FRAMEBUFFER:
     case LOCAL_GL_DRAW_FRAMEBUFFER:
         fb = mBoundDrawFramebuffer;
         break;
@@ -340,20 +425,106 @@ WebGL2Context::FramebufferTextureLayer(G
     fb->FramebufferTextureLayer(attachment, texture, level, layer);
 }
 
 JS::Value
 WebGL2Context::GetFramebufferAttachmentParameter(JSContext* cx,
                                                  GLenum target,
                                                  GLenum attachment,
                                                  GLenum pname,
-                                                 ErrorResult& out_error)
+                                                 ErrorResult& rv)
 {
-    return WebGLContext::GetFramebufferAttachmentParameter(cx, target, attachment, pname,
-                                                           out_error);
+    if (IsContextLost())
+        return JS::NullValue();
+
+    // OpenGL ES 3.0.4 (August 27, 2014) 6.1. QUERYING GL STATE 240
+    // "getFramebufferAttachmentParamter returns information about attachments of a bound
+    // framebuffer object. target must be DRAW_FRAMEBUFFER, READ_FRAMEBUFFER, or
+    // FRAMEBUFFER."
+
+    if (!ValidateFramebufferTarget(target, "getFramebufferAttachmentParameter"))
+        return JS::NullValue();
+
+    // FRAMEBUFFER is equivalent to DRAW_FRAMEBUFFER.
+    if (target == LOCAL_GL_FRAMEBUFFER)
+        target = LOCAL_GL_DRAW_FRAMEBUFFER;
+
+    WebGLFramebuffer* boundFB = nullptr;
+    switch (target) {
+    case LOCAL_GL_DRAW_FRAMEBUFFER: boundFB = mBoundDrawFramebuffer; break;
+    case LOCAL_GL_READ_FRAMEBUFFER: boundFB = mBoundReadFramebuffer; break;
+    }
+
+    if (boundFB) {
+        return boundFB->GetAttachmentParameter(cx, attachment, pname, rv);
+    }
+
+    // Handle default FB
+    const gl::GLFormats& formats = gl->GetGLFormats();
+    GLenum internalFormat = LOCAL_GL_NONE;
+
+    /* If the default framebuffer is bound to target, then attachment must be BACK,
+       identifying the color buffer; DEPTH, identifying the depth buffer; or STENCIL,
+       identifying the stencil buffer. */
+    switch (attachment) {
+    case LOCAL_GL_BACK:
+        internalFormat = formats.color_texInternalFormat;
+        break;
+
+    case LOCAL_GL_DEPTH:
+        internalFormat = formats.depth;
+        break;
+
+    case LOCAL_GL_STENCIL:
+        internalFormat = formats.stencil;
+        break;
+
+    default:
+        ErrorInvalidEnum("getFramebufferAttachmentParameter: Can only query "
+                         "attachment BACK, DEPTH, or STENCIL from default "
+                         "framebuffer");
+        return JS::NullValue();
+    }
+
+    const FormatInfo* info = webgl::GetInfoBySizedFormat(internalFormat);
+    MOZ_RELEASE_ASSERT(info);
+    EffectiveFormat effectiveFormat = info->effectiveFormat;
+
+    switch (pname) {
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE:
+        return JS::Int32Value(LOCAL_GL_FRAMEBUFFER_DEFAULT);
+
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE:
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE:
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE:
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE:
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE:
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE:
+        return JS::Int32Value(webgl::GetComponentSize(effectiveFormat, pname));
+
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE:
+        if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT &&
+            pname == LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE)
+        {
+            ErrorInvalidOperation("getFramebufferAttachmentParameter: Querying "
+                                  "FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE against "
+                                  "DEPTH_STENCIL_ATTACHMENT is an error.");
+            return JS::NullValue();
+        }
+
+        return JS::Int32Value(webgl::GetComponentType(effectiveFormat));
+
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING:
+        return JS::Int32Value(webgl::GetColorEncoding(effectiveFormat));
+    }
+
+    /* Any combinations of framebuffer type and pname not described above will generate an
+       INVALID_ENUM error. */
+    ErrorInvalidEnum("getFramebufferAttachmentParameter: Invalid combination of ");
+    return JS::NullValue();
 }
 
 // Map attachments intended for the default buffer, to attachments for a non-
 // default buffer.
 static bool
 TranslateDefaultAttachments(const dom::Sequence<GLenum>& in, dom::Sequence<GLenum>* out)
 {
     for (size_t i = 0; i < in.Length(); i++) {
@@ -381,24 +552,22 @@ TranslateDefaultAttachments(const dom::S
     return true;
 }
 
 void
 WebGL2Context::InvalidateFramebuffer(GLenum target,
                                      const dom::Sequence<GLenum>& attachments,
                                      ErrorResult& rv)
 {
-    const char funcName[] = "invalidateSubFramebuffer";
-
     if (IsContextLost())
         return;
 
     MakeContextCurrent();
 
-    if (!ValidateFramebufferTarget(target, funcName))
+    if (!ValidateFramebufferTarget(target, "framebufferRenderbuffer"))
         return;
 
     const WebGLFramebuffer* fb;
     bool isDefaultFB;
     switch (target) {
     case LOCAL_GL_FRAMEBUFFER:
     case LOCAL_GL_DRAW_FRAMEBUFFER:
         fb = mBoundDrawFramebuffer;
@@ -409,65 +578,57 @@ WebGL2Context::InvalidateFramebuffer(GLe
         fb = mBoundReadFramebuffer;
         isDefaultFB = gl->Screen()->IsReadFramebufferDefault();
         break;
 
     default:
         MOZ_CRASH("Bad target.");
     }
 
-    const bool badColorAttachmentIsInvalidOp = true;
     for (size_t i = 0; i < attachments.Length(); i++) {
-        if (!ValidateFramebufferAttachment(fb, attachments[i], funcName,
-                                           badColorAttachmentIsInvalidOp))
+        if (!ValidateFramebufferAttachment(fb, attachments[i],
+                                           "invalidateFramebuffer"))
         {
             return;
         }
     }
 
     // InvalidateFramebuffer is a hint to the driver. Should be OK to
     // skip calls if not supported, for example by OSX 10.9 GL
     // drivers.
-    if (!gl->IsSupported(gl::GLFeature::invalidate_framebuffer))
+    static bool invalidateFBSupported = gl->IsSupported(gl::GLFeature::invalidate_framebuffer);
+    if (!invalidateFBSupported)
         return;
 
     if (!fb && !isDefaultFB) {
         dom::Sequence<GLenum> tmpAttachments;
         if (!TranslateDefaultAttachments(attachments, &tmpAttachments)) {
             rv.Throw(NS_ERROR_OUT_OF_MEMORY);
             return;
         }
 
-        gl->fInvalidateFramebuffer(target, tmpAttachments.Length(),
-                                   tmpAttachments.Elements());
+        gl->fInvalidateFramebuffer(target, tmpAttachments.Length(), tmpAttachments.Elements());
     } else {
         gl->fInvalidateFramebuffer(target, attachments.Length(), attachments.Elements());
     }
 }
 
 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 (IsContextLost())
         return;
 
     MakeContextCurrent();
 
-    if (!ValidateFramebufferTarget(target, funcName))
+    if (!ValidateFramebufferTarget(target, "framebufferRenderbuffer"))
         return;
 
-    if (width < 0 || height < 0) {
-        ErrorInvalidValue("%s: width and height must be >= 0.", funcName);
-        return;
-    }
-
     const WebGLFramebuffer* fb;
     bool isDefaultFB;
     switch (target) {
     case LOCAL_GL_FRAMEBUFFER:
     case LOCAL_GL_DRAW_FRAMEBUFFER:
         fb = mBoundDrawFramebuffer;
         isDefaultFB = gl->Screen()->IsDrawFramebufferDefault();
         break;
@@ -476,54 +637,54 @@ WebGL2Context::InvalidateSubFramebuffer(
         fb = mBoundReadFramebuffer;
         isDefaultFB = gl->Screen()->IsReadFramebufferDefault();
         break;
 
     default:
         MOZ_CRASH("Bad target.");
     }
 
-    const bool badColorAttachmentIsInvalidOp = true;
     for (size_t i = 0; i < attachments.Length(); i++) {
-        if (!ValidateFramebufferAttachment(fb, attachments[i], funcName,
-                                           badColorAttachmentIsInvalidOp))
+        if (!ValidateFramebufferAttachment(fb, attachments[i],
+                                           "invalidateSubFramebuffer"))
         {
             return;
         }
     }
 
     // InvalidateFramebuffer is a hint to the driver. Should be OK to
     // skip calls if not supported, for example by OSX 10.9 GL
     // drivers.
-    if (!gl->IsSupported(gl::GLFeature::invalidate_framebuffer))
+    static bool invalidateFBSupported = gl->IsSupported(gl::GLFeature::invalidate_framebuffer);
+    if (!invalidateFBSupported)
         return;
 
     if (!fb && !isDefaultFB) {
         dom::Sequence<GLenum> tmpAttachments;
         if (!TranslateDefaultAttachments(attachments, &tmpAttachments)) {
             rv.Throw(NS_ERROR_OUT_OF_MEMORY);
             return;
         }
 
-        gl->fInvalidateSubFramebuffer(target, tmpAttachments.Length(),
-                                      tmpAttachments.Elements(), x, y, width, height);
+        gl->fInvalidateSubFramebuffer(target, tmpAttachments.Length(), tmpAttachments.Elements(),
+                                      x, y, width, height);
     } else {
-        gl->fInvalidateSubFramebuffer(target, attachments.Length(),
-                                      attachments.Elements(), x, y, width, height);
+        gl->fInvalidateSubFramebuffer(target, attachments.Length(), attachments.Elements(),
+                                      x, y, width, height);
     }
 }
 
 void
 WebGL2Context::ReadBuffer(GLenum mode)
 {
     if (IsContextLost())
         return;
 
     const bool isColorAttachment = (mode >= LOCAL_GL_COLOR_ATTACHMENT0 &&
-                                    mode <= LastColorAttachmentEnum());
+                                    mode <= LastColorAttachment());
 
     if (mode != LOCAL_GL_NONE && mode != LOCAL_GL_BACK && !isColorAttachment) {
         ErrorInvalidEnum("readBuffer: `mode` must be one of NONE, BACK, or "
                          "COLOR_ATTACHMENTi. Was %s",
                          EnumName(mode));
         return;
     }
 
--- a/dom/canvas/WebGL2ContextState.cpp
+++ b/dom/canvas/WebGL2ContextState.cpp
@@ -54,16 +54,18 @@ WebGL2Context::GetParameter(JSContext* c
 
       return JS::Int32Value(LOCAL_GL_BACK);
     }
 
     case LOCAL_GL_FRAGMENT_SHADER_DERIVATIVE_HINT:
       /* fall through */
 
     /* GLint */
+    case LOCAL_GL_MAX_3D_TEXTURE_SIZE:
+    case LOCAL_GL_MAX_ARRAY_TEXTURE_LAYERS:
     case LOCAL_GL_MAX_COMBINED_UNIFORM_BLOCKS:
     case LOCAL_GL_MAX_ELEMENTS_INDICES:
     case LOCAL_GL_MAX_ELEMENTS_VERTICES:
     case LOCAL_GL_MAX_FRAGMENT_INPUT_COMPONENTS:
     case LOCAL_GL_MAX_FRAGMENT_UNIFORM_BLOCKS:
     case LOCAL_GL_MAX_FRAGMENT_UNIFORM_COMPONENTS:
     case LOCAL_GL_MAX_PROGRAM_TEXEL_OFFSET:
     case LOCAL_GL_MAX_SAMPLES:
@@ -82,22 +84,16 @@ WebGL2Context::GetParameter(JSContext* c
     case LOCAL_GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT:
     case LOCAL_GL_UNPACK_IMAGE_HEIGHT:
     case LOCAL_GL_UNPACK_ROW_LENGTH: {
       GLint val;
       gl->fGetIntegerv(pname, &val);
       return JS::Int32Value(val);
     }
 
-    case LOCAL_GL_MAX_3D_TEXTURE_SIZE:
-      return JS::Int32Value(mImplMax3DTextureSize);
-
-    case LOCAL_GL_MAX_ARRAY_TEXTURE_LAYERS:
-      return JS::Int32Value(mImplMaxArrayTextureLayers);
-
     case LOCAL_GL_MAX_VARYING_COMPONENTS: {
       // On OS X Core Profile this is buggy.  The spec says that the
       // value is 4 * GL_MAX_VARYING_VECTORS
       GLint val;
       gl->fGetIntegerv(LOCAL_GL_MAX_VARYING_VECTORS, &val);
       return JS::Int32Value(4*val);
     }
 
@@ -150,17 +146,19 @@ WebGL2Context::GetParameter(JSContext* c
     // DRAW_FRAMEBUFFER_BINDING is the same as FRAMEBUFFER_BINDING.
     case LOCAL_GL_READ_FRAMEBUFFER_BINDING:
       return WebGLObjectAsJSValue(cx, mBoundReadFramebuffer.get(), rv);
 
     case LOCAL_GL_SAMPLER_BINDING:
       return WebGLObjectAsJSValue(cx, mBoundSamplers[mActiveTexture].get(), rv);
 
     case LOCAL_GL_TEXTURE_BINDING_2D_ARRAY:
-      return WebGLObjectAsJSValue(cx, mBound2DArrayTextures[mActiveTexture].get(), rv);
+      // TODO: Implement gl.TEXTURE_2D_ARRAY
+      // return WebGLObjectAsJSValue(cx, mBound2DTextureArrays[mActiveTexture].get(), rv);
+      return JS::NullValue();
 
     case LOCAL_GL_TEXTURE_BINDING_3D:
       return WebGLObjectAsJSValue(cx, mBound3DTextures[mActiveTexture].get(), rv);
 
     case LOCAL_GL_TRANSFORM_FEEDBACK_BINDING: {
       WebGLTransformFeedback* tf =
         (mBoundTransformFeedback != mDefaultTransformFeedback) ? mBoundTransformFeedback.get() : nullptr;
       return WebGLObjectAsJSValue(cx, tf, rv);
--- a/dom/canvas/WebGL2ContextTextures.cpp
+++ b/dom/canvas/WebGL2ContextTextures.cpp
@@ -6,208 +6,114 @@
 #include "GLContext.h"
 #include "WebGL2Context.h"
 #include "WebGLContextUtils.h"
 #include "WebGLTexture.h"
 
 namespace mozilla {
 
 void
-WebGL2Context::TexStorage2D(GLenum rawTexTarget, GLsizei levels, GLenum internalFormat,
-                            GLsizei width, GLsizei height)
+WebGL2Context::TexStorage2D(GLenum rawTexTarget, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height)
 {
     const char funcName[] = "TexStorage2D";
-    const uint8_t funcDims = 2;
-
-    TexTarget target;
+    TexTarget texTarget;
     WebGLTexture* tex;
-    if (!ValidateTexTarget(this, funcName, funcDims, rawTexTarget, &target, &tex))
+    if (!ValidateTexTarget(this, rawTexTarget, funcName, &texTarget, &tex))
         return;
 
-    const GLsizei depth = 1;
-    tex->TexStorage(funcName, target, levels, internalFormat, width, height, depth);
+    tex->TexStorage2D(texTarget, levels, internalFormat, width, height);
 }
 
 void
 WebGL2Context::TexStorage3D(GLenum rawTexTarget, GLsizei levels, GLenum internalFormat,
                             GLsizei width, GLsizei height, GLsizei depth)
 {
     const char funcName[] = "texStorage3D";
-    const uint8_t funcDims = 3;
-
-    TexTarget target;
+    TexTarget texTarget;
     WebGLTexture* tex;
-    if (!ValidateTexTarget(this, funcName, funcDims, rawTexTarget, &target, &tex))
+    if (!ValidateTexTarget(this, rawTexTarget, funcName, &texTarget, &tex))
         return;
 
-    tex->TexStorage(funcName, target, levels, internalFormat, width, height, depth);
+    tex->TexStorage3D(texTarget, levels, internalFormat, width, height, depth);
 }
 
 void
 WebGL2Context::TexImage3D(GLenum rawTexImageTarget, GLint level, GLenum internalFormat,
-                          GLsizei width, GLsizei height, GLsizei depth, GLint border,
-                          GLenum unpackFormat, GLenum unpackType,
-                          const dom::Nullable<dom::ArrayBufferViewOrSharedArrayBufferView>& maybeView)
+                          GLsizei width, GLsizei height, GLsizei depth,
+                          GLint border, GLenum unpackFormat, GLenum unpackType,
+                          const dom::Nullable<dom::ArrayBufferViewOrSharedArrayBufferView>& maybeView,
+                          ErrorResult& out_rv)
 {
     const char funcName[] = "texImage3D";
-    const uint8_t funcDims = 3;
-
-    TexImageTarget target;
+    TexImageTarget texImageTarget;
     WebGLTexture* tex;
-    if (!ValidateTexImageTarget(this, funcName, funcDims, rawTexImageTarget, &target,
-                                &tex))
-    {
-        return;
-    }
-
-    const bool isSubImage = false;
-    const GLint xOffset = 0;
-    const GLint yOffset = 0;
-    const GLint zOffset = 0;
-    tex->TexOrSubImage(isSubImage, funcName, target, level, internalFormat, xOffset,
-                       yOffset, zOffset, width, height, depth, border, unpackFormat,
-                       unpackType, maybeView);
-}
-
-void
-WebGL2Context::TexSubImage3D(GLenum rawTexImageTarget, GLint level, GLint xOffset,
-                             GLint yOffset, GLint zOffset, GLsizei width, GLsizei height,
-                             GLsizei depth, GLenum unpackFormat, GLenum unpackType,
-                             const dom::Nullable<dom::ArrayBufferViewOrSharedArrayBufferView>& maybeView,
-                             ErrorResult& /*out_rv*/)
-{
-    const char funcName[] = "texSubImage3D";
-    const uint8_t funcDims = 3;
-
-    TexImageTarget target;
-    WebGLTexture* tex;
-    if (!ValidateTexImageTarget(this, funcName, funcDims, rawTexImageTarget, &target,
-                                &tex))
-    {
-        return;
-    }
-
-    const bool isSubImage = true;
-    const GLenum internalFormat = 0;
-    const GLint border = 0;
-    tex->TexOrSubImage(isSubImage, funcName, target, level, internalFormat, xOffset,
-                       yOffset, zOffset, width, height, depth, border, unpackFormat,
-                       unpackType, maybeView);
-}
-
-void
-WebGL2Context::TexSubImage3D(GLenum rawTexImageTarget, GLint level, GLint xOffset,
-                             GLint yOffset, GLint zOffset, GLenum unpackFormat,
-                             GLenum unpackType, dom::ImageData* imageData,
-                             ErrorResult& /*out_rv*/)
-{
-    const char funcName[] = "texSubImage3D";
-    const uint8_t funcDims = 3;
-
-    TexImageTarget target;
-    WebGLTexture* tex;
-    if (!ValidateTexImageTarget(this, funcName, funcDims, rawTexImageTarget, &target,
-                                &tex))
+    if (!ValidateTexImageTarget(this, rawTexImageTarget, funcName, &texImageTarget, &tex))
     {
         return;
     }
 
-    const bool isSubImage = true;
-    const GLenum internalFormat = 0;
-    tex->TexOrSubImage(isSubImage, funcName, target, level, internalFormat, xOffset,
-                       yOffset, zOffset, unpackFormat, unpackType, imageData);
+    tex->TexImage3D(texImageTarget, level, internalFormat, width, height, depth, border,
+                    unpackFormat, unpackType, maybeView, &out_rv);
 }
 
 void
-WebGL2Context::TexSubImage3D(GLenum rawTexImageTarget, GLint level, GLint xOffset,
-                             GLint yOffset, GLint zOffset, GLenum unpackFormat,
-                             GLenum unpackType, dom::Element* elem,
-                             ErrorResult* const out_rv)
+WebGL2Context::TexSubImage3D(GLenum rawTexImageTarget, GLint level,
+                             GLint xOffset, GLint yOffset, GLint zOffset,
+                             GLsizei width, GLsizei height, GLsizei depth,
+                             GLenum unpackFormat, GLenum unpackType,
+                             const dom::Nullable<dom::ArrayBufferViewOrSharedArrayBufferView>& maybeView,
+                             ErrorResult& out_rv)
 {
     const char funcName[] = "texSubImage3D";
-    const uint8_t funcDims = 3;
-
-    TexImageTarget target;
+    TexImageTarget texImageTarget;
     WebGLTexture* tex;
-    if (!ValidateTexImageTarget(this, funcName, funcDims, rawTexImageTarget, &target,
-                                &tex))
-    {
-        return;
-    }
-
-    const bool isSubImage = true;
-    const GLenum internalFormat = 0;
-    tex->TexOrSubImage(isSubImage, funcName, target, level, internalFormat, xOffset,
-                       yOffset, zOffset, unpackFormat, unpackType, elem, out_rv);
-}
-
-void
-WebGL2Context::CompressedTexImage3D(GLenum rawTexImageTarget, GLint level,
-                                    GLenum internalFormat, GLsizei width, GLsizei height,
-                                    GLsizei depth, GLint border,
-                                    const dom::ArrayBufferViewOrSharedArrayBufferView& view)
-{
-    const char funcName[] = "compressedTexImage3D";
-    const uint8_t funcDims = 3;
-
-    TexImageTarget target;
-    WebGLTexture* tex;
-    if (!ValidateTexImageTarget(this, funcName, funcDims, rawTexImageTarget, &target,
-                                &tex))
+    if (!ValidateTexImageTarget(this, rawTexImageTarget, funcName, &texImageTarget, &tex))
     {
         return;
     }
 
-    tex->CompressedTexImage(funcName, target, level, internalFormat, width, height, depth,
-                            border, view);
+    tex->TexSubImage3D(texImageTarget, level, xOffset, yOffset, zOffset, width, height,
+                       depth, unpackFormat, unpackType, maybeView, &out_rv);
+
+}
+
+void
+WebGL2Context::TexSubImage3D(GLenum target, GLint level,
+                             GLint xOffset, GLint yOffset, GLint zOffset,
+                             GLenum unpackFormat, GLenum unpackType, dom::ImageData* imageData,
+                             ErrorResult& out_rv)
+{
+    GenerateWarning("texSubImage3D: Not implemented.");
 }
 
 void
-WebGL2Context::CompressedTexSubImage3D(GLenum rawTexImageTarget, GLint level,
-                                       GLint xOffset, GLint yOffset, GLint zOffset,
-                                       GLsizei width, GLsizei height, GLsizei depth,
-                                       GLenum sizedUnpackFormat,
-                                       const dom::ArrayBufferViewOrSharedArrayBufferView& view)
+WebGL2Context::CopyTexSubImage3D(GLenum target, GLint level,
+                                 GLint xOffset, GLint yOffset, GLint zOffset,
+                                 GLint x, GLint y, GLsizei width, GLsizei height)
 {
-    const char funcName[] = "compressedTexSubImage3D";
-    const uint8_t funcDims = 3;
-
-    TexImageTarget target;
-    WebGLTexture* tex;
-    if (!ValidateTexImageTarget(this, funcName, funcDims, rawTexImageTarget, &target,
-                                &tex))
-    {
-        return;
-    }
-
-    tex->CompressedTexSubImage(funcName, target, level, xOffset, yOffset, zOffset, width,
-                               height, depth, sizedUnpackFormat, view);
+    GenerateWarning("copyTexSubImage3D: Not implemented.");
 }
 
 void
-WebGL2Context::CopyTexSubImage3D(GLenum rawTexImageTarget, GLint level, GLint xOffset,
-                                 GLint yOffset, GLint zOffset, GLint x, GLint y,
-                                 GLsizei width, GLsizei height)
+WebGL2Context::CompressedTexImage3D(GLenum target, GLint level, GLenum internalFormat,
+                                    GLsizei width, GLsizei height, GLsizei depth,
+                                    GLint border, GLsizei imageSize, const dom::ArrayBufferViewOrSharedArrayBufferView& view)
 {
-    const char funcName[] = "copyTexSubImage3D";
-    const uint8_t funcDims = 3;
-
-    TexImageTarget target;
-    WebGLTexture* tex;
-    if (!ValidateTexImageTarget(this, funcName, funcDims, rawTexImageTarget, &target,
-                                &tex))
-    {
-        return;
-    }
-
-    tex->CopyTexSubImage(funcName, target, level, xOffset, yOffset, zOffset, x, y, width,
-                         height);
+    GenerateWarning("compressedTexImage3D: Not implemented.");
 }
 
-/*virtual*/ bool
+void
+WebGL2Context::CompressedTexSubImage3D(GLenum target, GLint level, GLint xOffset, GLint yOffset, GLint zOffset,
+                                       GLsizei width, GLsizei height, GLsizei depth,
+                                       GLenum unpackFormat, GLsizei imageSize, const dom::ArrayBufferViewOrSharedArrayBufferView& view)
+{
+    GenerateWarning("compressedTexSubImage3D: Not implemented.");
+}
+
+bool
 WebGL2Context::IsTexParamValid(GLenum pname) const
 {
     switch (pname) {
     case LOCAL_GL_TEXTURE_BASE_LEVEL:
     case LOCAL_GL_TEXTURE_COMPARE_FUNC:
     case LOCAL_GL_TEXTURE_COMPARE_MODE:
     case LOCAL_GL_TEXTURE_IMMUTABLE_FORMAT:
     case LOCAL_GL_TEXTURE_IMMUTABLE_LEVELS:
--- a/dom/canvas/WebGLBuffer.h
+++ b/dom/canvas/WebGLBuffer.h
@@ -50,17 +50,17 @@ public:
                                         size_t updateSizeInBytes);
 
     bool Validate(GLenum type, uint32_t max_allowed, size_t first, size_t count,
                   uint32_t* const out_upperBound);
 
     bool IsElementArrayUsedWithMultipleTypes() const;
 
     WebGLContext* GetParentObject() const {
-        return mContext;
+        return Context();
     }
 
     virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
 
     const GLenum mGLName;
 
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLBuffer)
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLBuffer)
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -90,36 +90,37 @@ WebGLContextOptions::WebGLContextOptions
     , preserveDrawingBuffer(false)
     , failIfMajorPerformanceCaveat(false)
 {
     // Set default alpha state based on preference.
     if (gfxPrefs::WebGLDefaultNoAlpha())
         alpha = false;
 }
 
-
-/*static*/ const uint32_t WebGLContext::kMinMaxColorAttachments = 4;
-/*static*/ const uint32_t WebGLContext::kMinMaxDrawBuffers = 4;
-
 WebGLContext::WebGLContext()
     : WebGLContextUnchecked(nullptr)
     , mBypassShaderValidation(false)
     , mGLMaxSamples(1)
     , mNeedsFakeNoAlpha(false)
     , mNeedsFakeNoDepth(false)
     , mNeedsFakeNoStencil(false)
 {
     mGeneration = 0;
     mInvalidated = false;
     mCapturedFrameInvalidated = false;
     mShouldPresent = true;
     mResetLayer = true;
     mOptionsFrozen = false;
 
     mActiveTexture = 0;
+    mPixelStoreFlipY = false;
+    mPixelStorePremultiplyAlpha = false;
+    mPixelStoreColorspaceConversion = BROWSER_DEFAULT_WEBGL;
+
+    mFakeBlackStatus = WebGLContextFakeBlackStatus::NotNeeded;
 
     mVertexAttrib0Vector[0] = 0;
     mVertexAttrib0Vector[1] = 0;
     mVertexAttrib0Vector[2] = 0;
     mVertexAttrib0Vector[3] = 1;
     mFakeVertexAttrib0BufferObjectVector[0] = 0;
     mFakeVertexAttrib0BufferObjectVector[1] = 0;
     mFakeVertexAttrib0BufferObjectVector[2] = 0;
@@ -134,16 +135,41 @@ WebGLContext::WebGLContext()
     mViewportHeight = 0;
 
     mDitherEnabled = 1;
     mRasterizerDiscardEnabled = 0; // OpenGL ES 3.0 spec p244
     mScissorTestEnabled = 0;
     mDepthTestEnabled = 0;
     mStencilTestEnabled = 0;
 
+    // initialize some GL values: we're going to get them from the GL and use them as the sizes of arrays,
+    // so in case glGetIntegerv leaves them uninitialized because of a GL bug, we would have very weird crashes.
+    mGLMaxVertexAttribs = 0;
+    mGLMaxTextureUnits = 0;
+    mGLMaxTextureSize = 0;
+    mGLMaxTextureSizeLog2 = 0;
+    mGLMaxCubeMapTextureSize = 0;
+    mGLMaxCubeMapTextureSizeLog2 = 0;
+    mGLMaxRenderbufferSize = 0;
+    mGLMaxTextureImageUnits = 0;
+    mGLMaxVertexTextureImageUnits = 0;
+    mGLMaxVaryingVectors = 0;
+    mGLMaxFragmentUniformVectors = 0;
+    mGLMaxVertexUniformVectors = 0;
+    mGLMaxColorAttachments = 1;
+    mGLMaxDrawBuffers = 1;
+    mGLMaxTransformFeedbackSeparateAttribs = 0;
+    mGLMaxUniformBufferBindings = 0;
+    mGLMax3DTextureSize = 0;
+    mGLMaxArrayTextureLayers = 0;
+
+    // See OpenGL ES 2.0.25 spec, 6.2 State Tables, table 6.13
+    mPixelStorePackAlignment = 4;
+    mPixelStoreUnpackAlignment = 4;
+
     if (NS_IsMainThread()) {
         // XXX mtseng: bug 709490, not thread safe
         WebGLMemoryTracker::AddWebGLContext(this);
     }
 
     mAllowContextRestore = true;
     mLastLossWasSimulated = false;
     mContextLossHandler = new WebGLContextLossHandler(this);
@@ -193,17 +219,16 @@ WebGLContext::DestroyResourcesAndContext
     if (!gl)
         return;
 
     gl->MakeCurrent();
 
     mBound2DTextures.Clear();
     mBoundCubeMapTextures.Clear();
     mBound3DTextures.Clear();
-    mBound2DArrayTextures.Clear();
     mBoundSamplers.Clear();
     mBoundArrayBuffer = nullptr;
     mBoundCopyReadBuffer = nullptr;
     mBoundCopyWriteBuffer = nullptr;
     mBoundPixelPackBuffer = nullptr;
     mBoundPixelUnpackBuffer = nullptr;
     mBoundTransformFeedbackBuffer = nullptr;
     mBoundUniformBuffer = nullptr;
@@ -237,24 +262,20 @@ WebGLContext::DestroyResourcesAndContext
         mPrograms.getLast()->DeleteOnce();
     while (!mQueries.isEmpty())
         mQueries.getLast()->DeleteOnce();
     while (!mSamplers.isEmpty())
         mSamplers.getLast()->DeleteOnce();
     while (!mTransformFeedbacks.isEmpty())
         mTransformFeedbacks.getLast()->DeleteOnce();
 
-    mFakeBlack_2D_0000       = nullptr;
-    mFakeBlack_2D_0001       = nullptr;
-    mFakeBlack_CubeMap_0000  = nullptr;
-    mFakeBlack_CubeMap_0001  = nullptr;
-    mFakeBlack_3D_0000       = nullptr;
-    mFakeBlack_3D_0001       = nullptr;
-    mFakeBlack_2D_Array_0000 = nullptr;
-    mFakeBlack_2D_Array_0001 = nullptr;
+    mBlackOpaqueTexture2D = nullptr;
+    mBlackOpaqueTextureCubeMap = nullptr;
+    mBlackTransparentTexture2D = nullptr;
+    mBlackTransparentTextureCubeMap = nullptr;
 
     if (mFakeVertexAttrib0BufferObject)
         gl->fDeleteBuffers(1, &mFakeVertexAttrib0BufferObject);
 
     // disable all extensions except "WEBGL_lose_context". see bug #927969
     // spec: http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
     for (size_t i = 0; i < size_t(WebGLExtensionID::Max); ++i) {
         WebGLExtensionID extension = WebGLExtensionID(i);
@@ -1272,57 +1293,89 @@ WebGLContext::MozGetUnderlyingParamStrin
     }
 
     return NS_OK;
 }
 
 void
 WebGLContext::ClearScreen()
 {
+    bool colorAttachmentsMask[WebGLContext::kMaxColorAttachments] = {false};
+
     MakeContextCurrent();
     ScopedBindFramebuffer autoFB(gl, 0);
 
-    const bool changeDrawBuffers = (mDefaultFB_DrawBuffer0 != LOCAL_GL_BACK);
-    if (changeDrawBuffers) {
-        const GLenum back = LOCAL_GL_BACK;
-        gl->fDrawBuffers(1, &back);
-    }
-
-    GLbitfield bufferBits = LOCAL_GL_COLOR_BUFFER_BIT;
+    GLbitfield clearMask = LOCAL_GL_COLOR_BUFFER_BIT;
     if (mOptions.depth)
-        bufferBits |= LOCAL_GL_DEPTH_BUFFER_BIT;
+        clearMask |= LOCAL_GL_DEPTH_BUFFER_BIT;
     if (mOptions.stencil)
-        bufferBits |= LOCAL_GL_STENCIL_BUFFER_BIT;
+        clearMask |= LOCAL_GL_STENCIL_BUFFER_BIT;
 
-    ForceClearFramebufferWithDefaultValues(bufferBits, mNeedsFakeNoAlpha);
+    colorAttachmentsMask[0] = true;
 
-    if (changeDrawBuffers) {
-        gl->fDrawBuffers(1, &mDefaultFB_DrawBuffer0);
-    }
+    ForceClearFramebufferWithDefaultValues(mNeedsFakeNoAlpha, clearMask,
+                                           colorAttachmentsMask);
 }
 
 void
-WebGLContext::ForceClearFramebufferWithDefaultValues(GLbitfield clearBits,
-                                                     bool fakeNoAlpha)
+WebGLContext::ForceClearFramebufferWithDefaultValues(bool fakeNoAlpha, GLbitfield mask,
+                                                     const bool colorAttachmentsMask[kMaxColorAttachments])
 {
     MakeContextCurrent();
 
-    const bool initializeColorBuffer = bool(clearBits & LOCAL_GL_COLOR_BUFFER_BIT);
-    const bool initializeDepthBuffer = bool(clearBits & LOCAL_GL_DEPTH_BUFFER_BIT);
-    const bool initializeStencilBuffer = bool(clearBits & LOCAL_GL_STENCIL_BUFFER_BIT);
+    bool initializeColorBuffer = 0 != (mask & LOCAL_GL_COLOR_BUFFER_BIT);
+    bool initializeDepthBuffer = 0 != (mask & LOCAL_GL_DEPTH_BUFFER_BIT);
+    bool initializeStencilBuffer = 0 != (mask & LOCAL_GL_STENCIL_BUFFER_BIT);
+    bool drawBuffersIsEnabled = IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers);
+    bool shouldOverrideDrawBuffers = false;
+    bool usingDefaultFrameBuffer = !mBoundDrawFramebuffer;
+
+    GLenum currentDrawBuffers[WebGLContext::kMaxColorAttachments];
 
     // Fun GL fact: No need to worry about the viewport here, glViewport is just
     // setting up a coordinates transformation, it doesn't affect glClear at all.
     AssertCachedState(); // Can't check cached bindings, as we could
                          // have a different FB bound temporarily.
 
     // Prepare GL state for clearing.
     gl->fDisable(LOCAL_GL_SCISSOR_TEST);
 
     if (initializeColorBuffer) {
+
+        if (drawBuffersIsEnabled) {
+
+            GLenum drawBuffersCommand[WebGLContext::kMaxColorAttachments] = { LOCAL_GL_NONE };
+
+            for (int32_t i = 0; i < mGLMaxDrawBuffers; i++) {
+                GLint temp;
+                gl->fGetIntegerv(LOCAL_GL_DRAW_BUFFER0 + i, &temp);
+                currentDrawBuffers[i] = temp;
+
+                if (colorAttachmentsMask[i]) {
+                    drawBuffersCommand[i] = LOCAL_GL_COLOR_ATTACHMENT0 + i;
+                }
+                if (currentDrawBuffers[i] != drawBuffersCommand[i])
+                    shouldOverrideDrawBuffers = true;
+            }
+
+            // When clearing the default framebuffer, we must be clearing only
+            // GL_BACK, and nothing else, or else gl may return an error. We will
+            // only use the first element of currentDrawBuffers in this case.
+            if (usingDefaultFrameBuffer) {
+                gl->Screen()->SetDrawBuffer(LOCAL_GL_BACK);
+                if (currentDrawBuffers[0] == LOCAL_GL_COLOR_ATTACHMENT0)
+                    currentDrawBuffers[0] = LOCAL_GL_BACK;
+                shouldOverrideDrawBuffers = false;
+            }
+            // calling draw buffers can cause resolves on adreno drivers so
+            // we try to avoid calling it
+            if (shouldOverrideDrawBuffers)
+                gl->fDrawBuffers(mGLMaxDrawBuffers, drawBuffersCommand);
+        }
+
         gl->fColorMask(1, 1, 1, 1);
 
         if (fakeNoAlpha) {
             gl->fClearColor(0.0f, 0.0f, 0.0f, 1.0f);
         } else {
             gl->fClearColor(0.0f, 0.0f, 0.0f, 0.0f);
         }
     }
@@ -1340,28 +1393,37 @@ WebGLContext::ForceClearFramebufferWithD
         gl->fClearStencil(0);
     }
 
     if (mRasterizerDiscardEnabled) {
         gl->fDisable(LOCAL_GL_RASTERIZER_DISCARD);
     }
 
     // Do the clear!
-    gl->fClear(clearBits);
+    gl->fClear(mask);
 
     // And reset!
     if (mScissorTestEnabled)
         gl->fEnable(LOCAL_GL_SCISSOR_TEST);
 
     if (mRasterizerDiscardEnabled) {
         gl->fEnable(LOCAL_GL_RASTERIZER_DISCARD);
     }
 
     // Restore GL state after clearing.
     if (initializeColorBuffer) {
+
+        if (drawBuffersIsEnabled) {
+            if (usingDefaultFrameBuffer) {
+                gl->Screen()->SetDrawBuffer(currentDrawBuffers[0]);
+            } else if (shouldOverrideDrawBuffers) {
+                gl->fDrawBuffers(mGLMaxDrawBuffers, currentDrawBuffers);
+            }
+        }
+
         gl->fColorMask(mColorWriteMask[0],
                        mColorWriteMask[1],
                        mColorWriteMask[2],
                        mColorWriteMask[3]);
         gl->fClearColor(mColorClearValue[0],
                         mColorClearValue[1],
                         mColorClearValue[2],
                         mColorClearValue[3]);
@@ -1751,40 +1813,27 @@ WebGLContext::GetSurfaceSnapshot(bool* o
 void
 WebGLContext::DidRefresh()
 {
     if (gl) {
         gl->FlushIfHeavyGLCallsSinceLastFlush();
     }
 }
 
-bool
-WebGLContext::ValidateCurFBForRead(const char* funcName,
-                                   const webgl::FormatUsageInfo** const out_format,
-                                   uint32_t* const out_width, uint32_t* const out_height)
+size_t
+RoundUpToMultipleOf(size_t value, size_t multiple)
 {
-    if (!mBoundReadFramebuffer) {
-        ClearBackbufferIfNeeded();
+    size_t overshoot = value + multiple - 1;
+    return overshoot - (overshoot % multiple);
+}
 
-        // FIXME - here we're assuming that the default framebuffer is backed by
-        // UNSIGNED_BYTE that might not always be true, say if we had a 16bpp default
-        // framebuffer.
-        auto effFormat = mOptions.alpha ? webgl::EffectiveFormat::RGBA8
-                                        : webgl::EffectiveFormat::RGB8;
-
-        *out_format = mFormatUsage->GetUsage(effFormat);
-        MOZ_ASSERT(*out_format);
-
-        *out_width = mWidth;
-        *out_height = mHeight;
-        return true;
-    }
-
-    return mBoundReadFramebuffer->ValidateForRead(funcName, out_format, out_width,
-                                                  out_height);
+CheckedUint32
+RoundedToNextMultipleOf(CheckedUint32 x, CheckedUint32 y)
+{
+    return ((x + y - 1) / y) * y;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
 WebGLContext::ScopedMaskWorkaround::ScopedMaskWorkaround(WebGLContext& webgl)
     : mWebGL(webgl)
     , mFakeNoAlpha(ShouldFakeNoAlpha(webgl))
     , mFakeNoDepth(ShouldFakeNoDepth(webgl))
@@ -1815,239 +1864,29 @@ WebGLContext::ScopedMaskWorkaround::~Sco
     if (mFakeNoDepth) {
         mWebGL.gl->fEnable(LOCAL_GL_DEPTH_TEST);
     }
     if (mFakeNoStencil) {
         mWebGL.gl->fEnable(LOCAL_GL_STENCIL_TEST);
     }
 }
 
-////////////////////////////////////////
-
-ScopedUnpackReset::ScopedUnpackReset(WebGLContext* webgl)
-    : ScopedGLWrapper<ScopedUnpackReset>(webgl->gl)
-    , mWebGL(webgl)
-{
-    if (mWebGL->mPixelStore_UnpackAlignment != 4) mGL->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4);
-
-    if (mWebGL->IsWebGL2()) {
-        if (mWebGL->mPixelStore_UnpackRowLength   != 0) mGL->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH  , 0);
-        if (mWebGL->mPixelStore_UnpackImageHeight != 0) mGL->fPixelStorei(LOCAL_GL_UNPACK_IMAGE_HEIGHT, 0);
-        if (mWebGL->mPixelStore_UnpackSkipPixels  != 0) mGL->fPixelStorei(LOCAL_GL_UNPACK_SKIP_PIXELS , 0);
-        if (mWebGL->mPixelStore_UnpackSkipRows    != 0) mGL->fPixelStorei(LOCAL_GL_UNPACK_SKIP_ROWS   , 0);
-        if (mWebGL->mPixelStore_UnpackSkipImages  != 0) mGL->fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES , 0);
-
-        if (mWebGL->mBoundPixelUnpackBuffer) mGL->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, 0);
-    }
-}
-
-void
-ScopedUnpackReset::UnwrapImpl()
-{
-    mGL->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, mWebGL->mPixelStore_UnpackAlignment);
-
-    if (mWebGL->IsWebGL2()) {
-        mGL->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH  , mWebGL->mPixelStore_UnpackRowLength  );
-        mGL->fPixelStorei(LOCAL_GL_UNPACK_IMAGE_HEIGHT, mWebGL->mPixelStore_UnpackImageHeight);
-        mGL->fPixelStorei(LOCAL_GL_UNPACK_SKIP_PIXELS , mWebGL->mPixelStore_UnpackSkipPixels );
-        mGL->fPixelStorei(LOCAL_GL_UNPACK_SKIP_ROWS   , mWebGL->mPixelStore_UnpackSkipRows   );
-        mGL->fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES , mWebGL->mPixelStore_UnpackSkipImages );
-
-        GLuint pbo = 0;
-        if (mWebGL->mBoundPixelUnpackBuffer) {
-            pbo = mWebGL->mBoundPixelUnpackBuffer->mGLName;
-        }
-
-        mGL->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, pbo);
-    }
-}
-
-////////////////////////////////////////
-
-void
-Intersect(uint32_t srcSize, int32_t dstStartInSrc, uint32_t dstSize,
-          uint32_t* const out_intStartInSrc, uint32_t* const out_intStartInDst,
-          uint32_t* const out_intSize)
-{
-    // Only >0 if dstStartInSrc is >0:
-    // 0  3          // src coords
-    // |  [========] // dst box
-    // ^--^
-    *out_intStartInSrc = std::max<int32_t>(0, dstStartInSrc);
-
-    // Only >0 if dstStartInSrc is <0:
-    //-6     0       // src coords
-    // [=====|==]    // dst box
-    // ^-----^
-    *out_intStartInDst = std::max<int32_t>(0, 0 - dstStartInSrc);
-
-    int32_t intEndInSrc = std::min<int32_t>(srcSize, dstStartInSrc + dstSize);
-    *out_intSize = std::max<int32_t>(0, intEndInSrc - *out_intStartInSrc);
-}
-
-bool
-ZeroTextureData(WebGLContext* webgl, const char* funcName, bool respecifyTexture,
-                TexImageTarget target, uint32_t level,
-                const webgl::FormatUsageInfo* usage, uint32_t xOffset, uint32_t yOffset,
-                uint32_t zOffset, uint32_t width, uint32_t height, uint32_t depth)
-{
-    // This has two usecases:
-    // 1. Lazy zeroing of uninitialized textures:
-    //    a. Before draw, when FakeBlack isn't viable. (TexStorage + Draw*)
-    //    b. Before partial upload. (TexStorage + TexSubImage)
-    // 2. Zero subrects from out-of-bounds blits. (CopyTex(Sub)Image)
-
-    // We have no sympathy for any of these cases.
-
-    // "Doctor, it hurts when I do this!" "Well don't do that!"
-    webgl->GenerateWarning("%s: This operation requires zeroing texture data. This is"
-                           " slow.",
-                           funcName);
-
-    gl::GLContext* gl = webgl->GL();
-    gl->MakeCurrent();
-
-    ScopedUnpackReset scopedReset(webgl);
-    gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1); // Don't bother with striding it well.
-
-    auto compression = usage->format->compression;
-    if (compression) {
-        MOZ_RELEASE_ASSERT(!xOffset && !yOffset && !zOffset);
-        MOZ_RELEASE_ASSERT(!respecifyTexture);
-
-        auto sizedFormat = usage->format->sizedFormat;
-        MOZ_RELEASE_ASSERT(sizedFormat);
-
-        const auto fnSizeInBlocks = [](CheckedUint32 pixels, uint8_t pixelsPerBlock) {
-            return RoundUpToMultipleOf(pixels, pixelsPerBlock) / pixelsPerBlock;
-        };
-
-        const auto widthBlocks = fnSizeInBlocks(width, compression->blockWidth);
-        const auto heightBlocks = fnSizeInBlocks(height, compression->blockHeight);
-
-        CheckedUint32 checkedByteCount = compression->bytesPerBlock;
-        checkedByteCount *= widthBlocks;
-        checkedByteCount *= heightBlocks;
-        checkedByteCount *= depth;
-
-        if (!checkedByteCount.isValid())
-            return false;
-
-        const size_t byteCount = checkedByteCount.value();
-
-        UniqueBuffer zeros = calloc(1, byteCount);
-        if (!zeros)
-            return false;
-
-        GLenum error = DoCompressedTexSubImage(gl, target.get(), level, xOffset, yOffset,
-                                               zOffset, width, height, depth, sizedFormat,
-                                               byteCount, zeros.get());
-        if (error)
-            return false;
-
-        return true;
-    }
-
-    const auto driverUnpackInfo = usage->idealUnpack;
-    MOZ_RELEASE_ASSERT(driverUnpackInfo);
-
-    const webgl::PackingInfo packing = driverUnpackInfo->ToPacking();
-
-    const auto bytesPerPixel = webgl::BytesPerPixel(packing);
-
-    CheckedUint32 checkedByteCount = bytesPerPixel;
-    checkedByteCount *= width;
-    checkedByteCount *= height;
-    checkedByteCount *= depth;
-
-    if (!checkedByteCount.isValid())
-        return false;
-
-    const size_t byteCount = checkedByteCount.value();
-
-    UniqueBuffer zeros = calloc(1, byteCount);
-    if (!zeros)
-        return false;
-
-    GLenum error;
-    if (respecifyTexture) {
-        MOZ_RELEASE_ASSERT(!xOffset && !yOffset && !zOffset);
-        error = DoTexImage(gl, target, level, driverUnpackInfo, width, height, depth,
-                           zeros.get());
-    } else {
-        error = DoTexSubImage(gl, target, level, xOffset, yOffset, zOffset, width, height,
-                              depth, packing, zeros.get());
-    }
-    if (error)
-        return false;
-
-    return true;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-CheckedUint32
-WebGLContext::GetUnpackSize(bool isFunc3D, uint32_t width, uint32_t height,
-                            uint32_t depth, uint8_t bytesPerPixel)
-{
-    if (!width || !height || !depth)
-        return 0;
-
-    ////////////////
-
-    const auto& maybeRowLength = mPixelStore_UnpackRowLength;
-    const auto& maybeImageHeight = mPixelStore_UnpackImageHeight;
-
-    const auto usedPixelsPerRow = CheckedUint32(mPixelStore_UnpackSkipPixels) + width;
-    const auto stridePixelsPerRow = (maybeRowLength ? CheckedUint32(maybeRowLength)
-                                                    : usedPixelsPerRow);
-
-    const auto usedRowsPerImage = CheckedUint32(mPixelStore_UnpackSkipRows) + height;
-    const auto strideRowsPerImage = (maybeImageHeight ? CheckedUint32(maybeImageHeight)
-                                                      : usedRowsPerImage);
-
-    const uint32_t skipImages = (isFunc3D ? mPixelStore_UnpackSkipImages
-                                          : 0);
-    const CheckedUint32 usedImages = CheckedUint32(skipImages) + depth;
-
-    ////////////////
-
-    CheckedUint32 strideBytesPerRow = bytesPerPixel * stridePixelsPerRow;
-    strideBytesPerRow = RoundUpToMultipleOf(strideBytesPerRow,
-                                            mPixelStore_UnpackAlignment);
-
-    const CheckedUint32 strideBytesPerImage = strideBytesPerRow * strideRowsPerImage;
-
-    ////////////////
-
-    CheckedUint32 usedBytesPerRow = bytesPerPixel * usedPixelsPerRow;
-    // Don't round this to the alignment, since alignment here is really just used for
-    // establishing stride, particularly in WebGL 1, where you can't set ROW_LENGTH.
-
-    CheckedUint32 totalBytes = strideBytesPerImage * (usedImages - 1);
-    totalBytes += strideBytesPerRow * (usedRowsPerImage - 1);
-    totalBytes += usedBytesPerRow;
-
-    return totalBytes;
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 // XPCOM goop
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(WebGLContext)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(WebGLContext)
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLContext,
   mCanvasElement,
   mOffscreenCanvas,
   mExtensions,
   mBound2DTextures,
   mBoundCubeMapTextures,
   mBound3DTextures,
-  mBound2DArrayTextures,
   mBoundSamplers,
   mBoundArrayBuffer,
   mBoundCopyReadBuffer,
   mBoundCopyWriteBuffer,
   mBoundPixelPackBuffer,
   mBoundPixelUnpackBuffer,
   mBoundTransformFeedbackBuffer,
   mBoundUniformBuffer,
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -21,29 +21,26 @@
 #include "mozilla/UniquePtr.h"
 #include "mozilla/WeakPtr.h"
 #include "nsCycleCollectionNoteChild.h"
 #include "nsICanvasRenderingContextInternal.h"
 #include "nsLayoutUtils.h"
 #include "nsTArray.h"
 #include "nsWrapperCache.h"
 #include "SurfaceTypes.h"
-#include "ScopedGLHelpers.h"
-#include "TexUnpackBlob.h"
 
 #ifdef XP_MACOSX
 #include "ForceDiscreteGPUHelperCGL.h"
 #endif
 
 // Local
 #include "WebGLContextUnchecked.h"
 #include "WebGLFormats.h"
 #include "WebGLObjectModel.h"
 #include "WebGLStrongTypes.h"
-#include "WebGLTexture.h"
 
 // Generated
 #include "nsIDOMEventListener.h"
 #include "nsIDOMWebGLRenderingContext.h"
 #include "nsICanvasRenderingContextInternal.h"
 #include "nsIObserver.h"
 #include "mozilla/dom/HTMLCanvasElement.h"
 #include "nsWrapperCache.h"
@@ -66,35 +63,27 @@ class nsIDocShell;
 #define MINVALUE_GL_MAX_VERTEX_UNIFORM_VECTORS        128   // Page 164
 #define MINVALUE_GL_MAX_VARYING_VECTORS               8     // Page 164
 #define MINVALUE_GL_MAX_TEXTURE_IMAGE_UNITS           8     // Page 164
 #define MINVALUE_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS    0     // Page 164
 #define MINVALUE_GL_MAX_RENDERBUFFER_SIZE             1024  // Different from the spec, which sets it to 1 on page 164
 #define MINVALUE_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS  8     // Page 164
 
 /*
- * Minimum value constants define in 6.2 State Tables of OpenGL ES - 3.0.4
- */
-#define MINVALUE_GL_MAX_3D_TEXTURE_SIZE             256
-#define MINVALUE_GL_MAX_ARRAY_TEXTURE_LAYERS        256
-
-/*
  * WebGL-only GLenums
  */
 #define LOCAL_GL_BROWSER_DEFAULT_WEBGL                       0x9244
 #define LOCAL_GL_CONTEXT_LOST_WEBGL                          0x9242
 #define LOCAL_GL_MAX_CLIENT_WAIT_TIMEOUT_WEBGL               0x9247
 #define LOCAL_GL_UNPACK_COLORSPACE_CONVERSION_WEBGL          0x9243
 #define LOCAL_GL_UNPACK_FLIP_Y_WEBGL                         0x9240
 #define LOCAL_GL_UNPACK_PREMULTIPLY_ALPHA_WEBGL              0x9241
 
 namespace mozilla {
-class ScopedCopyTexImageSource;
-class ScopedResolveTexturesForDraw;
-class ScopedUnpackReset;
+
 class WebGLActiveInfo;
 class WebGLContextLossHandler;
 class WebGLBuffer;
 class WebGLExtensionBase;
 class WebGLFramebuffer;
 class WebGLProgram;
 class WebGLQuery;
 class WebGLRenderbuffer;
@@ -117,17 +106,16 @@ template<typename> struct Nullable;
 
 namespace gfx {
 class SourceSurface;
 } // namespace gfx
 
 namespace webgl {
 struct LinkedProgramInfo;
 class ShaderValidator;
-class TexUnpackBlob;
 } // namespace webgl
 
 WebGLTexelFormat GetWebGLTexelFormat(TexInternalFormat format);
 
 void AssertUintParamCorrect(gl::GLContext* gl, GLenum pname, GLuint shadow);
 
 struct WebGLContextOptions
 {
@@ -207,19 +195,16 @@ class WebGLContext
         UNPACK_PREMULTIPLY_ALPHA_WEBGL = 0x9241,
         CONTEXT_LOST_WEBGL = 0x9242,
         UNPACK_COLORSPACE_CONVERSION_WEBGL = 0x9243,
         BROWSER_DEFAULT_WEBGL = 0x9244,
         UNMASKED_VENDOR_WEBGL = 0x9245,
         UNMASKED_RENDERER_WEBGL = 0x9246
     };
 
-    static const uint32_t kMinMaxColorAttachments;
-    static const uint32_t kMinMaxDrawBuffers;
-
 public:
     WebGLContext();
 
 protected:
     virtual ~WebGLContext();
 
 public:
     MOZ_DECLARE_WEAKREFERENCE_TYPENAME(WebGLContext)
@@ -310,18 +295,16 @@ public:
     WebGLTexture* ActiveBoundTextureForTarget(const TexTarget texTarget) const {
         switch (texTarget.get()) {
         case LOCAL_GL_TEXTURE_2D:
             return mBound2DTextures[mActiveTexture];
         case LOCAL_GL_TEXTURE_CUBE_MAP:
             return mBoundCubeMapTextures[mActiveTexture];
         case LOCAL_GL_TEXTURE_3D:
             return mBound3DTextures[mActiveTexture];
-        case LOCAL_GL_TEXTURE_2D_ARRAY:
-            return mBound2DArrayTextures[mActiveTexture];
         default:
             MOZ_CRASH("bad target");
         }
     }
 
     /* Use this function when you have the texture image target, for example:
      * GL_TEXTURE_2D, GL_TEXTURE_CUBE_MAP_[POSITIVE|NEGATIVE]_[X|Y|Z], and
      * not the actual texture binding target: GL_TEXTURE_2D or GL_TEXTURE_CUBE_MAP.
@@ -352,21 +335,28 @@ public:
     bool IsPreservingDrawingBuffer() const { return mOptions.preserveDrawingBuffer; }
 
     bool PresentScreenBuffer();
 
     // a number that increments every time we have an event that causes
     // all context resources to be lost.
     uint32_t Generation() { return mGeneration.value(); }
 
+    // Returns null if the current bound FB is not likely complete.
+    const WebGLRectangleObject* CurValidDrawFBRectObject() const;
+    const WebGLRectangleObject* CurValidReadFBRectObject() const;
+
+    static const size_t kMaxColorAttachments = 16;
+
     // This is similar to GLContext::ClearSafely, but tries to minimize the
     // amount of work it does.
     // It only clears the buffers we specify, and can reset its state without
     // first having to query anything, as WebGL knows its state at all times.
-    void ForceClearFramebufferWithDefaultValues(GLbitfield bufferBits, bool fakeNoAlpha);
+    void ForceClearFramebufferWithDefaultValues(bool fakeNoAlpha, GLbitfield mask,
+                                                const bool colorAttachmentsMask[kMaxColorAttachments]);
 
     // Calls ForceClearFramebufferWithDefaultValues() for the Context's 'screen'.
     void ClearScreen();
     void ClearBackbufferIfNeeded();
 
     bool MinCapabilityMode() const { return mMinCapability; }
 
     void RunContextLossTimer();
@@ -439,19 +429,18 @@ public:
     void Finish();
     void FramebufferRenderbuffer(GLenum target, GLenum attachment,
                                  GLenum rbTarget, WebGLRenderbuffer* rb);
     void FramebufferTexture2D(GLenum target, GLenum attachment,
                               GLenum texImageTarget, WebGLTexture* tex,
                               GLint level);
 
     // Framebuffer validation
-    bool ValidateFramebufferAttachment(const WebGLFramebuffer* fb, GLenum attachment,
-                                       const char* funcName,
-                                       bool badColorAttachmentIsInvalidOp = false);
+    bool ValidateFramebufferAttachment(const WebGLFramebuffer* fb,
+                                       GLenum attachment, const char* funcName);
 
     void FrontFace(GLenum mode);
     already_AddRefed<WebGLActiveInfo> GetActiveAttrib(WebGLProgram* prog,
                                                       GLuint index);
     already_AddRefed<WebGLActiveInfo> GetActiveUniform(WebGLProgram* prog,
                                                        GLuint index);
 
     void
@@ -533,21 +522,16 @@ public:
     bool IsProgram(WebGLProgram* prog);
     bool IsRenderbuffer(WebGLRenderbuffer* rb);
     bool IsShader(WebGLShader* shader);
     bool IsVertexArray(WebGLVertexArray* vao);
     void LineWidth(GLfloat width);
     void LinkProgram(WebGLProgram* prog);
     void PixelStorei(GLenum pname, GLint param);
     void PolygonOffset(GLfloat factor, GLfloat units);
-protected:
-    bool DoReadPixelsAndConvert(GLint x, GLint y, GLsizei width, GLsizei height,
-                                GLenum destFormat, GLenum destType, void* destBytes,
-                                GLenum auxReadFormat, GLenum auxReadType);
-public:
     void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height,
                     GLenum format, GLenum type,
                     const dom::Nullable<dom::ArrayBufferViewOrSharedArrayBufferView>& pixels,
                     ErrorResult& rv);
     void RenderbufferStorage(GLenum target, GLenum internalFormat,
                              GLsizei width, GLsizei height);
 protected:
     void RenderbufferStorage_base(const char* funcName, GLenum target,
@@ -900,72 +884,56 @@ public:
     void CopyTexSubImage2D(GLenum texImageTarget, GLint level, GLint xOffset,
                            GLint yOffset, GLint x, GLint y, GLsizei width,
                            GLsizei height);
 
     void TexImage2D(GLenum texImageTarget, GLint level, GLenum internalFormat,
                     GLsizei width, GLsizei height, GLint border, GLenum unpackFormat,
                     GLenum unpackType,
                     const dom::Nullable<dom::ArrayBufferViewOrSharedArrayBufferView>& maybeView,
-                    ErrorResult&);
+                    ErrorResult& out_rv);
     void TexImage2D(GLenum texImageTarget, GLint level, GLenum internalFormat,
                     GLenum unpackFormat, GLenum unpackType, dom::ImageData* imageData,
-                    ErrorResult&);
+                    ErrorResult& out_rv);
     void TexImage2D(GLenum texImageTarget, GLint level, GLenum internalFormat,
                     GLenum unpackFormat, GLenum unpackType, dom::Element* elem,
-                    ErrorResult* const out_error);
+                    ErrorResult* const out_rv);
+
 
     void TexSubImage2D(GLenum texImageTarget, GLint level, GLint xOffset, GLint yOffset,
                        GLsizei width, GLsizei height, GLenum unpackFormat,
                        GLenum unpackType,
                        const dom::Nullable<dom::ArrayBufferViewOrSharedArrayBufferView>& maybeView,
-                       ErrorResult&);
+                       ErrorResult& out_rv);
     void TexSubImage2D(GLenum texImageTarget, GLint level, GLint xOffset, GLint yOffset,
                        GLenum unpackFormat, GLenum unpackType, dom::ImageData* imageData,
-                       ErrorResult&);
+                       ErrorResult& out_rv);
     void TexSubImage2D(GLenum texImageTarget, GLint level, GLint xOffset, GLint yOffset,
                        GLenum unpackFormat, GLenum unpackType, dom::Element* elem,
-                       ErrorResult* const out_error);
+                       ErrorResult* const out_rv);
 
     // Allow whatever element unpackTypes the bindings are willing to pass
     // us in Tex(Sub)Image2D
-    template<typename T>
-    inline void
-    TexImage2D(GLenum texImageTarget, GLint level, GLenum internalFormat,
-               GLenum unpackFormat, GLenum unpackType, T& elem, ErrorResult& out_error)
+    template<typename ElementT>
+    void TexImage2D(GLenum texImageTarget, GLint level, GLenum internalFormat,
+                    GLenum unpackFormat, GLenum unpackType, ElementT& elem,
+                    ErrorResult& out_rv)
     {
         TexImage2D(texImageTarget, level, internalFormat, unpackFormat, unpackType, &elem,
-                   &out_error);
+                   &out_rv);
     }
-
-    template<typename T>
-    inline void
-    TexSubImage2D(GLenum texImageTarget, GLint level, GLint xOffset, GLint yOffset,
-                  GLenum unpackFormat, GLenum unpackType, T& elem, ErrorResult& out_error)
+    template<typename ElementT>
+    void TexSubImage2D(GLenum texImageTarget, GLint level, GLint xOffset, GLint yOffset,
+                       GLenum unpackFormat, GLenum unpackType, ElementT& elem,
+                       ErrorResult& out_rv)
     {
         TexSubImage2D(texImageTarget, level, xOffset, yOffset, unpackFormat, unpackType,
-                      &elem, &out_error);
+                      &elem, &out_rv);
     }
 
-    // WebGLTextureUpload.cpp
-    bool ValidateTexImageSpecification(const char* funcName, uint8_t funcDims,
-                                       GLenum texImageTarget, GLint level,
-                                       GLsizei width, GLsizei height, GLsizei depth,
-                                       GLint border,
-                                       TexImageTarget* const out_target,
-                                       WebGLTexture** const out_texture,
-                                       WebGLTexture::ImageInfo** const out_imageInfo);
-    bool ValidateTexImageSelection(const char* funcName, uint8_t funcDims,
-                                   GLenum texImageTarget, GLint level, GLint xOffset,
-                                   GLint yOffset, GLint zOffset, GLsizei width,
-                                   GLsizei height, GLsizei depth,
-                                   TexImageTarget* const out_target,
-                                   WebGLTexture** const out_texture,
-                                   WebGLTexture::ImageInfo** const out_imageInfo);
-
 // -----------------------------------------------------------------------------
 // Vertices Feature (WebGLContextVertices.cpp)
 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);
@@ -1038,37 +1006,51 @@ private:
     uint32_t mMaxFetchedInstances;
 
     bool DrawArrays_check(GLint first, GLsizei count, GLsizei primcount,
                           const char* info);
     bool DrawElements_check(GLsizei count, GLenum type, WebGLintptr byteOffset,
                             GLsizei primcount, const char* info,
                             GLuint* out_upperBound);
     bool DrawInstanced_check(const char* info);
-    void Draw_cleanup(const char* funcName);
+    void Draw_cleanup();
 
     void VertexAttrib1fv_base(GLuint index, uint32_t arrayLength,
                               const GLfloat* ptr);
     void VertexAttrib2fv_base(GLuint index, uint32_t arrayLength,
                               const GLfloat* ptr);
     void VertexAttrib3fv_base(GLuint index, uint32_t arrayLength,
                               const GLfloat* ptr);
     void VertexAttrib4fv_base(GLuint index, uint32_t arrayLength,
                               const GLfloat* ptr);
 
     bool ValidateBufferFetching(const char* info);
     bool BindArrayAttribToLocation0(WebGLProgram* prog);
 
 // -----------------------------------------------------------------------------
 // PROTECTED
 protected:
+    void SetFakeBlackStatus(WebGLContextFakeBlackStatus x) {
+        mFakeBlackStatus = x;
+    }
+    // Returns the current fake-black-status, except if it was Unknown,
+    // in which case this function resolves it first, so it never returns Unknown.
+    WebGLContextFakeBlackStatus ResolvedFakeBlackStatus();
+
+    void BindFakeBlackTextures();
+    void UnbindFakeBlackTextures();
+
     WebGLVertexAttrib0Status WhatDoesVertexAttrib0Need();
     bool DoFakeVertexAttrib0(GLuint vertexCount);
     void UndoFakeVertexAttrib0();
 
+    static CheckedUint32 GetImageSize(GLsizei height, GLsizei width,
+                                      GLsizei depth, uint32_t pixelSize,
+                                      uint32_t alignment);
+
     inline void InvalidateBufferFetching()
     {
         mBufferFetchingIsVerified = false;
         mBufferFetchingHasPerVertex = false;
         mMaxFetchedVertices = 0;
         mMaxFetchedInstances = 0;
     }
 
@@ -1089,72 +1071,59 @@ protected:
     bool mShouldPresent;
     bool mBackbufferNeedsClear;
     bool mDisableFragHighP;
 
     template<typename WebGLObjectType>
     void DeleteWebGLObjectsArray(nsTArray<WebGLObjectType>& array);
 
     GLuint mActiveTexture;
-    GLenum mDefaultFB_DrawBuffer0;
 
     // glGetError sources:
     bool mEmitContextLostErrorOnce;
     GLenum mWebGLError;
     GLenum mUnderlyingGLError;
     GLenum GetAndFlushUnderlyingGLErrors();
 
     bool mBypassShaderValidation;
 
     webgl::ShaderValidator* CreateShaderValidator(GLenum shaderType) const;
 
     // some GL constants
     int32_t mGLMaxVertexAttribs;
     int32_t mGLMaxTextureUnits;
+    int32_t mGLMaxTextureSize;
+    int32_t mGLMaxTextureSizeLog2;
+    int32_t mGLMaxCubeMapTextureSize;
+    int32_t mGLMaxCubeMapTextureSizeLog2;
+    int32_t mGLMaxRenderbufferSize;
     int32_t mGLMaxTextureImageUnits;
     int32_t mGLMaxVertexTextureImageUnits;
     int32_t mGLMaxVaryingVectors;
     int32_t mGLMaxFragmentUniformVectors;
     int32_t mGLMaxVertexUniformVectors;
+    int32_t mGLMaxColorAttachments;
+    int32_t mGLMaxDrawBuffers;
     uint32_t  mGLMaxTransformFeedbackSeparateAttribs;
     GLuint  mGLMaxUniformBufferBindings;
     GLsizei mGLMaxSamples;
-
-    // What is supported:
-    uint32_t mGLMaxColorAttachments;
-    uint32_t mGLMaxDrawBuffers;
-    // What we're allowing:
-    uint32_t mImplMaxColorAttachments;
-    uint32_t mImplMaxDrawBuffers;
-
-public:
-    GLenum LastColorAttachmentEnum() const {
-        return LOCAL_GL_COLOR_ATTACHMENT0 + mImplMaxColorAttachments - 1;
-    }
-
-protected:
-
-    // Texture sizes are often not actually the GL values. Let's be explicit that these
-    // are implementation limits.
-    uint32_t mImplMaxTextureSize;
-    uint32_t mImplMaxCubeMapTextureSize;
-    uint32_t mImplMax3DTextureSize;
-    uint32_t mImplMaxArrayTextureLayers;
-    uint32_t mImplMaxRenderbufferSize;
+    GLuint  mGLMax3DTextureSize;
+    GLuint  mGLMaxArrayTextureLayers;
 
 public:
     GLuint MaxVertexAttribs() const {
         return mGLMaxVertexAttribs;
     }
 
     GLuint GLMaxTextureUnits() const {
         return mGLMaxTextureUnits;
     }
 
-    bool IsFormatValidForFB(TexInternalFormat format) const;
+
+    bool IsFormatValidForFB(GLenum sizedFormat) const;
 
 protected:
     // Represents current status of the context with respect to context loss.
     // That is, whether the context is lost, and what part of the context loss
     // process we currently are at.
     // This is used to support the WebGL spec's asyncronous nature in handling
     // context loss.
     enum ContextStatus {
@@ -1181,21 +1150,19 @@ protected:
 
     // enable an extension. the extension should not be enabled before.
     void EnableExtension(WebGLExtensionID ext);
 
     // Enable an extension if it's supported. Return the extension on success.
     WebGLExtensionBase* EnableSupportedExtension(JSContext* js,
                                                  WebGLExtensionID ext);
 
-public:
     // returns true if the extension has been enabled by calling getExtension.
     bool IsExtensionEnabled(WebGLExtensionID ext) const;
 
-protected:
     // returns true if the extension is supported for this JSContext (this decides what getSupportedExtensions exposes)
     bool IsExtensionSupported(JSContext* cx, WebGLExtensionID ext) const;
     bool IsExtensionSupported(WebGLExtensionID ext) const;
 
     static const char* GetExtensionString(WebGLExtensionID ext);
 
     nsTArray<GLenum> mCompressedTextureFormats;
 
@@ -1235,18 +1202,18 @@ protected:
                               WebGLTexImageFunc func, WebGLTexDimensions dims);
     bool ValidateDrawModeEnum(GLenum mode, const char* info);
     bool ValidateAttribIndex(GLuint index, const char* info);
     bool ValidateAttribPointer(bool integerMode, GLuint index, GLint size, GLenum type,
                                WebGLboolean normalized, GLsizei stride,
                                WebGLintptr byteOffset, const char* info);
     bool ValidateStencilParamsForDrawCall();
 
-    bool ValidateCopyTexImage(TexInternalFormat srcFormat, TexInternalFormat dstformat,
-                              WebGLTexImageFunc func, WebGLTexDimensions dims);
+    bool ValidateCopyTexImage(GLenum internalFormat, WebGLTexImageFunc func,
+                              WebGLTexDimensions dims);
 
     bool ValidateSamplerParameterName(GLenum pname, const char* info);
     bool ValidateSamplerParameterParams(GLenum pname, const WebGLIntOrFloat& param, const char* info);
 
     bool ValidateTexImage(TexImageTarget texImageTarget,
                           GLint level, GLenum internalFormat,
                           GLint xoffset, GLint yoffset, GLint zoffset,
                           GLint width, GLint height, GLint depth,
@@ -1283,50 +1250,54 @@ protected:
                                       uint32_t byteLength,
                                       WebGLTexImageFunc func,
                                       WebGLTexDimensions dims);
 
     bool ValidateUniformLocationForProgram(WebGLUniformLocation* location,
                                            WebGLProgram* program,
                                            const char* funcName);
 
-    bool ValidateCurFBForRead(const char* funcName,
-                              const webgl::FormatUsageInfo** const out_format,
-                              uint32_t* const out_width, uint32_t* const out_height);
-
     void Invalidate();
     void DestroyResourcesAndContext();
 
     void MakeContextCurrent() const;
 
     // helpers
 
     bool ConvertImage(size_t width, size_t height, size_t srcStride,
                       size_t dstStride, const uint8_t* src, uint8_t* dst,
                       WebGLTexelFormat srcFormat, bool srcPremultiplied,
                       WebGLTexelFormat dstFormat, bool dstPremultiplied,
                       size_t dstTexelSize);
 
-public:
+    template<class ElementType>
     nsLayoutUtils::SurfaceFromElementResult
-    SurfaceFromElement(dom::Element* elem)
-    {
+    SurfaceFromElement(ElementType* element) {
+        MOZ_ASSERT(element);
+
         uint32_t flags = nsLayoutUtils::SFE_WANT_IMAGE_SURFACE;
-
-        if (mPixelStore_ColorspaceConversion == LOCAL_GL_NONE)
+        if (mPixelStoreColorspaceConversion == LOCAL_GL_NONE)
             flags |= nsLayoutUtils::SFE_NO_COLORSPACE_CONVERSION;
-
-        if (!mPixelStore_PremultiplyAlpha)
+        if (!mPixelStorePremultiplyAlpha)
             flags |= nsLayoutUtils::SFE_PREFER_NO_PREMULTIPLY_ALPHA;
 
-        gfx::DrawTarget* idealDrawTarget = nullptr; // Don't care for now.
-        return nsLayoutUtils::SurfaceFromElement(elem, flags, idealDrawTarget);
+        return nsLayoutUtils::SurfaceFromElement(element, flags);
     }
 
-protected:
+    template<class ElementType>
+    nsLayoutUtils::SurfaceFromElementResult
+    SurfaceFromElement(ElementType& element) {
+       return SurfaceFromElement(&element);
+    }
+
+    nsresult
+    SurfaceFromElementResultToImageSurface(nsLayoutUtils::SurfaceFromElementResult& res,
+                                           RefPtr<gfx::DataSourceSurface>& imageOut,
+                                           WebGLTexelFormat* format);
+
     // Returns false if `object` is null or not valid.
     template<class ObjectType>
     bool ValidateObject(const char* info, ObjectType* object);
 
     // Returns false if `object` is not valid.  Considers null to be valid.
     template<class ObjectType>
     bool ValidateObjectAllowNull(const char* info, ObjectType* object);
 
@@ -1355,39 +1326,51 @@ private:
     virtual bool ValidateBufferTarget(GLenum target, const char* info) = 0;
     virtual bool ValidateBufferIndexedTarget(GLenum target, const char* info) = 0;
     virtual bool ValidateBufferForTarget(GLenum target, WebGLBuffer* buffer, const char* info);
     virtual bool ValidateBufferUsageEnum(GLenum usage, const char* info) = 0;
     virtual bool ValidateQueryTarget(GLenum usage, const char* info) = 0;
     virtual bool ValidateUniformMatrixTranspose(bool transpose, const char* info) = 0;
 
 protected:
+    int32_t MaxTextureSizeForTarget(TexTarget target) const {
+        return (target == LOCAL_GL_TEXTURE_2D) ? mGLMaxTextureSize
+                                               : mGLMaxCubeMapTextureSize;
+    }
+
+    int32_t
+    MaxTextureLevelForTexImageTarget(TexImageTarget texImageTarget) const {
+        const TexTarget target = TexImageTargetToTexTarget(texImageTarget);
+        return (target == LOCAL_GL_TEXTURE_2D) ? mGLMaxTextureSizeLog2
+                                               : mGLMaxCubeMapTextureSizeLog2;
+    }
+
     /** Like glBufferData, but if the call may change the buffer size, checks
      *  any GL error generated by this glBufferData call and returns it.
      */
     GLenum CheckedBufferData(GLenum target, GLsizeiptr size, const GLvoid* data,
                              GLenum usage);
 
-public:
     void ForceLoseContext(bool simulateLoss = false);
-
-protected:
     void ForceRestoreContext();
 
     nsTArray<WebGLRefPtr<WebGLTexture> > mBound2DTextures;
     nsTArray<WebGLRefPtr<WebGLTexture> > mBoundCubeMapTextures;
     nsTArray<WebGLRefPtr<WebGLTexture> > mBound3DTextures;
-    nsTArray<WebGLRefPtr<WebGLTexture> > mBound2DArrayTextures;
     nsTArray<WebGLRefPtr<WebGLSampler> > mBoundSamplers;
 
     void ResolveTexturesForDraw() const;
 
     WebGLRefPtr<WebGLProgram> mCurrentProgram;
     RefPtr<const webgl::LinkedProgramInfo> mActiveProgramLinkInfo;
 
+    GLenum LastColorAttachment() const {
+        return LOCAL_GL_COLOR_ATTACHMENT0 + mGLMaxColorAttachments - 1;
+    }
+
     bool ValidateFramebufferTarget(GLenum target, const char* const info);
 
     WebGLRefPtr<WebGLFramebuffer> mBoundDrawFramebuffer;
     WebGLRefPtr<WebGLFramebuffer> mBoundReadFramebuffer;
     WebGLRefPtr<WebGLRenderbuffer> mBoundRenderbuffer;
     WebGLRefPtr<WebGLTransformFeedback> mBoundTransformFeedback;
     WebGLRefPtr<WebGLVertexArray> mBoundVertexArray;
 
@@ -1403,60 +1386,44 @@ protected:
     // TODO(djg): Does this need a rethink? Should it be WebGL2Context?
     LinkedList<WebGLSampler> mSamplers;
     LinkedList<WebGLTransformFeedback> mTransformFeedbacks;
 
     WebGLRefPtr<WebGLTransformFeedback> mDefaultTransformFeedback;
     WebGLRefPtr<WebGLVertexArray> mDefaultVertexArray;
 
     // PixelStore parameters
-    uint32_t mPixelStore_UnpackImageHeight;
-    uint32_t mPixelStore_UnpackSkipImages;
-    uint32_t mPixelStore_UnpackRowLength;
-    uint32_t mPixelStore_UnpackSkipRows;
-    uint32_t mPixelStore_UnpackSkipPixels;
-    uint32_t mPixelStore_UnpackAlignment;
-    uint32_t mPixelStore_PackRowLength;
-    uint32_t mPixelStore_PackSkipRows;
-    uint32_t mPixelStore_PackSkipPixels;
-    uint32_t mPixelStore_PackAlignment;
+    uint32_t mPixelStorePackAlignment;
+    uint32_t mPixelStoreUnpackAlignment;
+    uint32_t mPixelStoreColorspaceConversion;
+    bool mPixelStoreFlipY;
+    bool mPixelStorePremultiplyAlpha;
 
-    CheckedUint32 GetUnpackSize(bool isFunc3D, uint32_t width, uint32_t height,
-                                uint32_t depth, uint8_t bytesPerPixel);
+    WebGLContextFakeBlackStatus mFakeBlackStatus;
 
-    CheckedUint32 GetPackSize(uint32_t width, uint32_t height, uint8_t bytesPerPixel,
-                              CheckedUint32* const out_startOffset,
-                              CheckedUint32* const out_rowStride);
-
-    GLenum mPixelStore_ColorspaceConversion;
-    bool mPixelStore_FlipY;
-    bool mPixelStore_PremultiplyAlpha;
+    class FakeBlackTexture {
+        gl::GLContext* const mGL;
+        GLuint mGLName;
 
-    ////////////////////////////////////
-    class FakeBlackTexture {
     public:
-        gl::GLContext* const mGL;
-        const GLuint mGLName;
-
-        FakeBlackTexture(gl::GLContext* gl, TexTarget target, FakeBlackType type);
+        FakeBlackTexture(gl::GLContext* gl, TexTarget target, GLenum format);
         ~FakeBlackTexture();
+        GLuint GLName() const { return mGLName; }
     };
 
-    UniquePtr<FakeBlackTexture> mFakeBlack_2D_0000;
-    UniquePtr<FakeBlackTexture> mFakeBlack_2D_0001;
-    UniquePtr<FakeBlackTexture> mFakeBlack_CubeMap_0000;
-    UniquePtr<FakeBlackTexture> mFakeBlack_CubeMap_0001;
-    UniquePtr<FakeBlackTexture> mFakeBlack_3D_0000;
-    UniquePtr<FakeBlackTexture> mFakeBlack_3D_0001;
-    UniquePtr<FakeBlackTexture> mFakeBlack_2D_Array_0000;
-    UniquePtr<FakeBlackTexture> mFakeBlack_2D_Array_0001;
+    UniquePtr<FakeBlackTexture> mBlackOpaqueTexture2D;
+    UniquePtr<FakeBlackTexture> mBlackOpaqueTextureCubeMap;
+    UniquePtr<FakeBlackTexture> mBlackTransparentTexture2D;
+    UniquePtr<FakeBlackTexture> mBlackTransparentTextureCubeMap;
 
-    void BindFakeBlack(uint32_t texUnit, TexTarget target, FakeBlackType fakeBlack);
-
-    ////////////////////////////////////
+    void
+    BindFakeBlackTexturesHelper(GLenum target,
+                                const nsTArray<WebGLRefPtr<WebGLTexture> >& boundTexturesArray,
+                                UniquePtr<FakeBlackTexture>& opaqueTextureScopedPtr,
+                                UniquePtr<FakeBlackTexture>& transparentTextureScopedPtr);
 
     // Generic Vertex Attributes
     UniquePtr<GLenum[]> mVertexAttribType;
     GLfloat mVertexAttrib0Vector[4];
     GLfloat mFakeVertexAttrib0BufferObjectVector[4];
     size_t mFakeVertexAttrib0BufferObjectSize;
     GLuint mFakeVertexAttrib0BufferObject;
     WebGLVertexAttrib0Status mFakeVertexAttrib0BufferStatus;
@@ -1562,29 +1529,20 @@ protected:
 
 public:
     // console logging helpers
     void GenerateWarning(const char* fmt, ...);
     void GenerateWarning(const char* fmt, va_list ap);
 
 public:
     UniquePtr<webgl::FormatUsageAuthority> mFormatUsage;
-
-    virtual UniquePtr<webgl::FormatUsageAuthority>
-    CreateFormatUsage(gl::GLContext* gl) const = 0;
+    virtual UniquePtr<webgl::FormatUsageAuthority> CreateFormatUsage() const = 0;
 
     // Friend list
-    friend class ScopedCopyTexImageSource;
-    friend class ScopedResolveTexturesForDraw;
-    friend class ScopedUnpackReset;
-    friend class webgl::TexUnpackBlob;
-    friend class webgl::TexUnpackBytes;
-    friend class webgl::TexUnpackSurface;
     friend class WebGLTexture;
-    friend class WebGLFBAttachPoint;
     friend class WebGLFramebuffer;
     friend class WebGLRenderbuffer;
     friend class WebGLProgram;
     friend class WebGLQuery;
     friend class WebGLBuffer;
     friend class WebGLSampler;
     friend class WebGLShader;
     friend class WebGLSync;
@@ -1668,104 +1626,29 @@ WebGLContext::ValidateObject(const char*
     if (!object) {
         ErrorInvalidValue("%s: null object passed as argument", info);
         return false;
     }
 
     return ValidateObjectAssumeNonNull(info, object);
 }
 
-// Returns `value` rounded to the next highest multiple of `multiple`.
-// AKA PadToAlignment, StrideForAlignment.
-template<typename V, typename M>
-V
-RoundUpToMultipleOf(const V& value, const M& multiple)
-{
-    return ((value + multiple - 1) / multiple) * multiple;
-}
+size_t RoundUpToMultipleOf(size_t value, size_t multiple);
 
 bool
-ValidateTexTarget(WebGLContext* webgl, const char* funcName, uint8_t funcDims,
-                  GLenum rawTexTarget, TexTarget* const out_texTarget,
-                  WebGLTexture** const out_tex);
+ValidateTexTarget(WebGLContext* webgl, GLenum rawTexTarget, const char* funcName,
+                  TexTarget* const out_texTarget, WebGLTexture** const out_tex);
 bool
-ValidateTexImageTarget(WebGLContext* webgl, const char* funcName, uint8_t funcDims,
-                       GLenum rawTexImageTarget, TexImageTarget* const out_texImageTarget,
+ValidateTexImageTarget(WebGLContext* webgl, GLenum rawTexImageTarget,
+                       const char* funcName, TexImageTarget* const out_texImageTarget,
                        WebGLTexture** const out_tex);
 
-class UniqueBuffer
-{
-    // Like UniquePtr<>, but for void* and malloc/calloc/free.
-    void* mBuffer;
-
-public:
-    UniqueBuffer()
-        : mBuffer(nullptr)
-    { }
-
-    MOZ_IMPLICIT UniqueBuffer(void* buffer)
-        : mBuffer(buffer)
-    { }
-
-    ~UniqueBuffer() {
-        free(mBuffer);
-    }
-
-    UniqueBuffer(UniqueBuffer&& other) {
-        this->mBuffer = other.mBuffer;
-        other.mBuffer = nullptr;
-    }
-
-    UniqueBuffer& operator =(UniqueBuffer&& other) {
-        free(this->mBuffer);
-        this->mBuffer = other.mBuffer;
-        other.mBuffer = nullptr;
-        return *this;
-    }
-
-    UniqueBuffer& operator =(void* newBuffer) {
-        free(this->mBuffer);
-        this->mBuffer = newBuffer;
-        return *this;
-    }
-
-    explicit operator bool() const { return bool(mBuffer); }
-
-    void* get() const { return mBuffer; }
-
-    UniqueBuffer(const UniqueBuffer& other) = delete; // construct using Move()!
-    void operator =(const UniqueBuffer& other) = delete; // assign using Move()!
-};
-
-class ScopedUnpackReset
-    : public gl::ScopedGLWrapper<ScopedUnpackReset>
-{
-    friend struct gl::ScopedGLWrapper<ScopedUnpackReset>;
-
-protected:
-    WebGLContext* const mWebGL;
-
-public:
-    explicit ScopedUnpackReset(WebGLContext* webgl);
-
-protected:
-    void UnwrapImpl();
-};
+// Returns x rounded to the next highest multiple of y.
+CheckedUint32 RoundedToNextMultipleOf(CheckedUint32 x, CheckedUint32 y);
 
 void
 ComputeLengthAndData(const dom::ArrayBufferViewOrSharedArrayBufferView& view,
                      void** const out_data, size_t* const out_length,
                      js::Scalar::Type* const out_type);
 
-void
-Intersect(uint32_t srcSize, int32_t dstStartInSrc, uint32_t dstSize,
-          uint32_t* const out_intStartInSrc, uint32_t* const out_intStartInDst,
-          uint32_t* const out_intSize);
-
-bool
-ZeroTextureData(WebGLContext* webgl, const char* funcName, bool respecifyTexture,
-                TexImageTarget target, uint32_t level,
-                const webgl::FormatUsageInfo* usage, uint32_t xOffset, uint32_t yOffset,
-                uint32_t zOffset, uint32_t width, uint32_t height, uint32_t depth);
-
 } // namespace mozilla
 
 #endif
--- a/dom/canvas/WebGLContextDraw.cpp
+++ b/dom/canvas/WebGLContextDraw.cpp
@@ -18,144 +18,23 @@
 #include "WebGLVertexArray.h"
 #include "WebGLVertexAttribData.h"
 
 namespace mozilla {
 
 // For a Tegra workaround.
 static const int MAX_DRAW_CALLS_SINCE_FLUSH = 100;
 
-////////////////////////////////////////
-
-class ScopedResolveTexturesForDraw
-{
-    struct TexRebindRequest
-    {
-        uint32_t texUnit;
-        WebGLTexture* tex;
-    };
-
-    WebGLContext* const mWebGL;
-    std::vector<TexRebindRequest> mRebindRequests;
-
-public:
-    ScopedResolveTexturesForDraw(WebGLContext* webgl, const char* funcName,
-                                 bool* const out_error);
-    ~ScopedResolveTexturesForDraw();
-};
-
-ScopedResolveTexturesForDraw::ScopedResolveTexturesForDraw(WebGLContext* webgl,
-                                                           const char* funcName,
-                                                           bool* const out_error)
-    : mWebGL(webgl)
-{
-    //typedef nsTArray<WebGLRefPtr<WebGLTexture>> TexturesT;
-    typedef decltype(WebGLContext::mBound2DTextures) TexturesT;
-
-    const auto fnResolveAll = [this, funcName, out_error](const TexturesT& textures)
-    {
-        const auto len = textures.Length();
-        for (uint32_t texUnit = 0; texUnit < len; ++texUnit) {
-            WebGLTexture* tex = textures[texUnit];
-            if (!tex)
-                continue;
-
-            FakeBlackType fakeBlack;
-            *out_error |= !tex->ResolveForDraw(funcName, texUnit, &fakeBlack);
-
-            if (fakeBlack == FakeBlackType::None)
-                continue;
-
-            mWebGL->BindFakeBlack(texUnit, tex->Target(), fakeBlack);
-            mRebindRequests.push_back({texUnit, tex});
-        }
-    };
-
-    *out_error = false;
-
-    fnResolveAll(mWebGL->mBound2DTextures);
-    fnResolveAll(mWebGL->mBoundCubeMapTextures);
-    fnResolveAll(mWebGL->mBound3DTextures);
-    fnResolveAll(mWebGL->mBound2DArrayTextures);
-
-    if (*out_error) {
-        mWebGL->ErrorOutOfMemory("%s: Failed to resolve textures for draw.", funcName);
-    }
-}
-
-ScopedResolveTexturesForDraw::~ScopedResolveTexturesForDraw()
-{
-    if (!mRebindRequests.size())
-        return;
-
-    gl::GLContext* gl = mWebGL->gl;
-
-    for (const auto& itr : mRebindRequests) {
-        gl->fActiveTexture(LOCAL_GL_TEXTURE0 + itr.texUnit);
-        gl->fBindTexture(itr.tex->Target().get(), itr.tex->mGLName);
-    }
-
-    gl->fActiveTexture(LOCAL_GL_TEXTURE0 + mWebGL->mActiveTexture);
-}
-
-void
-WebGLContext::BindFakeBlack(uint32_t texUnit, TexTarget target, FakeBlackType fakeBlack)
-{
-    MOZ_ASSERT(fakeBlack == FakeBlackType::RGBA0000 ||
-               fakeBlack == FakeBlackType::RGBA0001);
-
-    const auto fnGetSlot = [this, target, fakeBlack]() -> UniquePtr<FakeBlackTexture>*
-    {
-        switch (fakeBlack) {
-        case FakeBlackType::RGBA0000:
-            switch (target.get()) {
-            case LOCAL_GL_TEXTURE_2D      : return &mFakeBlack_2D_0000;
-            case LOCAL_GL_TEXTURE_CUBE_MAP: return &mFakeBlack_CubeMap_0000;
-            case LOCAL_GL_TEXTURE_3D      : return &mFakeBlack_3D_0000;
-            case LOCAL_GL_TEXTURE_2D_ARRAY: return &mFakeBlack_2D_Array_0000;
-            default: return nullptr;
-            }
-
-        case FakeBlackType::RGBA0001:
-            switch (target.get()) {
-            case LOCAL_GL_TEXTURE_2D      : return &mFakeBlack_2D_0001;
-            case LOCAL_GL_TEXTURE_CUBE_MAP: return &mFakeBlack_CubeMap_0001;
-            case LOCAL_GL_TEXTURE_3D      : return &mFakeBlack_3D_0001;
-            case LOCAL_GL_TEXTURE_2D_ARRAY: return &mFakeBlack_2D_Array_0001;
-            default: return nullptr;
-            }
-
-        default:
-            return nullptr;
-        }
-    };
-
-    UniquePtr<FakeBlackTexture>* slot = fnGetSlot();
-    if (!slot) {
-        MOZ_CRASH("fnGetSlot failed.");
-    }
-    UniquePtr<FakeBlackTexture>& fakeBlackTex = *slot;
-
-    if (!fakeBlackTex) {
-        fakeBlackTex.reset(new FakeBlackTexture(gl, target, fakeBlack));
-    }
-
-    gl->fActiveTexture(LOCAL_GL_TEXTURE0 + texUnit);
-    gl->fBindTexture(target.get(), fakeBlackTex->mGLName);
-    gl->fActiveTexture(LOCAL_GL_TEXTURE0 + mActiveTexture);
-}
-
-////////////////////////////////////////
-
 bool
 WebGLContext::DrawInstanced_check(const char* info)
 {
-    MOZ_ASSERT(IsWebGL2() ||
-               IsExtensionEnabled(WebGLExtensionID::ANGLE_instanced_arrays));
-    if (!mBufferFetchingHasPerVertex) {
+    if ((IsWebGL2() ||
+         IsExtensionEnabled(WebGLExtensionID::ANGLE_instanced_arrays)) &&
+        !mBufferFetchingHasPerVertex)
+    {
         /* http://www.khronos.org/registry/gles/extensions/ANGLE/ANGLE_instanced_arrays.txt
          *  If all of the enabled vertex attribute arrays that are bound to active
          *  generic attributes in the program have a non-zero divisor, the draw
          *  call should return INVALID_OPERATION.
          *
          * NB: This also appears to apply to NV_instanced_arrays, though the
          * INVALID_OPERATION emission is not explicitly stated.
          * ARB_instanced_arrays does not have this restriction.
@@ -227,76 +106,67 @@ WebGLContext::DrawArrays_check(GLint fir
     } else {
         ClearBackbufferIfNeeded();
     }
 
     if (!DoFakeVertexAttrib0(checked_firstPlusCount.value())) {
         return false;
     }
 
+    if (!DrawInstanced_check(info)) {
+        return false;
+    }
+
+    BindFakeBlackTextures();
+
     return true;
 }
 
 void
 WebGLContext::DrawArrays(GLenum mode, GLint first, GLsizei count)
 {
-    const char funcName[] = "drawArrays";
     if (IsContextLost())
         return;
 
-    if (!ValidateDrawModeEnum(mode, funcName))
+    if (!ValidateDrawModeEnum(mode, "drawArrays: mode"))
         return;
 
-    bool error;
-    ScopedResolveTexturesForDraw scopedResolve(this, funcName, &error);
-    if (error)
-        return;
-
-    if (!DrawArrays_check(first, count, 1, funcName))
+    if (!DrawArrays_check(first, count, 1, "drawArrays"))
         return;
 
     RunContextLossTimer();
 
     {
         ScopedMaskWorkaround autoMask(*this);
         gl->fDrawArrays(mode, first, count);
     }
 
-    Draw_cleanup(funcName);
+    Draw_cleanup();
 }
 
 void
 WebGLContext::DrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei primcount)
 {
-    const char funcName[] = "drawArraysInstanced";
     if (IsContextLost())
         return;
 
-    if (!ValidateDrawModeEnum(mode, funcName))
+    if (!ValidateDrawModeEnum(mode, "drawArraysInstanced: mode"))
         return;
 
-    bool error;
-    ScopedResolveTexturesForDraw scopedResolve(this, funcName, &error);
-    if (error)
-        return;
-
-    if (!DrawArrays_check(first, count, primcount, funcName))
-        return;
-
-    if (!DrawInstanced_check(funcName))
+    if (!DrawArrays_check(first, count, primcount, "drawArraysInstanced"))
         return;
 
     RunContextLossTimer();
 
     {
         ScopedMaskWorkaround autoMask(*this);
         gl->fDrawArraysInstanced(mode, first, count, primcount);
     }
 
-    Draw_cleanup(funcName);
+    Draw_cleanup();
 }
 
 bool
 WebGLContext::DrawElements_check(GLsizei count, GLenum type,
                                  WebGLintptr byteOffset, GLsizei primcount,
                                  const char* info, GLuint* out_upperBound)
 {
     if (count < 0 || byteOffset < 0) {
@@ -309,50 +179,49 @@ WebGLContext::DrawElements_check(GLsizei
         return false;
     }
 
     if (!ValidateStencilParamsForDrawCall()) {
         return false;
     }
 
     // If count is 0, there's nothing to do.
-    if (count == 0 || primcount == 0)
-        return false;
-
-    uint8_t bytesPerElem = 0;
-    switch (type) {
-    case LOCAL_GL_UNSIGNED_BYTE:
-        bytesPerElem = 1;
-        break;
-
-    case LOCAL_GL_UNSIGNED_SHORT:
-        bytesPerElem = 2;
-        break;
-
-    case LOCAL_GL_UNSIGNED_INT:
-        if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::OES_element_index_uint)) {
-            bytesPerElem = 4;
-        }
-        break;
-    }
-
-    if (!bytesPerElem) {
-        ErrorInvalidEnum("%s: Invalid `type`: 0x%04x", info, type);
+    if (count == 0 || primcount == 0) {
         return false;
     }
 
-    if (byteOffset % bytesPerElem != 0) {
-        ErrorInvalidOperation("%s: `byteOffset` must be a multiple of the size of `type`",
-                              info);
+    CheckedUint32 checked_byteCount;
+
+    GLsizei first = 0;
+
+    if (type == LOCAL_GL_UNSIGNED_SHORT) {
+        checked_byteCount = 2 * CheckedUint32(count);
+        if (byteOffset % 2 != 0) {
+            ErrorInvalidOperation("%s: invalid byteOffset for UNSIGNED_SHORT (must be a multiple of 2)", info);
+            return false;
+        }
+        first = byteOffset / 2;
+    }
+    else if (type == LOCAL_GL_UNSIGNED_BYTE) {
+        checked_byteCount = count;
+        first = byteOffset;
+    }
+    else if (type == LOCAL_GL_UNSIGNED_INT && IsExtensionEnabled(WebGLExtensionID::OES_element_index_uint)) {
+        checked_byteCount = 4 * CheckedUint32(count);
+        if (byteOffset % 4 != 0) {
+            ErrorInvalidOperation("%s: invalid byteOffset for UNSIGNED_INT (must be a multiple of 4)", info);
+            return false;
+        }
+        first = byteOffset / 4;
+    }
+    else {
+        ErrorInvalidEnum("%s: type must be UNSIGNED_SHORT or UNSIGNED_BYTE", info);
         return false;
     }
 
-    const GLsizei first = byteOffset / bytesPerElem;
-    const CheckedUint32 checked_byteCount = bytesPerElem * CheckedUint32(count);
-
     if (!checked_byteCount.isValid()) {
         ErrorInvalidValue("%s: overflow in byteCount", info);
         return false;
     }
 
     // Any checks below this depend on a program being available.
     if (!mCurrentProgram) {
         ErrorInvalidOperation("%s: null CURRENT_PROGRAM", info);
@@ -418,94 +287,92 @@ WebGLContext::DrawElements_check(GLsizei
     } else {
         ClearBackbufferIfNeeded();
     }
 
     if (!DoFakeVertexAttrib0(mMaxFetchedVertices)) {
         return false;
     }
 
+    if (!DrawInstanced_check(info)) {
+        return false;
+    }
+
+    BindFakeBlackTextures();
+
     return true;
 }
 
 void
 WebGLContext::DrawElements(GLenum mode, GLsizei count, GLenum type,
                            WebGLintptr byteOffset)
 {
-    const char funcName[] = "drawElements";
     if (IsContextLost())
         return;
 
-    if (!ValidateDrawModeEnum(mode, funcName))
-        return;
-
-    bool error;
-    ScopedResolveTexturesForDraw scopedResolve(this, funcName, &error);
-    if (error)
+    if (!ValidateDrawModeEnum(mode, "drawElements: mode"))
         return;
 
     GLuint upperBound = 0;
-    if (!DrawElements_check(count, type, byteOffset, 1, funcName, &upperBound))
+    if (!DrawElements_check(count, type, byteOffset, 1, "drawElements",
+                            &upperBound))
+    {
         return;
+    }
 
     RunContextLossTimer();
 
     {
         ScopedMaskWorkaround autoMask(*this);
 
         if (gl->IsSupported(gl::GLFeature::draw_range_elements)) {
             gl->fDrawRangeElements(mode, 0, upperBound, count, type,
                                    reinterpret_cast<GLvoid*>(byteOffset));
         } else {
             gl->fDrawElements(mode, count, type,
                               reinterpret_cast<GLvoid*>(byteOffset));
         }
     }
 
-    Draw_cleanup(funcName);
+    Draw_cleanup();
 }
 
 void
 WebGLContext::DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type,
                                     WebGLintptr byteOffset, GLsizei primcount)
 {
-    const char funcName[] = "drawElementsInstanced";
     if (IsContextLost())
         return;
 
-    if (!ValidateDrawModeEnum(mode, funcName))
-        return;
-
-    bool error;
-    ScopedResolveTexturesForDraw scopedResolve(this, funcName, &error);
-    if (error)
+    if (!ValidateDrawModeEnum(mode, "drawElementsInstanced: mode"))
         return;
 
     GLuint upperBound = 0;
-    if (!DrawElements_check(count, type, byteOffset, primcount, funcName, &upperBound))
+    if (!DrawElements_check(count, type, byteOffset, primcount,
+                            "drawElementsInstanced", &upperBound))
+    {
         return;
-
-    if (!DrawInstanced_check(funcName))
-        return;
+    }
 
     RunContextLossTimer();
 
     {
         ScopedMaskWorkaround autoMask(*this);
         gl->fDrawElementsInstanced(mode, count, type,
                                    reinterpret_cast<GLvoid*>(byteOffset),
                                    primcount);
     }
 
-    Draw_cleanup(funcName);
+    Draw_cleanup();
 }
 
-void WebGLContext::Draw_cleanup(const char* funcName)
+void WebGLContext::Draw_cleanup()
 {
     UndoFakeVertexAttrib0();
+    UnbindFakeBlackTextures();
 
     if (!mBoundDrawFramebuffer) {
         Invalidate();
         mShouldPresent = true;
         MOZ_ASSERT(!mBackbufferNeedsClear);
     }
 
     if (gl->WorkAroundDriverBugs()) {
@@ -514,39 +381,27 @@ void WebGLContext::Draw_cleanup(const ch
 
             if (mDrawCallsSinceLastFlush >= MAX_DRAW_CALLS_SINCE_FLUSH) {
                 gl->fFlush();
                 mDrawCallsSinceLastFlush = 0;
             }
         }
     }
 
-    // Let's check for a really common error: Viewport is larger than the actual
-    // destination framebuffer.
-    uint32_t destWidth = mViewportWidth;
-    uint32_t destHeight = mViewportHeight;
-
-    if (mBoundDrawFramebuffer) {
-        const auto& fba = mBoundDrawFramebuffer->ColorAttachment(0);
-        if (fba.IsDefined()) {
-            fba.Size(&destWidth, &destHeight);
-        }
-    } else {
-        destWidth = mWidth;
-        destHeight = mHeight;
-    }
-
-    if (mViewportWidth > int32_t(destWidth) ||
-        mViewportHeight > int32_t(destHeight))
-    {
-        if (!mAlreadyWarnedAboutViewportLargerThanDest) {
-            GenerateWarning("%s: Drawing to a destination rect smaller than the viewport"
-                            " rect. (This warning will only be given once)",
-                            funcName);
-            mAlreadyWarnedAboutViewportLargerThanDest = true;
+    // Let's check the viewport
+    const WebGLRectangleObject* rect = CurValidDrawFBRectObject();
+    if (rect) {
+        if (mViewportWidth > rect->Width() ||
+            mViewportHeight > rect->Height())
+        {
+            if (!mAlreadyWarnedAboutViewportLargerThanDest) {
+                GenerateWarning("Drawing to a destination rect smaller than the viewport rect. "
+                                "(This warning will only be given once)");
+                mAlreadyWarnedAboutViewportLargerThanDest = true;
+            }
         }
     }
 }
 
 /*
  * Verify that state is consistent for drawing, and compute max number of elements (maxAllowedCount)
  * that will be legal to be read from bound VBOs.
  */
@@ -786,72 +641,150 @@ WebGLContext::UndoFakeVertexAttrib0()
         }
     } else {
         gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
     }
 
     gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mBoundArrayBuffer ? mBoundArrayBuffer->mGLName : 0);
 }
 
-static GLuint
-CreateGLTexture(gl::GLContext* gl)
+WebGLContextFakeBlackStatus
+WebGLContext::ResolvedFakeBlackStatus()
 {
-    MOZ_ASSERT(gl->IsCurrent());
-    GLuint ret = 0;
-    gl->fGenTextures(1, &ret);
-    return ret;
-}
+    // handle this case first, it's the generic case
+    if (MOZ_LIKELY(mFakeBlackStatus == WebGLContextFakeBlackStatus::NotNeeded))
+        return mFakeBlackStatus;
 
-WebGLContext::FakeBlackTexture::FakeBlackTexture(gl::GLContext* gl, TexTarget target,
-                                                 FakeBlackType type)
-    : mGL(gl)
-    , mGLName(CreateGLTexture(gl))
-{
-    GLenum texFormat;
-    switch (type) {
-    case FakeBlackType::RGBA0000:
-        texFormat = LOCAL_GL_RGBA;
-        break;
+    if (mFakeBlackStatus == WebGLContextFakeBlackStatus::Needed)
+        return mFakeBlackStatus;
 
-    case FakeBlackType::RGBA0001:
-        texFormat = LOCAL_GL_RGB;
-        break;
-
-    default:
-        MOZ_CRASH("bad type");
+    for (int32_t i = 0; i < mGLMaxTextureUnits; ++i) {
+        if ((mBound2DTextures[i] && mBound2DTextures[i]->ResolvedFakeBlackStatus() != WebGLTextureFakeBlackStatus::NotNeeded) ||
+            (mBoundCubeMapTextures[i] && mBoundCubeMapTextures[i]->ResolvedFakeBlackStatus() != WebGLTextureFakeBlackStatus::NotNeeded))
+        {
+            mFakeBlackStatus = WebGLContextFakeBlackStatus::Needed;
+            return mFakeBlackStatus;
+        }
     }
 
-    gl::ScopedBindTexture scopedBind(mGL, mGLName, target.get());
+    // we have exhausted all cases where we do need fakeblack, so if the status is still unknown,
+    // that means that we do NOT need it.
+    mFakeBlackStatus = WebGLContextFakeBlackStatus::NotNeeded;
+    return mFakeBlackStatus;
+}
 
-    mGL->fTexParameteri(target.get(), LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_NEAREST);
-    mGL->fTexParameteri(target.get(), LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_NEAREST);
+void
+WebGLContext::BindFakeBlackTexturesHelper(
+    GLenum target,
+    const nsTArray<WebGLRefPtr<WebGLTexture> > & boundTexturesArray,
+    UniquePtr<FakeBlackTexture> & opaqueTextureScopedPtr,
+    UniquePtr<FakeBlackTexture> & transparentTextureScopedPtr)
+{
+    for (int32_t i = 0; i < mGLMaxTextureUnits; ++i) {
+        if (!boundTexturesArray[i]) {
+            continue;
+        }
+
+        WebGLTextureFakeBlackStatus s = boundTexturesArray[i]->ResolvedFakeBlackStatus();
+        MOZ_ASSERT(s != WebGLTextureFakeBlackStatus::Unknown);
+
+        if (MOZ_LIKELY(s == WebGLTextureFakeBlackStatus::NotNeeded)) {
+            continue;
+        }
 
-    // We allocate our zeros on the heap, and we overallocate (16 bytes instead of 4) to
-    // minimize the risk of running into a driver bug in texImage2D, as it is a bit
-    // unusual maybe to create 1x1 textures, and the stack may not have the alignment that
-    // TexImage2D expects.
+        bool alpha = s == WebGLTextureFakeBlackStatus::UninitializedImageData &&
+                     FormatHasAlpha(boundTexturesArray[i]->ImageInfoBase().EffectiveInternalFormat());
+        UniquePtr<FakeBlackTexture>&
+            blackTexturePtr = alpha
+                              ? transparentTextureScopedPtr
+                              : opaqueTextureScopedPtr;
+
+        if (!blackTexturePtr) {
+            GLenum format = alpha ? LOCAL_GL_RGBA : LOCAL_GL_RGB;
+            blackTexturePtr = MakeUnique<FakeBlackTexture>(gl, target, format);
+        }
 
-    const webgl::DriverUnpackInfo dui = {texFormat, texFormat, LOCAL_GL_UNSIGNED_BYTE};
-    UniqueBuffer zeros = moz_xcalloc(1, 16); // Infallible allocation.
+        gl->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
+        gl->fBindTexture(target,
+                         blackTexturePtr->GLName());
+    }
+}
+
+void
+WebGLContext::BindFakeBlackTextures()
+{
+    // this is the generic case: try to return early
+    if (MOZ_LIKELY(ResolvedFakeBlackStatus() == WebGLContextFakeBlackStatus::NotNeeded))
+        return;
 
-    if (target == LOCAL_GL_TEXTURE_CUBE_MAP) {
-        for (int i = 0; i < 6; ++i) {
-            const TexImageTarget curTarget = LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X + i;
-            const GLenum error = DoTexImage(mGL, curTarget.get(), 0, &dui, 1, 1, 1,
-                                            zeros.get());
-            if (error)
-                MOZ_CRASH("Unexpected error during FakeBlack creation.");
+    BindFakeBlackTexturesHelper(LOCAL_GL_TEXTURE_2D,
+                                mBound2DTextures,
+                                mBlackOpaqueTexture2D,
+                                mBlackTransparentTexture2D);
+    BindFakeBlackTexturesHelper(LOCAL_GL_TEXTURE_CUBE_MAP,
+                                mBoundCubeMapTextures,
+                                mBlackOpaqueTextureCubeMap,
+                                mBlackTransparentTextureCubeMap);
+}
+
+void
+WebGLContext::UnbindFakeBlackTextures()
+{
+    // this is the generic case: try to return early
+    if (MOZ_LIKELY(ResolvedFakeBlackStatus() == WebGLContextFakeBlackStatus::NotNeeded))
+        return;
+
+    for (int32_t i = 0; i < mGLMaxTextureUnits; ++i) {
+        if (mBound2DTextures[i] && mBound2DTextures[i]->ResolvedFakeBlackStatus() != WebGLTextureFakeBlackStatus::NotNeeded) {
+            gl->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
+            gl->fBindTexture(LOCAL_GL_TEXTURE_2D, mBound2DTextures[i]->mGLName);
+        }
+        if (mBoundCubeMapTextures[i] && mBoundCubeMapTextures[i]->ResolvedFakeBlackStatus() != WebGLTextureFakeBlackStatus::NotNeeded) {
+            gl->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
+            gl->fBindTexture(LOCAL_GL_TEXTURE_CUBE_MAP, mBoundCubeMapTextures[i]->mGLName);
         }
-    } else {
-        const GLenum error = DoTexImage(mGL, target.get(), 0, &dui, 1, 1, 1,
-                                        zeros.get());
-        if (error)
-            MOZ_CRASH("Unexpected error during FakeBlack creation.");
     }
+
+    gl->fActiveTexture(LOCAL_GL_TEXTURE0 + mActiveTexture);
+}
+
+WebGLContext::FakeBlackTexture::FakeBlackTexture(gl::GLContext* gl, TexTarget target, GLenum format)
+    : mGL(gl)
+    , mGLName(0)
+{
+  MOZ_ASSERT(format == LOCAL_GL_RGB || format == LOCAL_GL_RGBA);
+
+  mGL->MakeCurrent();
+  GLuint formerBinding = 0;
+  gl->GetUIntegerv(target == LOCAL_GL_TEXTURE_2D
+                   ? LOCAL_GL_TEXTURE_BINDING_2D
+                   : LOCAL_GL_TEXTURE_BINDING_CUBE_MAP,
+                   &formerBinding);
+  gl->fGenTextures(1, &mGLName);
+  gl->fBindTexture(target.get(), mGLName);
+
+  // we allocate our zeros on the heap, and we overallocate (16 bytes instead of 4)
+  // to minimize the risk of running into a driver bug in texImage2D, as it is
+  // a bit unusual maybe to create 1x1 textures, and the stack may not have the alignment
+  // that texImage2D expects.
+  UniquePtr<uint8_t> zeros((uint8_t*)moz_xcalloc(1, 16));
+  if (target == LOCAL_GL_TEXTURE_2D) {
+      gl->fTexImage2D(target.get(), 0, format, 1, 1,
+                      0, format, LOCAL_GL_UNSIGNED_BYTE, zeros.get());
+  } else {
+      for (GLuint i = 0; i < 6; ++i) {
+          gl->fTexImage2D(LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, format, 1, 1,
+                          0, format, LOCAL_GL_UNSIGNED_BYTE, zeros.get());
+      }
+  }
+
+  gl->fBindTexture(target.get(), formerBinding);
 }
 
 WebGLContext::FakeBlackTexture::~FakeBlackTexture()
 {
-    mGL->MakeCurrent();
-    mGL->fDeleteTextures(1, &mGLName);
+  if (mGL) {
+      mGL->MakeCurrent();
+      mGL->fDeleteTextures(1, &mGLName);
+  }
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGLContextExtensions.cpp
+++ b/dom/canvas/WebGLContextExtensions.cpp
@@ -101,30 +101,56 @@ bool WebGLContext::IsExtensionSupported(
 }
 
 bool
 WebGLContext::IsExtensionSupported(WebGLExtensionID ext) const
 {
     if (mDisableExtensions)
         return false;
 
-    // Extensions for both WebGL 1 and 2.
+    // In alphabetical order
     switch (ext) {
-    // In alphabetical order
+    // ANGLE_
+    case WebGLExtensionID::ANGLE_instanced_arrays:
+        return WebGLExtensionInstancedArrays::IsSupported(this);
+
     // EXT_
+    case WebGLExtensionID::EXT_blend_minmax:
+        return WebGLExtensionBlendMinMax::IsSupported(this);
     case WebGLExtensionID::EXT_color_buffer_half_float:
         return WebGLExtensionColorBufferHalfFloat::IsSupported(this);
+    case WebGLExtensionID::EXT_frag_depth:
+        return WebGLExtensionFragDepth::IsSupported(this);
+    case WebGLExtensionID::EXT_shader_texture_lod:
+        return gl->IsExtensionSupported(gl::GLContext::EXT_shader_texture_lod);
+    case WebGLExtensionID::EXT_sRGB:
+        return WebGLExtensionSRGB::IsSupported(this);
     case WebGLExtensionID::EXT_texture_filter_anisotropic:
         return gl->IsExtensionSupported(gl::GLContext::EXT_texture_filter_anisotropic);
 
     // OES_
+    case WebGLExtensionID::OES_element_index_uint:
+        return gl->IsSupported(gl::GLFeature::element_index_uint);
+    case WebGLExtensionID::OES_standard_derivatives:
+        return gl->IsSupported(gl::GLFeature::standard_derivatives);
+    case WebGLExtensionID::OES_texture_float:
+        return gl->IsSupported(gl::GLFeature::texture_float);
     case WebGLExtensionID::OES_texture_float_linear:
         return gl->IsSupported(gl::GLFeature::texture_float_linear);
+    case WebGLExtensionID::OES_texture_half_float:
+        // If we have Feature::texture_half_float, we must not be on ES2
+        // and need to translate HALF_FLOAT_OES -> HALF_FLOAT.  We do that
+        // right before making the relevant calls.
+        return gl->IsExtensionSupported(gl::GLContext::OES_texture_half_float) ||
+               gl->IsSupported(gl::GLFeature::texture_half_float);
+
     case WebGLExtensionID::OES_texture_half_float_linear:
         return gl->IsSupported(gl::GLFeature::texture_half_float_linear);
+    case WebGLExtensionID::OES_vertex_array_object:
+        return true;
 
     // WEBGL_
     case WebGLExtensionID::WEBGL_color_buffer_float:
         return WebGLExtensionColorBufferFloat::IsSupported(this);
     case WebGLExtensionID::WEBGL_compressed_texture_atc:
         return gl->IsExtensionSupported(gl::GLContext::AMD_compressed_ATC_texture);
     case WebGLExtensionID::WEBGL_compressed_texture_etc1:
         return gl->IsExtensionSupported(gl::GLContext::OES_compressed_ETC1_RGB8_texture);
@@ -136,80 +162,42 @@ WebGLContext::IsExtensionSupported(WebGL
 
         return gl->IsExtensionSupported(gl::GLContext::EXT_texture_compression_dxt1) &&
                gl->IsExtensionSupported(gl::GLContext::ANGLE_texture_compression_dxt3) &&
                gl->IsExtensionSupported(gl::GLContext::ANGLE_texture_compression_dxt5);
 
     case WebGLExtensionID::WEBGL_debug_renderer_info:
         return Preferences::GetBool("webgl.enable-debug-renderer-info", false);
 
+    case WebGLExtensionID::WEBGL_depth_texture:
+        // WEBGL_depth_texture supports DEPTH_STENCIL textures
+        if (!gl->IsSupported(gl::GLFeature::packed_depth_stencil))
+            return false;
+
+        return gl->IsSupported(gl::GLFeature::depth_texture) ||
+               gl->IsExtensionSupported(gl::GLContext::ANGLE_depth_texture);
+    case WebGLExtensionID::WEBGL_draw_buffers:
+        return WebGLExtensionDrawBuffers::IsSupported(this);
     case WebGLExtensionID::WEBGL_lose_context:
         // We always support this extension.
         return true;
 
     default:
         // For warnings-as-errors.
         break;
     }
 
-    if (!IsWebGL2()) {
-        // WebGL1-only extensions
+    if (gfxPrefs::WebGLDraftExtensionsEnabled() || IsWebGL2()) {
         switch (ext) {
-        // ANGLE_
-        case WebGLExtensionID::ANGLE_instanced_arrays:
-            return WebGLExtensionInstancedArrays::IsSupported(this);
-
-        // EXT_
-        case WebGLExtensionID::EXT_blend_minmax:
-            return WebGLExtensionBlendMinMax::IsSupported(this);
-        case WebGLExtensionID::EXT_frag_depth:
-            return WebGLExtensionFragDepth::IsSupported(this);
-        case WebGLExtensionID::EXT_shader_texture_lod:
-            return gl->IsExtensionSupported(gl::GLContext::EXT_shader_texture_lod);
-        case WebGLExtensionID::EXT_sRGB:
-            return WebGLExtensionSRGB::IsSupported(this);
-
-        // OES_
-        case WebGLExtensionID::OES_element_index_uint:
-            return gl->IsSupported(gl::GLFeature::element_index_uint);
-        case WebGLExtensionID::OES_standard_derivatives:
-            return gl->IsSupported(gl::GLFeature::standard_derivatives);
-        case WebGLExtensionID::OES_texture_float:
-            return WebGLExtensionTextureFloat::IsSupported(this);
-        case WebGLExtensionID::OES_texture_half_float:
-            return WebGLExtensionTextureHalfFloat::IsSupported(this);
-
-        case WebGLExtensionID::OES_vertex_array_object:
-            return true;
-
-        // WEBGL_
-        case WebGLExtensionID::WEBGL_depth_texture:
-            // WEBGL_depth_texture supports DEPTH_STENCIL textures
-            if (!gl->IsSupported(gl::GLFeature::packed_depth_stencil))
-                return false;
-
-            return gl->IsSupported(gl::GLFeature::depth_texture) ||
-                   gl->IsExtensionSupported(gl::GLContext::ANGLE_depth_texture);
-        case WebGLExtensionID::WEBGL_draw_buffers:
-            return WebGLExtensionDrawBuffers::IsSupported(this);
+        case WebGLExtensionID::EXT_disjoint_timer_query:
+            return WebGLExtensionDisjointTimerQuery::IsSupported(this);
         default:
             // For warnings-as-errors.
             break;
         }
-
-        if (gfxPrefs::WebGLDraftExtensionsEnabled()) {
-            switch (ext) {
-            case WebGLExtensionID::EXT_disjoint_timer_query:
-                return WebGLExtensionDisjointTimerQuery::IsSupported(this);
-
-            default:
-                // For warnings-as-errors.
-                break;
-            }
-        }
     }
 
     return false;
 }
 
 static bool
 CompareWebGLExtensionName(const nsACString& name, const char* other)
 {
--- a/dom/canvas/WebGLContextFramebufferOperations.cpp
+++ b/dom/canvas/WebGLContextFramebufferOperations.cpp
@@ -132,90 +132,81 @@ WebGLContext::DepthMask(WebGLboolean b)
     MakeContextCurrent();
     mDepthWriteMask = b;
     gl->fDepthMask(b);
 }
 
 void
 WebGLContext::DrawBuffers(const dom::Sequence<GLenum>& buffers)
 {
-    const char funcName[] = "drawBuffers";
     if (IsContextLost())
         return;
 
+    const size_t buffersLength = buffers.Length();
+
+    if (!buffersLength) {
+        return ErrorInvalidValue("drawBuffers: invalid <buffers> (buffers must not be empty)");
+    }
+
     if (!mBoundDrawFramebuffer) {
-        // GLES 3.0.4 p186:
-        // "If the GL is bound to the default framebuffer, then `n` must be 1 and the
-        //  constant must be BACK or NONE. [...] If DrawBuffers is supplied with a
-        //  constant other than BACK and NONE, or with a value of `n` other than 1, the
-        //  error INVALID_OPERATION is generated."
-        if (buffers.Length() != 1) {
-            ErrorInvalidOperation("%s: For the default framebuffer, `buffers` must have a"
-                                  " length of 1.",
-                                  funcName);
-            return;
-        }
+        // OK: we are rendering in the default framebuffer
 
-        switch (buffers[0]) {
-        case LOCAL_GL_NONE:
-        case LOCAL_GL_BACK:
-            break;
-
-        default:
-            ErrorInvalidOperation("%s: For the default framebuffer, `buffers[0]` must be"
-                                  " BACK or NONE.",
-                                  funcName);
-            return;
+        /* EXT_draw_buffers :
+         If the GL is bound to the default framebuffer, then <buffersLength> must be 1
+         and the constant must be BACK or NONE. When draw buffer zero is
+         BACK, color values are written into the sole buffer for single-
+         buffered contexts, or into the back buffer for double-buffered
+         contexts. If DrawBuffersEXT is supplied with a constant other than
+         BACK and NONE, the error INVALID_OPERATION is generated.
+         */
+        if (buffersLength != 1) {
+            return ErrorInvalidValue("drawBuffers: invalid <buffers> (main framebuffer: buffers.length must be 1)");
         }
 
-        mDefaultFB_DrawBuffer0 = buffers[0];
-        gl->Screen()->SetDrawBuffer(buffers[0]);
-        return;
-    }
-
-    // Framebuffer object (not default framebuffer)
-
-    if (buffers.Length() > mImplMaxDrawBuffers) {
-        // "An INVALID_VALUE error is generated if `n` is greater than MAX_DRAW_BUFFERS."
-        ErrorInvalidValue("%s: `buffers` must have a length <= MAX_DRAW_BUFFERS.",
-                          funcName);
-        return;
+        if (buffers[0] == LOCAL_GL_NONE || buffers[0] == LOCAL_GL_BACK) {
+            gl->Screen()->SetDrawBuffer(buffers[0]);
+            return;
+        }
+        return ErrorInvalidOperation("drawBuffers: invalid operation (main framebuffer: buffers[0] must be GL_NONE or GL_BACK)");
     }
 
-    for (size_t i = 0; i < buffers.Length(); i++) {
-        // "If the GL is bound to a draw framebuffer object, the `i`th buffer listed in
-        //  bufs must be COLOR_ATTACHMENTi or NONE. Specifying a buffer out of order,
-        //  BACK, or COLOR_ATTACHMENTm where `m` is greater than or equal to the value of
-        // MAX_COLOR_ATTACHMENTS, will generate the error INVALID_OPERATION.
+    // OK: we are rendering in a framebuffer object
+
+    if (buffersLength > size_t(mGLMaxDrawBuffers)) {
+        /* EXT_draw_buffers :
+         The maximum number of draw buffers is implementation-dependent. The
+         number of draw buffers supported can be queried by calling
+         GetIntegerv with the symbolic constant MAX_DRAW_BUFFERS_EXT. An
+         INVALID_VALUE error is generated if <buffersLength> is greater than
+         MAX_DRAW_BUFFERS_EXT.
+         */
+        return ErrorInvalidValue("drawBuffers: invalid <buffers> (buffers.length > GL_MAX_DRAW_BUFFERS)");
+    }
 
-        // WEBGL_draw_buffers:
-        // "The value of the MAX_COLOR_ATTACHMENTS_WEBGL parameter must be greater than or
-        //  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.
+    for (uint32_t i = 0; i < buffersLength; i++)
+    {
+        /* EXT_draw_buffers :
+         If the GL is bound to a draw framebuffer object, the <i>th buffer listed
+         in <bufs> must be COLOR_ATTACHMENT<i>_EXT or NONE. Specifying a
+         buffer out of order, BACK, or COLOR_ATTACHMENT<m>_EXT where <m> is
+         greater than or equal to the value of MAX_COLOR_ATTACHMENTS_EXT,
+         will generate the error INVALID_OPERATION.
+         */
+        /* WEBGL_draw_buffers :
+         The value of the MAX_COLOR_ATTACHMENTS_WEBGL parameter must be greater than or equal to that of the MAX_DRAW_BUFFERS_WEBGL parameter.
+         */
         if (buffers[i] != LOCAL_GL_NONE &&
-            buffers[i] != LOCAL_GL_COLOR_ATTACHMENT0 + i)
-        {
-            ErrorInvalidOperation("%s: `buffers[i]` must be NONE or COLOR_ATTACHMENTi.",
-                                  funcName);
-            return;
+            buffers[i] != GLenum(LOCAL_GL_COLOR_ATTACHMENT0 + i)) {
+            return ErrorInvalidOperation("drawBuffers: invalid operation (buffers[i] must be GL_NONE or GL_COLOR_ATTACHMENTi)");
         }
     }
 
     MakeContextCurrent();
 
-    const GLenum* ptr = nullptr;
-    if (buffers.Length()) {
-        ptr = buffers.Elements();
-    }
-
-    gl->fDrawBuffers(buffers.Length(), ptr);
-
-    const auto end = ptr + buffers.Length();
-    mBoundDrawFramebuffer->mDrawBuffers.assign(ptr, end);
+    gl->fDrawBuffers(buffersLength, buffers.Elements());
 }
 
 void
 WebGLContext::StencilMask(GLuint mask)
 {
     if (IsContextLost())
         return;
 
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -55,16 +55,47 @@
 #include "mozilla/UniquePtrExtensions.h"
 
 namespace mozilla {
 
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 using namespace mozilla::gl;
 
+static const WebGLRectangleObject*
+CurValidFBRectObject(const WebGLContext* webgl,
+                     const WebGLFramebuffer* boundFB)
+{
+    const WebGLRectangleObject* rect = nullptr;
+
+    if (boundFB) {
+        // We don't really need to ask the driver.
+        // Use 'precheck' to just check that our internal state looks good.
+        FBStatus precheckStatus = boundFB->PrecheckFramebufferStatus();
+        if (precheckStatus == LOCAL_GL_FRAMEBUFFER_COMPLETE)
+            rect = &boundFB->RectangleObject();
+    } else {
+        rect = static_cast<const WebGLRectangleObject*>(webgl);
+    }
+
+    return rect;
+}
+
+const WebGLRectangleObject*
+WebGLContext::CurValidDrawFBRectObject() const
+{
+    return CurValidFBRectObject(this, mBoundDrawFramebuffer);
+}
+
+const WebGLRectangleObject*
+WebGLContext::CurValidReadFBRectObject() const
+{
+    return CurValidFBRectObject(this, mBoundReadFramebuffer);
+}
+
 //
 //  WebGL API
 //
 
 void
 WebGLContext::ActiveTexture(GLenum texture)
 {
     if (IsContextLost())
@@ -388,25 +419,26 @@ WebGLContext::DeleteTexture(WebGLTexture
         return;
 
     if (mBoundDrawFramebuffer)
         mBoundDrawFramebuffer->DetachTexture(tex);
 
     if (mBoundReadFramebuffer)
         mBoundReadFramebuffer->DetachTexture(tex);
 
+    tex->InvalidateStatusOfAttachedFBs();
+
     GLuint activeTexture = mActiveTexture;
     for (int32_t i = 0; i < mGLMaxTextureUnits; i++) {
-        if (mBound2DTextures[i] == tex ||
-            mBoundCubeMapTextures[i] == tex ||
-            mBound3DTextures[i] == tex ||
-            mBound2DArrayTextures[i] == tex)
+        if ((mBound2DTextures[i] == tex && tex->Target() == LOCAL_GL_TEXTURE_2D) ||
+            (mBoundCubeMapTextures[i] == tex && tex->Target() == LOCAL_GL_TEXTURE_CUBE_MAP) ||
+            (mBound3DTextures[i] == tex && tex->Target() == LOCAL_GL_TEXTURE_3D))
         {
             ActiveTexture(LOCAL_GL_TEXTURE0 + i);
-            BindTexture(tex->Target().get(), nullptr);
+            BindTexture(tex->Target(), nullptr);
         }
     }
     ActiveTexture(LOCAL_GL_TEXTURE0 + activeTexture);
 
     tex->RequestDelete();
 }
 
 void
@@ -689,22 +721,20 @@ WebGLContext::GetBufferParameter(GLenum 
 
 JS::Value
 WebGLContext::GetFramebufferAttachmentParameter(JSContext* cx,
                                                 GLenum target,
                                                 GLenum attachment,
                                                 GLenum pname,
                                                 ErrorResult& rv)
 {
-    const char funcName[] = "getFramebufferAttachmentParameter";
-
     if (IsContextLost())
         return JS::NullValue();
 
-    if (!ValidateFramebufferTarget(target, funcName))
+    if (!ValidateFramebufferTarget(target, "getFramebufferAttachmentParameter"))
         return JS::NullValue();
 
     WebGLFramebuffer* fb;
     switch (target) {
     case LOCAL_GL_FRAMEBUFFER:
     case LOCAL_GL_DRAW_FRAMEBUFFER:
         fb = mBoundDrawFramebuffer;
         break;
@@ -712,83 +742,199 @@ WebGLContext::GetFramebufferAttachmentPa
     case LOCAL_GL_READ_FRAMEBUFFER:
         fb = mBoundReadFramebuffer;
         break;
 
     default:
         MOZ_CRASH("Bad target.");
     }
 
-    if (fb)
-        return fb->GetAttachmentParameter(funcName, cx, target, attachment, pname, &rv);
-
-    ////////////////////////////////////
-
-    if (!IsWebGL2()) {
-        ErrorInvalidOperation("%s: Querying against the default framebuffer is not"
-                              " allowed in WebGL 1.",
-                              funcName);
+    if (!fb) {
+        ErrorInvalidOperation("getFramebufferAttachmentParameter: cannot query"
+                              " framebuffer 0.");
         return JS::NullValue();
     }
 
-    switch (attachment) {
-    case LOCAL_GL_COLOR:
-    case LOCAL_GL_DEPTH:
-    case LOCAL_GL_STENCIL:
-        break;
-
-    default:
-        ErrorInvalidEnum("%s: For the default framebuffer, can only query COLOR, DEPTH,"
-                         " or STENCIL.",
-                         funcName);
+    if (!ValidateFramebufferAttachment(fb, attachment,
+                                       "getFramebufferAttachmentParameter"))
+    {
         return JS::NullValue();
     }
 
-    switch (pname) {
-    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE:
-        return JS::Int32Value(LOCAL_GL_FRAMEBUFFER_DEFAULT);
-
-    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME:
+    if (IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers) &&
+        attachment >= LOCAL_GL_COLOR_ATTACHMENT0 &&
+        attachment <= LOCAL_GL_COLOR_ATTACHMENT15)
+    {
+        fb->EnsureColorAttachPoints(attachment - LOCAL_GL_COLOR_ATTACHMENT0);
+    }
+
+    MakeContextCurrent();
+
+    const WebGLFBAttachPoint& fba = fb->GetAttachPoint(attachment);
+
+    if (fba.Renderbuffer()) {
+        switch (pname) {
+            case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT:
+                if (IsExtensionEnabled(WebGLExtensionID::EXT_sRGB)) {
+                    const GLenum internalFormat = fba.Renderbuffer()->InternalFormat();
+                    return (internalFormat == LOCAL_GL_SRGB_EXT ||
+                            internalFormat == LOCAL_GL_SRGB_ALPHA_EXT ||
+                            internalFormat == LOCAL_GL_SRGB8_ALPHA8_EXT) ?
+                        JS::NumberValue(uint32_t(LOCAL_GL_SRGB_EXT)) :
+                        JS::NumberValue(uint32_t(LOCAL_GL_LINEAR));
+                }
+                break;
+
+            case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE:
+                return JS::NumberValue(uint32_t(LOCAL_GL_RENDERBUFFER));
+
+            case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME:
+                return WebGLObjectAsJSValue(cx, fba.Renderbuffer(), rv);
+
+            case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE: {
+                if (!IsExtensionEnabled(WebGLExtensionID::EXT_color_buffer_half_float) &&
+                    !IsExtensionEnabled(WebGLExtensionID::WEBGL_color_buffer_float))
+                {
+                    break;
+                }
+
+                if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
+                    ErrorInvalidOperation("getFramebufferAttachmentParameter: Cannot get component"
+                                          " type of a depth-stencil attachment.");
+                    return JS::NullValue();
+                }
+
+                if (!fba.IsComplete())
+                    return JS::NumberValue(uint32_t(LOCAL_GL_NONE));
+
+                uint32_t ret = LOCAL_GL_NONE;
+                switch (fba.Renderbuffer()->InternalFormat()) {
+                case LOCAL_GL_RGBA4:
+                case LOCAL_GL_RGB5_A1:
+                case LOCAL_GL_RGB565:
+                case LOCAL_GL_SRGB8_ALPHA8:
+                    ret = LOCAL_GL_UNSIGNED_NORMALIZED;
+                    break;
+                case LOCAL_GL_RGB16F:
+                case LOCAL_GL_RGBA16F:
+                case LOCAL_GL_RGB32F:
+                case LOCAL_GL_RGBA32F:
+                    ret = LOCAL_GL_FLOAT;
+                    break;
+                case LOCAL_GL_DEPTH_COMPONENT16:
+                case LOCAL_GL_STENCIL_INDEX8:
+                    ret = LOCAL_GL_UNSIGNED_INT;
+                    break;
+                default:
+                    MOZ_ASSERT(false, "Unhandled RB component type.");
+                    break;
+                }
+                return JS::NumberValue(uint32_t(ret));
+            }
+        }
+
+        ErrorInvalidEnumInfo("getFramebufferAttachmentParameter: pname", pname);
         return JS::NullValue();
-
-    ////////////////
-
-    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE:
-    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE:
-    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE:
-        if (attachment == LOCAL_GL_COLOR)
-            return JS::NumberValue(8);
-        return JS::NumberValue(0);
-
-    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE:
-        if (attachment == LOCAL_GL_COLOR)
-            return JS::NumberValue(mOptions.alpha ? 8 : 0);
-        return JS::NumberValue(0);
-
-    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE:
-        if (attachment == LOCAL_GL_DEPTH)
-            return JS::NumberValue(mOptions.depth ? 24 : 0);
-        return JS::NumberValue(0);
-
-    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE:
-        if (attachment == LOCAL_GL_STENCIL)
-            return JS::NumberValue(mOptions.stencil ? 8 : 0);
-        return JS::NumberValue(0);
-
-    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE:
-        if (attachment == LOCAL_GL_STENCIL)
-            return JS::NumberValue(LOCAL_GL_UNSIGNED_INT);
-        else
-            return JS::NumberValue(LOCAL_GL_UNSIGNED_NORMALIZED);
-
-    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING:
-        return JS::NumberValue(LOCAL_GL_LINEAR);
+    } else if (fba.Texture()) {
+        switch (pname) {
+             case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT:
+                if (IsExtensionEnabled(WebGLExtensionID::EXT_sRGB)) {
+                    const TexInternalFormat effectiveInternalFormat =
+                        fba.Texture()->ImageInfoBase().EffectiveInternalFormat();
+
+                    if (effectiveInternalFormat == LOCAL_GL_NONE) {
+                        ErrorInvalidOperation("getFramebufferAttachmentParameter: "
+                                              "texture contains no data");
+                        return JS::NullValue();
+                    }
+
+                    TexInternalFormat unsizedinternalformat = LOCAL_GL_NONE;
+                    TexType type = LOCAL_GL_NONE;
+                    UnsizedInternalFormatAndTypeFromEffectiveInternalFormat(
+                        effectiveInternalFormat, &unsizedinternalformat, &type);
+                    MOZ_ASSERT(unsizedinternalformat != LOCAL_GL_NONE);
+                    const bool srgb = unsizedinternalformat == LOCAL_GL_SRGB ||
+                                      unsizedinternalformat == LOCAL_GL_SRGB_ALPHA;
+                    return srgb ? JS::NumberValue(uint32_t(LOCAL_GL_SRGB))
+                                : JS::NumberValue(uint32_t(LOCAL_GL_LINEAR));
+                }
+                break;
+
+            case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE:
+                return JS::NumberValue(uint32_t(LOCAL_GL_TEXTURE));
+
+            case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME:
+                return WebGLObjectAsJSValue(cx, fba.Texture(), rv);
+
+            case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL:
+                return JS::Int32Value(fba.MipLevel());
+
+            case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE: {
+                GLenum face = fba.ImageTarget().get();
+                if (face == LOCAL_GL_TEXTURE_2D)
+                    face = 0;
+                return JS::Int32Value(face);
+            }
+
+            case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE: {
+                if (!IsExtensionEnabled(WebGLExtensionID::EXT_color_buffer_half_float) &&
+                    !IsExtensionEnabled(WebGLExtensionID::WEBGL_color_buffer_float))
+                {
+                    break;
+                }
+
+                if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
+                    ErrorInvalidOperation("getFramebufferAttachmentParameter: cannot component"
+                                          " type of depth-stencil attachments.");
+                    return JS::NullValue();
+                }
+
+                if (!fba.IsComplete())
+                    return JS::NumberValue(uint32_t(LOCAL_GL_NONE));
+
+                TexInternalFormat effectiveInternalFormat =
+                    fba.Texture()->ImageInfoAt(fba.ImageTarget(), fba.MipLevel()).EffectiveInternalFormat();
+                TexType type = TypeFromInternalFormat(effectiveInternalFormat);
+                GLenum ret = LOCAL_GL_NONE;
+                switch (type.get()) {
+                case LOCAL_GL_UNSIGNED_BYTE:
+                case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
+                case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
+                case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
+                    ret = LOCAL_GL_UNSIGNED_NORMALIZED;
+                    break;
+                case LOCAL_GL_FLOAT:
+                case LOCAL_GL_HALF_FLOAT:
+                    ret = LOCAL_GL_FLOAT;
+                    break;
+                case LOCAL_GL_UNSIGNED_SHORT:
+                case LOCAL_GL_UNSIGNED_INT:
+                    ret = LOCAL_GL_UNSIGNED_INT;
+                    break;
+                default:
+                    MOZ_ASSERT(false, "Unhandled RB component type.");
+                    break;
+                }
+                return JS::NumberValue(uint32_t(ret));
+            }
+        }
+
+        ErrorInvalidEnumInfo("getFramebufferAttachmentParameter: pname", pname);
+        return JS::NullValue();
+    } else {
+        switch (pname) {
+            case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE:
+                return JS::NumberValue(uint32_t(LOCAL_GL_NONE));
+
+            default:
+                ErrorInvalidEnumInfo("getFramebufferAttachmentParameter: pname", pname);
+                return JS::NullValue();
+        }
     }
 
-    ErrorInvalidEnum("%s: Invalid pname: 0x%04x", funcName, pname);
     return JS::NullValue();
 }
 
 JS::Value
 WebGLContext::GetRenderbufferParameter(GLenum target, GLenum pname)
 {
     if (IsContextLost())
         return JS::NullValue();
@@ -809,22 +955,25 @@ WebGLContext::GetRenderbufferParameter(G
         case LOCAL_GL_RENDERBUFFER_WIDTH:
         case LOCAL_GL_RENDERBUFFER_HEIGHT:
         case LOCAL_GL_RENDERBUFFER_RED_SIZE:
         case LOCAL_GL_RENDERBUFFER_GREEN_SIZE:
         case LOCAL_GL_RENDERBUFFER_BLUE_SIZE:
         case LOCAL_GL_RENDERBUFFER_ALPHA_SIZE:
         case LOCAL_GL_RENDERBUFFER_DEPTH_SIZE:
         case LOCAL_GL_RENDERBUFFER_STENCIL_SIZE:
-        case LOCAL_GL_RENDERBUFFER_INTERNAL_FORMAT:
         {
             // RB emulation means we have to ask the RB itself.
             GLint i = mBoundRenderbuffer->GetRenderbufferParameter(target, pname);
             return JS::Int32Value(i);
         }
+        case LOCAL_GL_RENDERBUFFER_INTERNAL_FORMAT:
+        {
+            return JS::NumberValue(mBoundRenderbuffer->InternalFormat());
+        }
         default:
             ErrorInvalidEnumInfo("getRenderbufferParameter: parameter", pname);
     }
 
     return JS::NullValue();
 }
 
 already_AddRefed<WebGLTexture>
@@ -954,27 +1103,23 @@ void
 WebGLContext::Hint(GLenum target, GLenum mode)
 {
     if (IsContextLost())
         return;
 
     bool isValid = false;
 
     switch (target) {
-    case LOCAL_GL_GENERATE_MIPMAP_HINT:
-        isValid = true;
-        break;
-
-    case LOCAL_GL_FRAGMENT_SHADER_DERIVATIVE_HINT:
-        if (IsWebGL2() ||
-            IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives))
-        {
+        case LOCAL_GL_GENERATE_MIPMAP_HINT:
             isValid = true;
-        }
-        break;
+            break;
+        case LOCAL_GL_FRAGMENT_SHADER_DERIVATIVE_HINT:
+            if (IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives))
+                isValid = true;
+            break;
     }
 
     if (!isValid)
         return ErrorInvalidEnum("hint: invalid hint");
 
     MakeContextCurrent();
     gl->fHint(target, mode);
 }
@@ -1070,248 +1215,147 @@ WebGLContext::LinkProgram(WebGLProgram* 
 }
 
 void
 WebGLContext::PixelStorei(GLenum pname, GLint param)
 {
     if (IsContextLost())
         return;
 
-    if (IsWebGL2()) {
-        uint32_t* pValueSlot = nullptr;
-        switch (pname) {
-        case LOCAL_GL_UNPACK_IMAGE_HEIGHT:
-            pValueSlot = &mPixelStore_UnpackImageHeight;
+    switch (pname) {
+        case UNPACK_FLIP_Y_WEBGL:
+            mPixelStoreFlipY = (param != 0);
             break;
-
-        case LOCAL_GL_UNPACK_SKIP_IMAGES:
-            pValueSlot = &mPixelStore_UnpackSkipImages;
-            break;
-
-        case LOCAL_GL_UNPACK_ROW_LENGTH:
-            pValueSlot = &mPixelStore_UnpackRowLength;
-            break;
-
-        case LOCAL_GL_UNPACK_SKIP_ROWS:
-            pValueSlot = &mPixelStore_UnpackSkipRows;
+        case UNPACK_PREMULTIPLY_ALPHA_WEBGL:
+            mPixelStorePremultiplyAlpha = (param != 0);
             break;
-
-        case LOCAL_GL_UNPACK_SKIP_PIXELS:
-            pValueSlot = &mPixelStore_UnpackSkipPixels;
-            break;
-
-        case LOCAL_GL_PACK_ROW_LENGTH:
-            pValueSlot = &mPixelStore_PackRowLength;
-            break;
-
-        case LOCAL_GL_PACK_SKIP_ROWS:
-            pValueSlot = &mPixelStore_PackSkipRows;
+        case UNPACK_COLORSPACE_CONVERSION_WEBGL:
+            if (param == LOCAL_GL_NONE || param == BROWSER_DEFAULT_WEBGL)
+                mPixelStoreColorspaceConversion = param;
+            else
+                return ErrorInvalidEnumInfo("pixelStorei: colorspace conversion parameter", param);
             break;
-
-        case LOCAL_GL_PACK_SKIP_PIXELS:
-            pValueSlot = &mPixelStore_PackSkipPixels;
-            break;
-        }
-
-        if (pValueSlot) {
-            if (param < 0) {
-                ErrorInvalidValue("pixelStorei: param must be >= 0.");
-                return;
-            }
-
+        case LOCAL_GL_PACK_ALIGNMENT:
+        case LOCAL_GL_UNPACK_ALIGNMENT:
+            if (param != 1 &&
+                param != 2 &&
+                param != 4 &&
+                param != 8)
+                return ErrorInvalidValue("pixelStorei: invalid pack/unpack alignment value");
+            if (pname == LOCAL_GL_PACK_ALIGNMENT)
+                mPixelStorePackAlignment = param;
+            else if (pname == LOCAL_GL_UNPACK_ALIGNMENT)
+                mPixelStoreUnpackAlignment = param;
             MakeContextCurrent();
             gl->fPixelStorei(pname, param);
-            *pValueSlot = param;
-            return;
-        }
-    }
-
-    switch (pname) {
-    case UNPACK_FLIP_Y_WEBGL:
-        mPixelStore_FlipY = bool(param);
-        return;
-
-    case UNPACK_PREMULTIPLY_ALPHA_WEBGL:
-        mPixelStore_PremultiplyAlpha = bool(param);
-        return;
-
-    case UNPACK_COLORSPACE_CONVERSION_WEBGL:
-        switch (param) {
-        case LOCAL_GL_NONE:
-        case BROWSER_DEFAULT_WEBGL:
-            mPixelStore_ColorspaceConversion = param;
-            return;
-
+            break;
         default:
-            ErrorInvalidEnumInfo("pixelStorei: colorspace conversion parameter",
-                                 param);
-            return;
-        }
-
-    case LOCAL_GL_PACK_ALIGNMENT:
-    case LOCAL_GL_UNPACK_ALIGNMENT:
-        switch (param) {
-        case 1:
-        case 2:
-        case 4:
-        case 8:
-            if (pname == LOCAL_GL_PACK_ALIGNMENT)
-                mPixelStore_PackAlignment = param;
-            else if (pname == LOCAL_GL_UNPACK_ALIGNMENT)
-                mPixelStore_UnpackAlignment = param;
-
-            MakeContextCurrent();
-            gl->fPixelStorei(pname, param);
-            return;
-
-        default:
-            ErrorInvalidValue("pixelStorei: invalid pack/unpack alignment value");
-            return;
-        }
-
-
-
-    default:
-        break;
+            return ErrorInvalidEnumInfo("pixelStorei: parameter", pname);
     }
-
-    ErrorInvalidEnumInfo("pixelStorei: parameter", pname);
 }
 
 // `width` in pixels.
 // `stride` in bytes.
-static void
-SetFullAlpha(void* data, GLenum format, GLenum type, size_t width, size_t height,
-             size_t stride)
+static bool
+SetFullAlpha(void* data, GLenum format, GLenum type, size_t width,
+             size_t height, size_t stride)
 {
     if (format == LOCAL_GL_ALPHA && type == LOCAL_GL_UNSIGNED_BYTE) {
         // Just memset the rows.
         uint8_t* row = static_cast<uint8_t*>(data);
         for (size_t j = 0; j < height; ++j) {
             memset(row, 0xff, width);
             row += stride;
         }
 
-        return;
+        return true;
     }
 
     if (format == LOCAL_GL_RGBA && type == LOCAL_GL_UNSIGNED_BYTE) {
         for (size_t j = 0; j < height; ++j) {
             uint8_t* row = static_cast<uint8_t*>(data) + j*stride;
 
             uint8_t* pAlpha = row + 3;
             uint8_t* pAlphaEnd = pAlpha + 4*width;
             while (pAlpha != pAlphaEnd) {
                 *pAlpha = 0xff;
                 pAlpha += 4;
             }
         }
 
-        return;
+        return true;
     }
 
     if (format == LOCAL_GL_RGBA && type == LOCAL_GL_FLOAT) {
         for (size_t j = 0; j < height; ++j) {
             uint8_t* rowBytes = static_cast<uint8_t*>(data) + j*stride;
             float* row = reinterpret_cast<float*>(rowBytes);
 
             float* pAlpha = row + 3;
             float* pAlphaEnd = pAlpha + 4*width;
             while (pAlpha != pAlphaEnd) {
                 *pAlpha = 1.0f;
                 pAlpha += 4;
             }
         }
 
+        return true;
+    }
+
+    MOZ_ASSERT(false, "Unhandled case, how'd we get here?");
+    return false;
+}
+
+static void
+ReadPixelsAndConvert(gl::GLContext* gl, GLint x, GLint y, GLsizei width, GLsizei height,
+                     GLenum readFormat, GLenum readType, size_t pixelStorePackAlignment,
+                     GLenum destFormat, GLenum destType, void* destBytes)
+{
+    if (readFormat == destFormat && readType == destType) {
+        gl->fReadPixels(x, y, width, height, destFormat, destType, destBytes);
         return;
     }
 
-    MOZ_CRASH("Unhandled case, how'd we get here?");
-}
-
-bool
-WebGLContext::DoReadPixelsAndConvert(GLint x, GLint y, GLsizei width, GLsizei height,
-                                     GLenum destFormat, GLenum destType, void* destBytes,
-                                     GLenum auxReadFormat, GLenum auxReadType)
-{
-    GLenum readFormat = destFormat;
-    GLenum readType = destType;
-
-    if (gl->WorkAroundDriverBugs() &&
-        gl->IsANGLE() &&
-        readType == LOCAL_GL_FLOAT &&
-        auxReadFormat == destFormat &&
-        auxReadType == LOCAL_GL_HALF_FLOAT)
+    if (readFormat == LOCAL_GL_RGBA &&
+        readType == LOCAL_GL_HALF_FLOAT &&
+        destFormat == LOCAL_GL_RGBA &&
+        destType == LOCAL_GL_FLOAT)
     {
-        readType = auxReadType;
-
-        const auto readBytesPerPixel = webgl::BytesPerPixel({readFormat, readType});
-        const auto destBytesPerPixel = webgl::BytesPerPixel({destFormat, destType});
-
-        CheckedUint32 readOffset;
-        CheckedUint32 readStride;
-        const CheckedUint32 readSize = GetPackSize(width, height, readBytesPerPixel,
-                                                   &readOffset, &readStride);
-
-        CheckedUint32 destOffset;
-        CheckedUint32 destStride;
-        const CheckedUint32 destSize = GetPackSize(width, height, destBytesPerPixel,
-                                                   &destOffset, &destStride);
-        if (!readSize.isValid() || !destSize.isValid()) {
-            ErrorOutOfMemory("readPixels: Overflow calculating sizes for conversion.");
-            return false;
-        }
-
-        UniqueBuffer readBuffer = malloc(readSize.value());
-        if (!readBuffer) {
-            ErrorOutOfMemory("readPixels: Failed to alloc temp buffer for conversion.");
-            return false;
-        }
-
-        gl::GLContext::LocalErrorScope errorScope(*gl);
+        size_t readBytesPerPixel = sizeof(uint16_t) * 4;
+        size_t destBytesPerPixel = sizeof(float) * 4;
+
+        size_t readBytesPerRow = readBytesPerPixel * width;
+
+        size_t readStride = RoundUpToMultipleOf(readBytesPerRow, pixelStorePackAlignment);
+        size_t destStride = RoundUpToMultipleOf(destBytesPerPixel * width,
+                                                pixelStorePackAlignment);
+
+        size_t bytesNeeded = ((height - 1) * readStride) + readBytesPerRow;
+        UniquePtr<uint8_t[]> readBuffer(new uint8_t[bytesNeeded]);
 
         gl->fReadPixels(x, y, width, height, readFormat, readType, readBuffer.get());
 
-        const GLenum error = errorScope.GetError();
-        if (error == LOCAL_GL_OUT_OF_MEMORY) {
-            ErrorOutOfMemory("readPixels: Driver ran out of memory.");
-            return false;
-        }
-
-        if (error) {
-            MOZ_RELEASE_ASSERT(false, "Unexpected driver error.");
-            return false;
-        }
-
-        size_t channelsPerRow = std::min(readStride.value() / sizeof(uint16_t),
-                                         destStride.value() / sizeof(float));
-
-        const uint8_t* srcRow = (uint8_t*)(readBuffer.get()) + readOffset.value();
-        uint8_t* dstRow = (uint8_t*)(destBytes) + destOffset.value();
-
+        size_t channelsPerRow = width * 4;
         for (size_t j = 0; j < (size_t)height; j++) {
-            auto src = (const uint16_t*)srcRow;
-            auto dst = (float*)dstRow;
-
-            const auto srcEnd = src + channelsPerRow;
+            uint16_t* src = (uint16_t*)(readBuffer.get()) + j*readStride;
+            float* dst = (float*)(destBytes) + j*destStride;
+
+            uint16_t* srcEnd = src + channelsPerRow;
             while (src != srcEnd) {
                 *dst = unpackFromFloat16(*src);
+
                 ++src;
                 ++dst;
             }
-
-            srcRow += readStride.value();
-            dstRow += destStride.value();
         }
 
-        return true;
+        return;
     }
 
-    gl->fReadPixels(x, y, width, height, destFormat, destType, destBytes);
-    return true;
+    MOZ_CRASH("bad format/type");
 }
 
 static bool
 IsFormatAndTypeUnpackable(GLenum format, GLenum type)
 {
     switch (type) {
     case LOCAL_GL_UNSIGNED_BYTE:
     case LOCAL_GL_FLOAT:
@@ -1333,97 +1377,68 @@ IsFormatAndTypeUnpackable(GLenum format,
     case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
         return format == LOCAL_GL_RGB;
 
     default:
         return false;
     }
 }
 
-CheckedUint32
-WebGLContext::GetPackSize(uint32_t width, uint32_t height, uint8_t bytesPerPixel,
-                          CheckedUint32* const out_startOffset,
-                          CheckedUint32* const out_rowStride)
-{
-    if (!width || !height) {
-        *out_startOffset = 0;
-        *out_rowStride = 0;
-        return 0;
-    }
-
-    const CheckedUint32 pixelsPerRow = (mPixelStore_PackRowLength ? mPixelStore_PackRowLength
-                                                                  : width);
-    const CheckedUint32 skipPixels = mPixelStore_PackSkipPixels;
-    const CheckedUint32 skipRows = mPixelStore_PackSkipRows;
-    const CheckedUint32 alignment = mPixelStore_PackAlignment;
-
-    // GLES 3.0.4, p116 (PACK_ functions like UNPACK_)
-    const auto totalBytesPerRow = bytesPerPixel * pixelsPerRow;
-    const auto rowStride = RoundUpToMultipleOf(totalBytesPerRow, alignment);
-
-    const auto startOffset = rowStride * skipRows + bytesPerPixel * skipPixels;
-    const auto usedBytesPerRow = bytesPerPixel * width;
-
-    const auto bytesNeeded = startOffset + rowStride * (height - 1) + usedBytesPerRow;
-
-    *out_startOffset = startOffset;
-    *out_rowStride = rowStride;
-    return bytesNeeded;
-}
-
 // This function is temporary, and will be removed once https://bugzilla.mozilla.org/show_bug.cgi?id=1176214 lands, which will
 // collapse the SharedArrayBufferView and ArrayBufferView into one.
 void
 ComputeLengthAndData(const dom::ArrayBufferViewOrSharedArrayBufferView& view,
                      void** const out_data, size_t* const out_length,
                      js::Scalar::Type* const out_type)
 {
     if (view.IsArrayBufferView()) {
-        const auto& view2 = view.GetAsArrayBufferView();
-        view2.ComputeLengthAndData();
-
-        *out_length = view2.Length();
-        *out_data = view2.Data();
-        *out_type = JS_GetArrayBufferViewType(view2.Obj());
+        const dom::ArrayBufferView& pixbuf = view.GetAsArrayBufferView();
+        pixbuf.ComputeLengthAndData();
+        *out_length = pixbuf.Length();
+        *out_data = pixbuf.Data();
+        *out_type = JS_GetArrayBufferViewType(pixbuf.Obj());
     } else {
-        const auto& view2 = view.GetAsSharedArrayBufferView();
-        view2.ComputeLengthAndData();
-        *out_length = view2.Length();
-        *out_data = view2.Data();
-        *out_type = JS_GetSharedArrayBufferViewType(view2.Obj());
+        const dom::SharedArrayBufferView& pixbuf = view.GetAsSharedArrayBufferView();
+        pixbuf.ComputeLengthAndData();
+        *out_length = pixbuf.Length();
+        *out_data = pixbuf.Data();
+        *out_type = JS_GetSharedArrayBufferViewType(pixbuf.Obj());
     }
 }
 
 void
-WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format,
-                         GLenum type,
-                         const dom::Nullable<dom::ArrayBufferViewOrSharedArrayBufferView>& pixels,
-                         ErrorResult& out_error)
+WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width,
+                         GLsizei height, GLenum format,
+                         GLenum type, const dom::Nullable<dom::ArrayBufferViewOrSharedArrayBufferView>& pixels,
+                         ErrorResult& rv)
 {
     if (IsContextLost())
         return;
 
     if (mCanvasElement &&
         mCanvasElement->IsWriteOnly() &&
         !nsContentUtils::IsCallerChrome())
     {
         GenerateWarning("readPixels: Not allowed");
-        out_error.Throw(NS_ERROR_DOM_SECURITY_ERR);
-        return;
+        return rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     }
 
     if (width < 0 || height < 0)
         return ErrorInvalidValue("readPixels: negative size passed");
 
     if (pixels.IsNull())
         return ErrorInvalidValue("readPixels: null destination buffer");
 
     if (!IsFormatAndTypeUnpackable(format, type))
         return ErrorInvalidEnum("readPixels: Bad format or type.");
 
+    const WebGLRectangleObject* framebufferRect = CurValidReadFBRectObject();
+    GLsizei framebufferWidth = framebufferRect ? framebufferRect->Width() : 0;
+    GLsizei framebufferHeight = framebufferRect ? framebufferRect->Height() : 0;
+
     int channels = 0;
 
     // Check the format param
     switch (format) {
     case LOCAL_GL_ALPHA:
         channels = 1;
         break;
     case LOCAL_GL_RGB:
@@ -1463,65 +1478,71 @@ WebGLContext::ReadPixels(GLint x, GLint 
         bytesPerPixel = 2*channels;
         requiredDataType = js::Scalar::Uint16;
         break;
 
     default:
         MOZ_CRASH("bad `type`");
     }
 
-    const auto& view = pixels.Value();
+    const dom::ArrayBufferViewOrSharedArrayBufferView &view = pixels.Value();
     // Compute length and data.  Don't reenter after this point, lest the
     // precomputed go out of sync with the instant length/data.
+    size_t dataByteLen;
     void* data;
-    size_t bytesAvailable;
     js::Scalar::Type dataType;
-    ComputeLengthAndData(view, &data, &bytesAvailable, &dataType);
+    ComputeLengthAndData(view, &data, &dataByteLen, &dataType);
 
     // Check the pixels param type
     if (dataType != requiredDataType)
         return ErrorInvalidOperation("readPixels: Mismatched type/pixels types");
 
-    CheckedUint32 startOffset;
-    CheckedUint32 rowStride;
-    const auto bytesNeeded = GetPackSize(width, height, bytesPerPixel, &startOffset,
-                                         &rowStride);
-    if (!bytesNeeded.isValid()) {
-        ErrorInvalidOperation("readPixels: Integer overflow computing the needed buffer"
-                              " size.");
-        return;
-    }
-
-    if (bytesNeeded.value() > bytesAvailable) {
-        ErrorInvalidOperation("readPixels: buffer too small");
-        return;
-    }
+    // Check the pixels param size
+    CheckedUint32 checked_neededByteLength =
+        GetImageSize(height, width, 1, bytesPerPixel, mPixelStorePackAlignment);
+
+    CheckedUint32 checked_plainRowSize = CheckedUint32(width) * bytesPerPixel;
+
+    CheckedUint32 checked_alignedRowSize =
+        RoundedToNextMultipleOf(checked_plainRowSize, mPixelStorePackAlignment);
+
+    if (!checked_neededByteLength.isValid())
+        return ErrorInvalidOperation("readPixels: integer overflow computing the needed buffer size");
+
+    if (checked_neededByteLength.value() > dataByteLen)
+        return ErrorInvalidOperation("readPixels: buffer too small");
 
     if (!data) {
         ErrorOutOfMemory("readPixels: buffer storage is null. Did we run out of memory?");
-        out_error.Throw(NS_ERROR_OUT_OF_MEMORY);
-        return;
+        return rv.Throw(NS_ERROR_OUT_OF_MEMORY);
     }
 
     MakeContextCurrent();
 
-    const webgl::FormatUsageInfo* srcFormat;
-    uint32_t srcWidth;
-    uint32_t srcHeight;
-    if (!ValidateCurFBForRead("readPixels", &srcFormat, &srcWidth, &srcHeight))
-        return;
-
-    auto srcType = srcFormat->format->componentType;
-    const bool isSrcTypeFloat = (srcType == webgl::ComponentType::Float);
+    bool isSourceTypeFloat;
+    if (mBoundReadFramebuffer) {
+        TexInternalFormat srcFormat;
+        if (!mBoundReadFramebuffer->ValidateForRead("readPixels", &srcFormat))
+            return;
+
+        MOZ_ASSERT(srcFormat != LOCAL_GL_NONE);
+        TexType texType = TypeFromInternalFormat(srcFormat);
+        isSourceTypeFloat = (texType == LOCAL_GL_FLOAT ||
+                             texType == LOCAL_GL_HALF_FLOAT);
+    } else {
+        ClearBackbufferIfNeeded();
+
+        isSourceTypeFloat = false;
+    }
 
     // Check the format and type params to assure they are an acceptable pair (as per spec)
 
     const GLenum mainReadFormat = LOCAL_GL_RGBA;
-    const GLenum mainReadType = isSrcTypeFloat ? LOCAL_GL_FLOAT
-                                               : LOCAL_GL_UNSIGNED_BYTE;
+    const GLenum mainReadType = isSourceTypeFloat ? LOCAL_GL_FLOAT
+                                                  : LOCAL_GL_UNSIGNED_BYTE;
 
     GLenum auxReadFormat = mainReadFormat;
     GLenum auxReadType = mainReadType;
 
     // OpenGL ES 2.0 $4.3.1 - IMPLEMENTATION_COLOR_READ_{TYPE/FORMAT} is a valid
     // combination for glReadPixels().
     if (gl->IsSupported(gl::GLFeature::ES2_compatibility)) {
         gl->fGetIntegerv(LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT,
@@ -1531,99 +1552,101 @@ WebGLContext::ReadPixels(GLint x, GLint 
     }
 
     const bool mainMatches = (format == mainReadFormat && type == mainReadType);
     const bool auxMatches = (format == auxReadFormat && type == auxReadType);
     const bool isValid = mainMatches || auxMatches;
     if (!isValid)
         return ErrorInvalidOperation("readPixels: Invalid format/type pair");
 
-    // Now that the errors are out of the way, on to actually reading!
-
-    uint32_t readX, readY;
-    uint32_t writeX, writeY;
-    uint32_t rwWidth, rwHeight;
-    Intersect(srcWidth, x, width, &readX, &writeX, &rwWidth);
-    Intersect(srcHeight, y, height, &readY, &writeY, &rwHeight);
-
-    if (rwWidth == uint32_t(width) && rwHeight == uint32_t(height)) {
-        DoReadPixelsAndConvert(x, y, width, height, format, type, data, auxReadFormat,
-                               auxReadType);
-        return;
+    GLenum readType = type;
+    if (gl->WorkAroundDriverBugs() && gl->IsANGLE()) {
+        if (type == LOCAL_GL_FLOAT &&
+            auxReadFormat == format &&
+            auxReadType == LOCAL_GL_HALF_FLOAT)
+        {
+            readType = auxReadType;
+        }
     }
 
-    // Read request contains out-of-bounds pixels. Unfortunately:
-    // GLES 3.0.4 p194 "Obtaining Pixels from the Framebuffer":
-    // "If any of these pixels lies outside of the window allocated to the current GL
-    //  context, or outside of the image attached to the currently bound framebuffer
-    //  object, then the values obtained for those pixels are undefined."
-
-    // This is a slow-path, so warn people away!
-    GenerateWarning("readPixels: Out-of-bounds reads with readPixels are deprecated, and"
-                    " may be slow.");
-
-    // Currently, the spec dictates that we need to zero the out-of-bounds pixels.
-
-    // Ideally we could just ReadPixels into the buffer, then zero the undefined parts.
-    // However, we can't do this for *shared* ArrayBuffers, as they can have racey
-    // accesses from Workers.
-
-    // We can use a couple tricks to do this faster, but we shouldn't encourage this
-    // anyway. Why not just do it the really safe, dead-simple way, even if it is
-    // hilariously slow?
-
-    ////////////////////////////////////
-    // Clear the targetted pixels to zero.
-
-    if (mPixelStore_PackRowLength ||
-        mPixelStore_PackSkipPixels ||
-        mPixelStore_PackSkipRows)
-    {
-        // Targetted bytes might not be contiguous, so do it row-by-row.
-        uint8_t* row = (uint8_t*)data + startOffset.value();
-        const auto bytesPerRow = bytesPerPixel * width;
-        for (uint32_t j = 0; j < uint32_t(height); j++) {
-            std::memset(row, 0, bytesPerRow);
-            row += rowStride.value();
-        }
+    // Now that the errors are out of the way, on to actually reading
+
+    // If we won't be reading any pixels anyways, just skip the actual reading
+    if (width == 0 || height == 0)
+        return DummyFramebufferOperation("readPixels");
+
+    if (CanvasUtils::CheckSaneSubrectSize(x, y, width, height, framebufferWidth, framebufferHeight)) {
+        // the easy case: we're not reading out-of-range pixels
+
+        // Effectively: gl->fReadPixels(x, y, width, height, format, type, dest);
+        ReadPixelsAndConvert(gl, x, y, width, height, format, readType,
+                             mPixelStorePackAlignment, format, type, data);
     } else {
-        std::memset(data, 0, bytesNeeded.value());
-    }
-
-    ////////////////////////////////////
-    // Read only the in-bounds pixels.
-
-    if (!rwWidth || !rwHeight) {
-        // There aren't any, so we're 'done'.
-        DummyFramebufferOperation("readPixels");
-        return;
-    }
-
-    if (IsWebGL2()) {
-        if (!mPixelStore_PackRowLength) {
-            gl->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH, width);
+        // the rectangle doesn't fit entirely in the bound buffer. We then have to set to zero the part
+        // of the buffer that correspond to out-of-range pixels. We don't want to rely on system OpenGL
+        // to do that for us, because passing out of range parameters to a buggy OpenGL implementation
+        // could conceivably allow to read memory we shouldn't be allowed to read. So we manually initialize
+        // the buffer to zero and compute the parameters to pass to OpenGL. We have to use an intermediate buffer
+        // to accomodate the potentially different strides (widths).
+
+        // Zero the whole pixel dest area in the destination buffer.
+        memset(data, 0, checked_neededByteLength.value());
+
+        if (   x >= framebufferWidth
+            || x+width <= 0
+            || y >= framebufferHeight
+            || y+height <= 0)
+        {
+            // we are completely outside of range, can exit now with buffer filled with zeros
+            return DummyFramebufferOperation("readPixels");
         }
-        gl->fPixelStorei(LOCAL_GL_PACK_SKIP_PIXELS, mPixelStore_PackSkipPixels + writeX);
-        gl->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, mPixelStore_PackSkipRows + writeY);
-
-        DoReadPixelsAndConvert(readX, readY, rwWidth, rwHeight, format, type, data,
-                               auxReadFormat, auxReadType);
-
-        gl->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH, mPixelStore_PackRowLength);
-        gl->fPixelStorei(LOCAL_GL_PACK_SKIP_PIXELS, mPixelStore_PackSkipPixels);
-        gl->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, mPixelStore_PackSkipRows);
-    } else {
-        // I *did* say "hilariously slow".
-
-        uint8_t* row = (uint8_t*)data + startOffset.value() + writeX * bytesPerPixel;
-        row += writeY * rowStride.value();
-        for (uint32_t j = 0; j < rwHeight; j++) {
-            DoReadPixelsAndConvert(readX, readY+j, rwWidth, 1, format, type, row,
-                                   auxReadFormat, auxReadType);
-            row += rowStride.value();
+
+        // compute the parameters of the subrect we're actually going to call glReadPixels on
+        GLint   subrect_x      = std::max(x, 0);
+        GLint   subrect_end_x  = std::min(x+width, framebufferWidth);
+        GLsizei subrect_width  = subrect_end_x - subrect_x;
+
+        GLint   subrect_y      = std::max(y, 0);
+        GLint   subrect_end_y  = std::min(y+height, framebufferHeight);
+        GLsizei subrect_height = subrect_end_y - subrect_y;
+
+        if (subrect_width < 0 || subrect_height < 0 ||
+            subrect_width > width || subrect_height > height)
+            return ErrorInvalidOperation("readPixels: integer overflow computing clipped rect size");
+
+        // now we know that subrect_width is in the [0..width] interval, and same for heights.
+
+        // now, same computation as above to find the size of the intermediate buffer to allocate for the subrect
+        // no need to check again for integer overflow here, since we already know the sizes aren't greater than before
+        uint32_t subrect_plainRowSize = subrect_width * bytesPerPixel;
+    // There are checks above to ensure that this doesn't overflow.
+        uint32_t subrect_alignedRowSize =
+            RoundedToNextMultipleOf(subrect_plainRowSize, mPixelStorePackAlignment).value();
+        uint32_t subrect_byteLength = (subrect_height-1)*subrect_alignedRowSize + subrect_plainRowSize;
+
+        // create subrect buffer, call glReadPixels, copy pixels into destination buffer, delete subrect buffer
+        auto subrect_data = MakeUniqueFallible<GLubyte[]>(subrect_byteLength);
+        if (!subrect_data)
+            return ErrorOutOfMemory("readPixels: subrect_data");
+
+        // Effectively: gl->fReadPixels(subrect_x, subrect_y, subrect_width,
+        //                              subrect_height, format, type, subrect_data.get());
+        ReadPixelsAndConvert(gl, subrect_x, subrect_y, subrect_width, subrect_height,
+                             format, readType, mPixelStorePackAlignment, format, type,
+                             subrect_data.get());
+
+        // notice that this for loop terminates because we already checked that subrect_height is at most height
+        for (GLint y_inside_subrect = 0; y_inside_subrect < subrect_height; ++y_inside_subrect) {
+            GLint subrect_x_in_dest_buffer = subrect_x - x;
+            GLint subrect_y_in_dest_buffer = subrect_y - y;
+            memcpy(static_cast<GLubyte*>(data)
+                     + checked_alignedRowSize.value() * (subrect_y_in_dest_buffer + y_inside_subrect)
+                     + bytesPerPixel * subrect_x_in_dest_buffer, // destination
+                   subrect_data.get() + subrect_alignedRowSize * y_inside_subrect, // source
+                   subrect_plainRowSize); // size
         }
     }
 
     // if we're reading alpha, we may need to do fixup.  Note that we don't allow
     // GL_ALPHA to readpixels currently, but we had the code written for it already.
     const bool formatHasAlpha = format == LOCAL_GL_ALPHA ||
                                 format == LOCAL_GL_RGBA;
     if (!formatHasAlpha)
@@ -1634,17 +1657,20 @@ WebGLContext::ReadPixels(GLint x, GLint 
         needAlphaFilled = !mBoundReadFramebuffer->ColorAttachment(0).HasAlpha();
     } else {
         needAlphaFilled = !mOptions.alpha;
     }
 
     if (!needAlphaFilled)
         return;
 
-    SetFullAlpha(data, format, type, width, height, rowStride.value());
+    size_t stride = checked_alignedRowSize.value(); // In bytes!
+    if (!SetFullAlpha(data, format, type, width, height, stride)) {
+        return rv.Throw(NS_ERROR_FAILURE);
+    }
 }
 
 void
 WebGLContext::RenderbufferStorage_base(const char* funcName, GLenum target,
                                        GLsizei samples,
                                        GLenum internalFormat, GLsizei width,
                                        GLsizei height)
 {
@@ -1666,42 +1692,101 @@ WebGLContext::RenderbufferStorage_base(c
         return;
     }
 
     if (width < 0 || height < 0) {
         ErrorInvalidValue("%s: Width and height must be >= 0.", funcName);
         return;
     }
 
-    if (uint32_t(width) > mImplMaxRenderbufferSize ||
-        uint32_t(height) > mImplMaxRenderbufferSize)
-    {
+    if (width > mGLMaxRenderbufferSize || height > mGLMaxRenderbufferSize) {
         ErrorInvalidValue("%s: Width or height exceeds maximum renderbuffer"
                           " size.", funcName);
         return;
     }
 
-    const auto usage = mFormatUsage->GetRBUsage(internalFormat);
-    if (!usage) {
+    // Convert DEPTH_STENCIL to sized type for testing
+    GLenum sizedInternalFormat = internalFormat;
+    if (sizedInternalFormat == LOCAL_GL_DEPTH_STENCIL)
+        sizedInternalFormat = LOCAL_GL_DEPTH24_STENCIL8;
+
+    bool isFormatValid = false;
+    const webgl::FormatInfo* info = webgl::GetInfoBySizedFormat(sizedInternalFormat);
+    if (info) {
+        const webgl::FormatUsageInfo* usage = mFormatUsage->GetUsage(info);
+        isFormatValid = usage && usage->asRenderbuffer;
+    }
+
+    if (!isFormatValid) {
         ErrorInvalidEnumInfo("`internalFormat`", funcName, internalFormat);
         return;
     }
 
+    // certain OpenGL ES renderbuffer formats may not exist on desktop OpenGL
+    GLenum internalFormatForGL = internalFormat;
+
+    switch (internalFormat) {
+    case LOCAL_GL_RGBA4:
+    case LOCAL_GL_RGB5_A1:
+        // 16-bit RGBA formats are not supported on desktop GL
+        if (!gl->IsGLES())
+            internalFormatForGL = LOCAL_GL_RGBA8;
+        break;
+
+    case LOCAL_GL_RGB565:
+        // the RGB565 format is not supported on desktop GL
+        if (!gl->IsGLES())
+            internalFormatForGL = LOCAL_GL_RGB8;
+        break;
+
+    case LOCAL_GL_DEPTH_COMPONENT16:
+        if (!gl->IsGLES() || gl->IsExtensionSupported(gl::GLContext::OES_depth24))
+            internalFormatForGL = LOCAL_GL_DEPTH_COMPONENT24;
+        else if (gl->IsExtensionSupported(gl::GLContext::OES_packed_depth_stencil))
+            internalFormatForGL = LOCAL_GL_DEPTH24_STENCIL8;
+        break;
+
+    case LOCAL_GL_DEPTH_STENCIL:
+        // We emulate this in WebGLRenderbuffer if we don't have the requisite extension.
+        internalFormatForGL = LOCAL_GL_DEPTH24_STENCIL8;
+        break;
+
+    default:
+        break;
+    }
+
     // Validation complete.
 
     MakeContextCurrent();
 
-    GetAndFlushUnderlyingGLErrors();
-    mBoundRenderbuffer->RenderbufferStorage(samples, usage, width, height);
-    GLenum error = GetAndFlushUnderlyingGLErrors();
-    if (error) {
-        GenerateWarning("%s generated error %s", funcName,
-                        ErrorName(error));
-        return;
+    bool willRealloc = samples != mBoundRenderbuffer->Samples() ||
+                       internalFormat != mBoundRenderbuffer->InternalFormat() ||
+                       width != mBoundRenderbuffer->Width() ||
+                       height != mBoundRenderbuffer->Height();
+
+    if (willRealloc) {
+        GetAndFlushUnderlyingGLErrors();
+        mBoundRenderbuffer->RenderbufferStorage(samples, internalFormatForGL,
+                                                width, height);
+        GLenum error = GetAndFlushUnderlyingGLErrors();
+        if (error) {
+            GenerateWarning("%s generated error %s", funcName,
+                            ErrorName(error));
+            return;
+        }
+    } else {
+        mBoundRenderbuffer->RenderbufferStorage(samples, internalFormatForGL,
+                                                width, height);
     }
+
+    mBoundRenderbuffer->SetSamples(samples);
+    mBoundRenderbuffer->SetInternalFormat(internalFormat);
+    mBoundRenderbuffer->SetInternalFormatForGL(internalFormatForGL);
+    mBoundRenderbuffer->setDimensions(width, height);
+    mBoundRenderbuffer->SetImageDataStatus(WebGLImageDataStatus::UninitializedImageData);
 }
 
 void
 WebGLContext::RenderbufferStorage(GLenum target, GLenum internalFormat, GLsizei width, GLsizei height)
 {
     RenderbufferStorage_base("renderbufferStorage", target, 0,
                              internalFormat, width, height);
 }
@@ -1794,16 +1879,89 @@ WebGLContext::StencilOpSeparate(GLenum f
         !ValidateStencilOpEnum(dpfail, "stencilOpSeparate: dpfail") ||
         !ValidateStencilOpEnum(dppass, "stencilOpSeparate: dppass"))
         return;
 
     MakeContextCurrent();
     gl->fStencilOpSeparate(face, sfail, dpfail, dppass);
 }
 
+nsresult
+WebGLContext::SurfaceFromElementResultToImageSurface(nsLayoutUtils::SurfaceFromElementResult& res,
+                                                     RefPtr<DataSourceSurface>& imageOut,
+                                                     WebGLTexelFormat* format)
+{
+   *format = WebGLTexelFormat::None;
+
+    if (!res.mSourceSurface)
+        return NS_OK;
+    RefPtr<DataSourceSurface> data = res.mSourceSurface->GetDataSurface();
+    if (!data) {
+        // SurfaceFromElement lied!
+        return NS_OK;
+    }
+
+    // We disallow loading cross-domain images and videos that have not been validated
+    // with CORS as WebGL textures. The reason for doing that is that timing
+    // attacks on WebGL shaders are able to retrieve approximations of the
+    // pixel values in WebGL textures; see bug 655987.
+    //
+    // To prevent a loophole where a Canvas2D would be used as a proxy to load
+    // cross-domain textures, we also disallow loading textures from write-only
+    // Canvas2D's.
+
+    // part 1: check that the DOM element is same-origin, or has otherwise been
+    // validated for cross-domain use.
+    if (!res.mCORSUsed) {
+        bool subsumes;
+        nsresult rv = mCanvasElement->NodePrincipal()->Subsumes(res.mPrincipal, &subsumes);
+        if (NS_FAILED(rv) || !subsumes) {
+            GenerateWarning("It is forbidden to load a WebGL texture from a cross-domain element that has not been validated with CORS. "
+                                "See https://developer.mozilla.org/en/WebGL/Cross-Domain_Textures");
+            return NS_ERROR_DOM_SECURITY_ERR;
+        }
+    }
+
+    // part 2: if the DOM element is write-only, it might contain
+    // cross-domain image data.
+    if (res.mIsWriteOnly) {
+        GenerateWarning("The canvas used as source for texImage2D here is tainted (write-only). It is forbidden "
+                        "to load a WebGL texture from a tainted canvas. A Canvas becomes tainted for example "
+                        "when a cross-domain image is drawn on it. "
+                        "See https://developer.mozilla.org/en/WebGL/Cross-Domain_Textures");
+        return NS_ERROR_DOM_SECURITY_ERR;
+    }
+
+    // End of security checks, now we should be safe regarding cross-domain images
+    // Notice that there is never a need to mark the WebGL canvas as write-only, since we reject write-only/cross-domain
+    // texture sources in the first place.
+
+    switch (data->GetFormat()) {
+        case SurfaceFormat::B8G8R8A8:
+            *format = WebGLTexelFormat::BGRA8; // careful, our ARGB means BGRA
+            break;
+        case SurfaceFormat::B8G8R8X8:
+            *format = WebGLTexelFormat::BGRX8; // careful, our RGB24 is not tightly packed. Whence BGRX8.
+            break;
+        case SurfaceFormat::A8:
+            *format = WebGLTexelFormat::A8;
+            break;
+        case SurfaceFormat::R5G6B5_UINT16:
+            *format = WebGLTexelFormat::RGB565;
+            break;
+        default:
+            NS_ASSERTION(false, "Unsupported image format. Unimplemented.");
+            return NS_ERROR_NOT_IMPLEMENTED;
+    }
+
+    imageOut = data;
+
+    return NS_OK;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // Uniform setters.
 
 void
 WebGLContext::Uniform1i(WebGLUniformLocation* loc, GLint a1)
 {
     GLuint rawLoc;
     if (!ValidateUniformSetter(loc, 1, LOCAL_GL_INT, "uniform1i", &rawLoc))
--- a/dom/canvas/WebGLContextState.cpp
+++ b/dom/canvas/WebGLContextState.cpp
@@ -72,19 +72,17 @@ StringValue(JSContext* cx, const nsAStri
     return JS::StringValue(jsStr);
 }
 
 bool
 WebGLContext::GetStencilBits(GLint* out_stencilBits)
 {
     *out_stencilBits = 0;
     if (mBoundDrawFramebuffer) {
-        if (mBoundDrawFramebuffer->StencilAttachment().IsDefined() &&
-            mBoundDrawFramebuffer->DepthStencilAttachment().IsDefined())
-        {
+        if (mBoundDrawFramebuffer->HasDepthStencilConflict()) {
             // Error, we don't know which stencil buffer's bits to use
             ErrorInvalidFramebufferOperation("getParameter: framebuffer has two stencil buffers bound");
             return false;
         }
 
         if (mBoundDrawFramebuffer->StencilAttachment().IsDefined() ||
             mBoundDrawFramebuffer->DepthStencilAttachment().IsDefined())
         {
@@ -139,49 +137,49 @@ WebGLContext::GetParameter(JSContext* cx
                 return JS::Int32Value(MINVALUE_GL_MAX_RENDERBUFFER_SIZE);
 
             default:
                 // Return the real value; we're not overriding this one
                 break;
         }
     }
 
-    if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers)) {
+    if (IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers)) {
         if (pname == LOCAL_GL_MAX_COLOR_ATTACHMENTS) {
-            return JS::Int32Value(mImplMaxColorAttachments);
+            return JS::Int32Value(mGLMaxColorAttachments);
 
         } else if (pname == LOCAL_GL_MAX_DRAW_BUFFERS) {
-            return JS::Int32Value(mImplMaxDrawBuffers);
+            return JS::Int32Value(mGLMaxDrawBuffers);
 
         } else if (pname >= LOCAL_GL_DRAW_BUFFER0 &&
-                   pname < GLenum(LOCAL_GL_DRAW_BUFFER0 + mImplMaxDrawBuffers))
+                   pname < GLenum(LOCAL_GL_DRAW_BUFFER0 + mGLMaxDrawBuffers))
         {
             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);
         }
     }
 
-    if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::OES_vertex_array_object)) {
+    if (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);
         }
     }
 
-    if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::EXT_disjoint_timer_query)) {
+    if (IsExtensionEnabled(WebGLExtensionID::EXT_disjoint_timer_query)) {
         if (pname == LOCAL_GL_TIMESTAMP_EXT) {
             GLuint64 iv = 0;
             gl->fGetInteger64v(pname, (GLint64*) &iv);
             // TODO: JS doesn't support 64-bit integers. Be lossy and
             // cast to double (53 bits)
             return JS::NumberValue(static_cast<double>(iv));
         } else if (pname == LOCAL_GL_GPU_DISJOINT_EXT) {
             // When disjoint isn't supported, leave as false.
@@ -232,17 +230,17 @@ WebGLContext::GetParameter(JSContext* cx
                     hasRetVal = true;
                 }
 
                 return StringValue(cx, ret, rv);
             }
         }
     }
 
-    if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives)) {
+    if (IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives)) {
         if (pname == LOCAL_GL_FRAGMENT_SHADER_DERIVATIVE_HINT) {
             GLint i = 0;
             gl->fGetIntegerv(pname, &i);
             return JS::Int32Value(i);
         }
     }
 
     if (IsExtensionEnabled(WebGLExtensionID::EXT_texture_filter_anisotropic)) {
@@ -377,23 +375,23 @@ WebGLContext::GetParameter(JSContext* cx
         case LOCAL_GL_ALPHA_BITS: {
             GLint i = 0;
             if (!mNeedsFakeNoAlpha) {
                 gl->fGetIntegerv(pname, &i);
             }
             return JS::Int32Value(i);
         }
         case LOCAL_GL_MAX_TEXTURE_SIZE:
-            return JS::Int32Value(mImplMaxTextureSize);
+            return JS::Int32Value(mGLMaxTextureSize);
 
         case LOCAL_GL_MAX_CUBE_MAP_TEXTURE_SIZE:
-            return JS::Int32Value(mImplMaxCubeMapTextureSize);
+            return JS::Int32Value(mGLMaxCubeMapTextureSize);
 
         case LOCAL_GL_MAX_RENDERBUFFER_SIZE:
-            return JS::Int32Value(mImplMaxRenderbufferSize);
+            return JS::Int32Value(mGLMaxRenderbufferSize);
 
         case LOCAL_GL_MAX_VERTEX_UNIFORM_VECTORS:
             return JS::Int32Value(mGLMaxVertexUniformVectors);
 
         case LOCAL_GL_MAX_FRAGMENT_UNIFORM_VECTORS:
             return JS::Int32Value(mGLMaxFragmentUniformVectors);
 
         case LOCAL_GL_MAX_VARYING_VECTORS:
@@ -446,23 +444,23 @@ WebGLContext::GetParameter(JSContext* cx
         case LOCAL_GL_DEPTH_WRITEMASK: {
             realGLboolean b = 0;
             gl->fGetBooleanv(pname, &b);
             return JS::BooleanValue(bool(b));
         }
 
         // bool, WebGL-specific
         case UNPACK_FLIP_Y_WEBGL:
-            return JS::BooleanValue(mPixelStore_FlipY);
+            return JS::BooleanValue(mPixelStoreFlipY);
         case UNPACK_PREMULTIPLY_ALPHA_WEBGL:
-            return JS::BooleanValue(mPixelStore_PremultiplyAlpha);
+            return JS::BooleanValue(mPixelStorePremultiplyAlpha);
 
         // uint, WebGL-specific
         case UNPACK_COLORSPACE_CONVERSION_WEBGL:
-            return JS::NumberValue(uint32_t(mPixelStore_ColorspaceConversion));
+            return JS::NumberValue(uint32_t(mPixelStoreColorspaceConversion));
 
         ////////////////////////////////
         // Complex values
 
         // 2 floats
         case LOCAL_GL_DEPTH_RANGE:
         case LOCAL_GL_ALIASED_POINT_SIZE_RANGE:
         case LOCAL_GL_ALIASED_LINE_WIDTH_RANGE: {
--- a/dom/canvas/WebGLContextTextures.cpp
+++ b/dom/canvas/WebGLContextTextures.cpp
@@ -47,134 +47,115 @@
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/ImageData.h"
 #include "mozilla/dom/ToJSValue.h"
 #include "mozilla/Endian.h"
 
 namespace mozilla {
 
 static bool
-IsValidTexTarget(WebGLContext* webgl, uint8_t funcDims, GLenum rawTexTarget,
-                 TexTarget* const out)
+IsValidTexTarget(WebGLContext* webgl, GLenum rawTexTarget, TexTarget* const out)
 {
-    uint8_t targetDims;
-
     switch (rawTexTarget) {
     case LOCAL_GL_TEXTURE_2D:
     case LOCAL_GL_TEXTURE_CUBE_MAP:
-        targetDims = 2;
         break;
 
     case LOCAL_GL_TEXTURE_3D:
-    case LOCAL_GL_TEXTURE_2D_ARRAY:
         if (!webgl->IsWebGL2())
             return false;
 
-        targetDims = 3;
         break;
 
     default:
         return false;
     }
 
-    // Some funcs (like GenerateMipmap) doesn't know the dimension, so don't check it.
-    if (funcDims && targetDims != funcDims)
-        return false;
-
     *out = rawTexTarget;
     return true;
 }
 
 static bool
-IsValidTexImageTarget(WebGLContext* webgl, uint8_t funcDims, GLenum rawTexImageTarget,
+IsValidTexImageTarget(WebGLContext* webgl, GLenum rawTexImageTarget,
                       TexImageTarget* const out)
 {
-    uint8_t targetDims;
-
     switch (rawTexImageTarget) {
     case LOCAL_GL_TEXTURE_2D:
     case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X:
     case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
     case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
     case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
     case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
     case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
-        targetDims = 2;
         break;
 
     case LOCAL_GL_TEXTURE_3D:
-    case LOCAL_GL_TEXTURE_2D_ARRAY:
         if (!webgl->IsWebGL2())
             return false;
 
-        targetDims = 3;
         break;
 
     default:
         return false;
     }
 
-    if (targetDims != funcDims)
-        return false;
-
     *out = rawTexImageTarget;
     return true;
 }
 
 bool
-ValidateTexTarget(WebGLContext* webgl, const char* funcName, uint8_t funcDims,
-                  GLenum rawTexTarget, TexTarget* const out_texTarget,
-                  WebGLTexture** const out_tex)
+ValidateTexTarget(WebGLContext* webgl, GLenum rawTexTarget, const char* funcName,
+                  TexTarget* const out_texTarget, WebGLTexture** const out_tex)
 {
     if (webgl->IsContextLost())
         return false;
 
     TexTarget texTarget;
-    if (!IsValidTexTarget(webgl, funcDims, rawTexTarget, &texTarget)) {
+    if (!IsValidTexTarget(webgl, rawTexTarget, &texTarget)) {
         webgl->ErrorInvalidEnum("%s: Invalid texTarget.", funcName);
         return false;
     }
 
     WebGLTexture* tex = webgl->ActiveBoundTextureForTarget(texTarget);
     if (!tex) {
         webgl->ErrorInvalidOperation("%s: No texture is bound to this target.", funcName);
         return false;
     }
 
     *out_texTarget = texTarget;
     *out_tex = tex;
     return true;
 }
 
 bool
-ValidateTexImageTarget(WebGLContext* webgl, const char* funcName, uint8_t funcDims,
-                       GLenum rawTexImageTarget, TexImageTarget* const out_texImageTarget,
+ValidateTexImageTarget(WebGLContext* webgl, GLenum rawTexImageTarget,
+                       const char* funcName, TexImageTarget* const out_texImageTarget,
                        WebGLTexture** const out_tex)
 {
     if (webgl->IsContextLost())
         return false;
 
     TexImageTarget texImageTarget;
-    if (!IsValidTexImageTarget(webgl, funcDims, rawTexImageTarget, &texImageTarget)) {
+    if (!IsValidTexImageTarget(webgl, rawTexImageTarget, &texImageTarget)) {
         webgl->ErrorInvalidEnum("%s: Invalid texImageTarget.", funcName);
         return false;
     }
 
     WebGLTexture* tex = webgl->ActiveBoundTextureForTexImageTarget(texImageTarget);
     if (!tex) {
         webgl->ErrorInvalidOperation("%s: No texture is bound to this target.", funcName);
         return false;
     }
 
     *out_texImageTarget = texImageTarget;
     *out_tex = tex;
     return true;
 }
 
-/*virtual*/ bool
+bool
 WebGLContext::IsTexParamValid(GLenum pname) const
 {
     switch (pname) {
     case LOCAL_GL_TEXTURE_MIN_FILTER:
     case LOCAL_GL_TEXTURE_MAG_FILTER:
     case LOCAL_GL_TEXTURE_WRAP_S:
     case LOCAL_GL_TEXTURE_WRAP_T:
         return true;
@@ -198,77 +179,65 @@ WebGLContext::BindTexture(GLenum rawTarg
 
      if (!ValidateObjectAllowDeletedOrNull("bindTexture", newTex))
         return;
 
     // Need to check rawTarget first before comparing against newTex->Target() as
     // newTex->Target() returns a TexTarget, which will assert on invalid value.
     WebGLRefPtr<WebGLTexture>* currentTexPtr = nullptr;
     switch (rawTarget) {
-    case LOCAL_GL_TEXTURE_2D:
-        currentTexPtr = &mBound2DTextures[mActiveTexture];
-        break;
+        case LOCAL_GL_TEXTURE_2D:
+            currentTexPtr = &mBound2DTextures[mActiveTexture];
+            break;
 
-   case LOCAL_GL_TEXTURE_CUBE_MAP:
-        currentTexPtr = &mBoundCubeMapTextures[mActiveTexture];
-        break;
+       case LOCAL_GL_TEXTURE_CUBE_MAP:
+            currentTexPtr = &mBoundCubeMapTextures[mActiveTexture];
+            break;
 
-   case LOCAL_GL_TEXTURE_3D:
-        if (IsWebGL2())
+       case LOCAL_GL_TEXTURE_3D:
+            if (!IsWebGL2())
+                return ErrorInvalidEnum("bindTexture: target TEXTURE_3D is only available in WebGL version 2.0 or newer");
+
             currentTexPtr = &mBound3DTextures[mActiveTexture];
-        break;
+            break;
 
-   case LOCAL_GL_TEXTURE_2D_ARRAY:
-        if (IsWebGL2())
-            currentTexPtr = &mBound2DArrayTextures[mActiveTexture];
-        break;
+       default:
+            return ErrorInvalidEnumInfo("bindTexture: target", rawTarget);
     }
-
-    if (!currentTexPtr) {
-        ErrorInvalidEnumInfo("bindTexture: target", rawTarget);
-        return;
-    }
-
     const TexTarget texTarget(rawTarget);
 
     MakeContextCurrent();
 
-    if (newTex) {
-        if (!newTex->BindTexture(texTarget))
-            return;
-    } else {
+    if (newTex && !newTex->BindTexture(texTarget))
+        return;
+
+    if (!newTex) {
         gl->fBindTexture(texTarget.get(), 0);
     }
 
     *currentTexPtr = newTex;
 }
 
 void
 WebGLContext::GenerateMipmap(GLenum rawTexTarget)
 {
-    const char funcName[] = "generateMipmap";
-    const uint8_t funcDims = 0;
-
     TexTarget texTarget;
     WebGLTexture* tex;
-    if (!ValidateTexTarget(this, funcName, funcDims, rawTexTarget, &texTarget, &tex))
+    if (!ValidateTexTarget(this, rawTexTarget, "texParameter", &texTarget, &tex))
         return;
 
     tex->GenerateMipmap(texTarget);
 }
 
 JS::Value
 WebGLContext::GetTexParameter(GLenum rawTexTarget, GLenum pname)
 {
-    const char funcName[] = "getTexParameter";
-    const uint8_t funcDims = 0;
-
     TexTarget texTarget;
     WebGLTexture* tex;
-    if (!ValidateTexTarget(this, funcName, funcDims, rawTexTarget, &texTarget, &tex))
+    if (!ValidateTexTarget(this, rawTexTarget, "texParameter", &texTarget, &tex))
         return JS::NullValue();
 
     if (!IsTexParamValid(pname)) {
         ErrorInvalidEnumInfo("getTexParameter: pname", pname);
         return JS::NullValue();
     }
 
     return tex->GetTexParameter(texTarget, pname);
@@ -287,275 +256,214 @@ WebGLContext::IsTexture(WebGLTexture* te
 }
 
 void
 WebGLContext::TexParameter_base(GLenum rawTexTarget, GLenum pname, GLint* maybeIntParam,
                                 GLfloat* maybeFloatParam)
 {
     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))
+    if (!ValidateTexTarget(this, rawTexTarget, "texParameter", &texTarget, &tex))
         return;
 
     tex->TexParameter(texTarget, pname, maybeIntParam, maybeFloatParam);
 }
 
 //////////////////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////////////////
 // Uploads
 
 
-////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////
 // TexImage
 
 void
 WebGLContext::TexImage2D(GLenum rawTexImageTarget, GLint level, GLenum internalFormat,
+                         GLenum unpackFormat, GLenum unpackType, dom::Element* elem,
+                         ErrorResult* const out_rv)
+{
+    TexImageTarget texImageTarget;
+    WebGLTexture* tex;
+    if (!ValidateTexImageTarget(this, rawTexImageTarget, "texImage2D", &texImageTarget,
+                                &tex))
+    {
+        return;
+    }
+
+    tex->TexImage2D(texImageTarget, level, internalFormat, unpackFormat, unpackType, elem,
+                    out_rv);
+}
+
+void
+WebGLContext::TexImage2D(GLenum rawTexImageTarget, GLint level, GLenum internalFormat,
                          GLsizei width, GLsizei height, GLint border, GLenum unpackFormat,
                          GLenum unpackType,
                          const dom::Nullable<dom::ArrayBufferViewOrSharedArrayBufferView>& maybeView,
-                         ErrorResult&)
+                         ErrorResult& out_rv)
 {
-    const char funcName[] = "texImage2D";
-    const uint8_t funcDims = 2;
-
-    TexImageTarget target;
+    TexImageTarget texImageTarget;
     WebGLTexture* tex;
-    if (!ValidateTexImageTarget(this, funcName, funcDims, rawTexImageTarget, &target,
+    if (!ValidateTexImageTarget(this, rawTexImageTarget, "texImage2D", &texImageTarget,
                                 &tex))
     {
         return;
     }
 
-    const bool isSubImage = false;
-    const GLint xOffset = 0;
-    const GLint yOffset = 0;
-    const GLint zOffset = 0;
-    const GLsizei depth = 1;
-    tex->TexOrSubImage(isSubImage, funcName, target, level, internalFormat, xOffset,
-                       yOffset, zOffset, width, height, depth, border, unpackFormat,
-                       unpackType, maybeView);
+    tex->TexImage2D(texImageTarget, level, internalFormat, width, height, border,
+                    unpackFormat, unpackType, maybeView, &out_rv);
 }
 
 void
 WebGLContext::TexImage2D(GLenum rawTexImageTarget, GLint level, GLenum internalFormat,
                          GLenum unpackFormat, GLenum unpackType,
-                         dom::ImageData* imageData, ErrorResult&)
+                         dom::ImageData* imageData, ErrorResult& out_rv)
 {
-    const char funcName[] = "texImage2D";
-    const uint8_t funcDims = 2;
-
-    TexImageTarget target;
+    TexImageTarget texImageTarget;
     WebGLTexture* tex;
-    if (!ValidateTexImageTarget(this, funcName, funcDims, rawTexImageTarget, &target,
+    if (!ValidateTexImageTarget(this, rawTexImageTarget, "texImage2D", &texImageTarget,
                                 &tex))
     {
         return;
     }
 
-    const bool isSubImage = false;
-    const GLint xOffset = 0;
-    const GLint yOffset = 0;
-    const GLint zOffset = 0;
-    tex->TexOrSubImage(isSubImage, funcName, target, level, internalFormat, xOffset,
-                       yOffset, zOffset, unpackFormat, unpackType, imageData);
+    tex->TexImage2D(texImageTarget, level, internalFormat, unpackFormat, unpackType,
+                    imageData, &out_rv);
 }
 
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// TexSubImage
+
+
 void
-WebGLContext::TexImage2D(GLenum rawTexImageTarget, GLint level, GLenum internalFormat,
-                         GLenum unpackFormat, GLenum unpackType, dom::Element* elem,
-                         ErrorResult* const out_error)
+WebGLContext::TexSubImage2D(GLenum rawTexImageTarget, GLint level, GLint xOffset,
+                            GLint yOffset, GLenum unpackFormat, GLenum unpackType,
+                            dom::Element* elem, ErrorResult* const out_rv)
 {
-    const char funcName[] = "texImage2D";
-    const uint8_t funcDims = 2;
-
-    TexImageTarget target;
+    TexImageTarget texImageTarget;
     WebGLTexture* tex;
-    if (!ValidateTexImageTarget(this, funcName, funcDims, rawTexImageTarget, &target,
+    if (!ValidateTexImageTarget(this, rawTexImageTarget, "texSubImage2D", &texImageTarget,
                                 &tex))
     {
         return;
     }
 
-    const bool isSubImage = false;
-    const GLint xOffset = 0;
-    const GLint yOffset = 0;
-    const GLint zOffset = 0;
-    tex->TexOrSubImage(isSubImage, funcName, target, level, internalFormat, xOffset,
-                       yOffset, zOffset, unpackFormat, unpackType, elem, out_error);
+    tex->TexSubImage2D(texImageTarget, level, xOffset, yOffset, unpackFormat, unpackType,
+                       elem, out_rv);
 }
 
-////////////////////////////////////////
-// TexSubImage
-
 void
 WebGLContext::TexSubImage2D(GLenum rawTexImageTarget, GLint level, GLint xOffset,
                             GLint yOffset, GLsizei width, GLsizei height,
                             GLenum unpackFormat, GLenum unpackType,
                             const dom::Nullable<dom::ArrayBufferViewOrSharedArrayBufferView>& maybeView,
-                            ErrorResult&)
+                            ErrorResult& out_rv)
 {
-    const char funcName[] = "texSubImage2D";
-    const uint8_t funcDims = 2;
-
-    TexImageTarget target;
+    TexImageTarget texImageTarget;
     WebGLTexture* tex;
-    if (!ValidateTexImageTarget(this, funcName, funcDims, rawTexImageTarget, &target,
-                                &tex))
-    {
-        return;
-    }
-
-    const bool isSubImage = true;
-    const GLenum internalFormat = 0;
-    const GLint zOffset = 0;
-    const GLsizei depth = 1;
-    const GLint border = 0;
-    tex->TexOrSubImage(isSubImage, funcName, target, level, internalFormat, xOffset,
-                       yOffset, zOffset, width, height, depth, border, unpackFormat,
-                       unpackType, maybeView);
-}
-
-void
-WebGLContext::TexSubImage2D(GLenum rawTexImageTarget, GLint level, GLint xOffset,
-                            GLint yOffset, GLenum unpackFormat, GLenum unpackType,
-                            dom::ImageData* imageData, ErrorResult&)
-{
-    const char funcName[] = "texSubImage2D";
-    const uint8_t funcDims = 2;
-
-    TexImageTarget target;
-    WebGLTexture* tex;
-    if (!ValidateTexImageTarget(this, funcName, funcDims, rawTexImageTarget, &target,
+    if (!ValidateTexImageTarget(this, rawTexImageTarget, "texSubImage2D", &texImageTarget,
                                 &tex))
     {
         return;
     }
 
-    const bool isSubImage = true;
-    const GLenum internalFormat = 0;
-    const GLint zOffset = 0;
-    tex->TexOrSubImage(isSubImage, funcName, target, level, internalFormat, xOffset,
-                       yOffset, zOffset, unpackFormat, unpackType, imageData);
+    tex->TexSubImage2D(texImageTarget, level, xOffset, yOffset, width, height,
+                       unpackFormat, unpackType, maybeView, &out_rv);
 }
 
 void
-WebGLContext::TexSubImage2D(GLenum rawTexImageTarget, GLint level, GLint xOffset,
-                            GLint yOffset, GLenum unpackFormat, GLenum unpackType,
-                            dom::Element* elem, ErrorResult* const out_error)
+WebGLContext::TexSubImage2D(GLenum rawTexImageTarget, GLint level, GLint xOffset, GLint yOffset,
+                            GLenum unpackFormat, GLenum unpackType, dom::ImageData* imageData,
+                            ErrorResult& out_rv)
 {
-    const char funcName[] = "texSubImage2D";
-    const uint8_t funcDims = 2;
-
-    TexImageTarget target;
+    TexImageTarget texImageTarget;
     WebGLTexture* tex;
-    if (!ValidateTexImageTarget(this, funcName, funcDims, rawTexImageTarget, &target,
+    if (!ValidateTexImageTarget(this, rawTexImageTarget, "texSubImage2D", &texImageTarget,
                                 &tex))
     {
         return;
     }
 
-    const bool isSubImage = true;
-    const GLenum internalFormat = 0;
-    const GLint zOffset = 0;
-    tex->TexOrSubImage(isSubImage, funcName, target, level, internalFormat, xOffset,
-                       yOffset, zOffset, unpackFormat, unpackType, elem, out_error);
+    tex->TexSubImage2D(texImageTarget, level, xOffset, yOffset, unpackFormat, unpackType,
+                       imageData, &out_rv);
 }
 
-////////////////////////////////////////
-// CompressedTex(Sub)Image
-
-void
-WebGLContext::CompressedTexImage2D(GLenum rawTexImageTarget, GLint level,
-                                   GLenum internalFormat, GLsizei width, GLsizei height,
-                                   GLint border,
-                                   const dom::ArrayBufferViewOrSharedArrayBufferView& view)
-{
-    const char funcName[] = "compressedTexImage2D";
-    const uint8_t funcDims = 2;
-
-    TexImageTarget target;
-    WebGLTexture* tex;
-    if (!ValidateTexImageTarget(this, funcName, funcDims, rawTexImageTarget, &target,
-                                &tex))
-    {
-        return;
-    }
-
-    const GLsizei depth = 1;
-
-    tex->CompressedTexImage(funcName, target, level, internalFormat, width, height, depth,
-                            border, view);
-}
 
-void
-WebGLContext::CompressedTexSubImage2D(GLenum rawTexImageTarget, GLint level,
-                                      GLint xOffset, GLint yOffset, GLsizei width,
-                                      GLsizei height, GLenum sizedUnpackFormat,
-                                      const dom::ArrayBufferViewOrSharedArrayBufferView& view)
-{
-    const char funcName[] = "compressedTexSubImage2D";
-    const uint8_t funcDims = 2;
+//////////////////////////////////////////////////////////////////////////////////////////
+// CopyTex(Sub)Image
 
-    TexImageTarget target;
-    WebGLTexture* tex;
-    if (!ValidateTexImageTarget(this, funcName, funcDims, rawTexImageTarget, &target,
-                                &tex))
-    {
-        return;
-    }
-
-    const GLint zOffset = 0;
-    const GLsizei depth = 1;
-
-    tex->CompressedTexSubImage(funcName, target, level, xOffset, yOffset, zOffset, width,
-                               height, depth, sizedUnpackFormat, view);
-}
-
-////////////////////////////////////////
-// CopyTex(Sub)Image
 
 void
 WebGLContext::CopyTexImage2D(GLenum rawTexImageTarget, GLint level, GLenum internalFormat,
                              GLint x, GLint y, GLsizei width, GLsizei height,
                              GLint border)
 {
-    const char funcName[] = "copyTexImage2D";
-    const uint8_t funcDims = 2;
-
-    TexImageTarget target;
+    TexImageTarget texImageTarget;
     WebGLTexture* tex;
-    if (!ValidateTexImageTarget(this, funcName, funcDims, rawTexImageTarget, &target,
-                                &tex))
+    if (!ValidateTexImageTarget(this, rawTexImageTarget, "copyTexImage2D",
+                                &texImageTarget, &tex))
     {
         return;
     }
 
-    tex->CopyTexImage2D(target, level, internalFormat, x, y, width, height, border);
+    tex->CopyTexImage2D(texImageTarget, level, internalFormat, x, y, width, height,
+                        border);
 }
 
 void
 WebGLContext::CopyTexSubImage2D(GLenum rawTexImageTarget, GLint level, GLint xOffset,
                                 GLint yOffset, GLint x, GLint y, GLsizei width,
                                 GLsizei height)
 {
-    const char funcName[] = "copyTexSubImage2D";
-    const uint8_t funcDims = 2;
-
-    TexImageTarget target;
+    TexImageTarget texImageTarget;
     WebGLTexture* tex;
-    if (!ValidateTexImageTarget(this, funcName, funcDims, rawTexImageTarget, &target,
-                                &tex))
+    if (!ValidateTexImageTarget(this, rawTexImageTarget, "copyTexSubImage2D",
+                                &texImageTarget, &tex))
     {
         return;
     }
 
-    const GLint zOffset = 0;
+    tex->CopyTexSubImage2D(texImageTarget, level, xOffset, yOffset, x, y, width, height);
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// CompressedTex(Sub)Image
+
+
+void
+WebGLContext::CompressedTexImage2D(GLenum rawTexImageTarget, GLint level,
+                                   GLenum internalFormat, GLsizei width, GLsizei height,
+                                   GLint border, const dom::ArrayBufferViewOrSharedArrayBufferView& view)
+{
+    TexImageTarget texImageTarget;
+    WebGLTexture* tex;
+    if (!ValidateTexImageTarget(this, rawTexImageTarget, "compressedTexImage2D",
+                                &texImageTarget, &tex))
+    {
+        return;
+    }
 
-    tex->CopyTexSubImage(funcName, target, level, xOffset, yOffset, zOffset, x, y, width,
-                         height);
+    tex->CompressedTexImage2D(texImageTarget, level, internalFormat, width, height,
+                              border, view);
+}
+
+void
+WebGLContext::CompressedTexSubImage2D(GLenum rawTexImageTarget, GLint level,
+                                      GLint xOffset, GLint yOffset, GLsizei width,
+                                      GLsizei height, GLenum unpackFormat,
+                                      const dom::ArrayBufferViewOrSharedArrayBufferView& view)
+{
+    TexImageTarget texImageTarget;
+    WebGLTexture* tex;
+    if (!ValidateTexImageTarget(this, rawTexImageTarget, "compressedTexSubImage2D",
+                                &texImageTarget, &tex))
+    {
+        return;
+    }
+
+    tex->CompressedTexSubImage2D(texImageTarget, level, xOffset, yOffset, width, height,
+                                 unpackFormat, view);
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGLContextUtils.cpp
+++ b/dom/canvas/WebGLContextUtils.cpp
@@ -22,45 +22,455 @@
 #include "WebGLFramebuffer.h"
 #include "WebGLProgram.h"
 #include "WebGLTexture.h"
 #include "WebGLVertexArray.h"
 #include "WebGLContextUtils.h"
 
 namespace mozilla {
 
+bool
+IsGLDepthFormat(TexInternalFormat internalformat)
+{
+    TexInternalFormat unsizedformat = UnsizedInternalFormatFromInternalFormat(internalformat);
+    return unsizedformat == LOCAL_GL_DEPTH_COMPONENT;
+}
+
+bool
+IsGLDepthStencilFormat(TexInternalFormat internalformat)
+{
+    TexInternalFormat unsizedformat = UnsizedInternalFormatFromInternalFormat(internalformat);
+    return unsizedformat == LOCAL_GL_DEPTH_STENCIL;
+}
+
+bool
+FormatHasAlpha(TexInternalFormat internalformat)
+{
+    TexInternalFormat unsizedformat = UnsizedInternalFormatFromInternalFormat(internalformat);
+    return unsizedformat == LOCAL_GL_RGBA ||
+           unsizedformat == LOCAL_GL_LUMINANCE_ALPHA ||
+           unsizedformat == LOCAL_GL_ALPHA ||
+           unsizedformat == LOCAL_GL_SRGB_ALPHA ||
+           unsizedformat == LOCAL_GL_RGBA_INTEGER;
+}
+
 TexTarget
 TexImageTargetToTexTarget(TexImageTarget texImageTarget)
 {
     switch (texImageTarget.get()) {
+    case LOCAL_GL_TEXTURE_2D:
+    case LOCAL_GL_TEXTURE_3D:
+        return texImageTarget.get();
     case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X:
     case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
     case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
     case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
     case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
     case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
         return LOCAL_GL_TEXTURE_CUBE_MAP;
-
     default:
-        return texImageTarget.get();
+        MOZ_ASSERT(false, "Bad texture target");
+        // Should be caught by the constructor for TexTarget
+        return LOCAL_GL_NONE;
     }
 }
 
 JS::Value
 StringValue(JSContext* cx, const char* chars, ErrorResult& rv)
 {
     JSString* str = JS_NewStringCopyZ(cx, chars);
     if (!str) {
         rv.Throw(NS_ERROR_OUT_OF_MEMORY);
         return JS::NullValue();
     }
 
     return JS::StringValue(str);
 }
 
+GLComponents::GLComponents(TexInternalFormat internalformat)
+{
+    TexInternalFormat unsizedformat = UnsizedInternalFormatFromInternalFormat(internalformat);
+    mComponents = 0;
+
+    switch (unsizedformat.get()) {
+    case LOCAL_GL_RGBA:
+    case LOCAL_GL_RGBA4:
+    case LOCAL_GL_RGBA8:
+    case LOCAL_GL_RGB5_A1:
+    // Luminance + Alpha can be converted
+    // to and from RGBA
+    case LOCAL_GL_LUMINANCE_ALPHA:
+        mComponents |= Components::Alpha;
+    // Drops through
+    case LOCAL_GL_RGB:
+    case LOCAL_GL_RGB565:
+    // Luminance can be converted to and from RGB
+    case LOCAL_GL_LUMINANCE:
+        mComponents |= Components::Red | Components::Green | Components::Blue;
+        break;
+    case LOCAL_GL_ALPHA:
+        mComponents |= Components::Alpha;
+        break;
+    case LOCAL_GL_DEPTH_COMPONENT:
+        mComponents |= Components::Depth;
+        break;
+    case LOCAL_GL_DEPTH_STENCIL:
+        mComponents |= Components::Stencil;
+        break;
+    default:
+        MOZ_ASSERT(false, "Unhandled case - GLComponents");
+        break;
+    }
+}
+
+bool
+GLComponents::IsSubsetOf(const GLComponents& other) const
+{
+    return (mComponents | other.mComponents) == other.mComponents;
+}
+
+TexType
+TypeFromInternalFormat(TexInternalFormat internalformat)
+{
+#define HANDLE_WEBGL_INTERNAL_FORMAT(table_effectiveinternalformat, table_internalformat, table_type) \
+    if (internalformat == table_effectiveinternalformat) { \
+        return table_type; \
+    }
+
+#include "WebGLInternalFormatsTable.h"
+
+    // if we're here, then internalformat is not an effective internalformat i.e. is an unsized internalformat.
+    return LOCAL_GL_NONE; // no size, no type
+}
+
+TexInternalFormat
+UnsizedInternalFormatFromInternalFormat(TexInternalFormat internalformat)
+{
+#define HANDLE_WEBGL_INTERNAL_FORMAT(table_effectiveinternalformat, table_internalformat, table_type) \
+    if (internalformat == table_effectiveinternalformat) { \
+        return table_internalformat; \
+    }
+
+#include "WebGLInternalFormatsTable.h"
+
+    // if we're here, then internalformat is not an effective internalformat i.e. is an unsized internalformat.
+    // so we can just return it.
+    return internalformat;
+}
+
+/*
+ * Note that the following two functions are inverse of each other:
+ * EffectiveInternalFormatFromInternalFormatAndType and
+ * InternalFormatAndTypeFromEffectiveInternalFormat both implement OpenGL ES 3.0.3 Table 3.2
+ * but in opposite directions.
+ */
+TexInternalFormat
+EffectiveInternalFormatFromUnsizedInternalFormatAndType(TexInternalFormat internalformat,
+                                                        TexType type)
+{
+    MOZ_ASSERT(TypeFromInternalFormat(internalformat) == LOCAL_GL_NONE);
+
+#define HANDLE_WEBGL_INTERNAL_FORMAT(table_effectiveinternalformat, table_internalformat, table_type) \
+    if (internalformat == table_internalformat && type == table_type) { \
+        return table_effectiveinternalformat; \
+    }
+
+#include "WebGLInternalFormatsTable.h"
+
+    // If we're here, that means that type was incompatible with the given internalformat.
+    return LOCAL_GL_NONE;
+}
+
+void
+UnsizedInternalFormatAndTypeFromEffectiveInternalFormat(TexInternalFormat effectiveinternalformat,
+                                                        TexInternalFormat* const out_internalformat,
+                                                        TexType* const out_type)
+{
+    MOZ_ASSERT(TypeFromInternalFormat(effectiveinternalformat) != LOCAL_GL_NONE);
+
+    MOZ_ASSERT(out_internalformat);
+    MOZ_ASSERT(out_type);
+
+    GLenum internalformat = LOCAL_GL_NONE;
+    GLenum type = LOCAL_GL_NONE;
+
+    switch (effectiveinternalformat.get()) {
+
+#define HANDLE_WEBGL_INTERNAL_FORMAT(table_effectiveinternalformat, table_internalformat, table_type) \
+    case table_effectiveinternalformat: \
+        internalformat = table_internalformat; \
+        type = table_type; \
+        break;
+
+#include "WebGLInternalFormatsTable.h"
+
+        default:
+            MOZ_CRASH(); // impossible to get here
+    }
+
+    *out_internalformat = internalformat;
+    *out_type = type;
+}
+
+TexInternalFormat
+EffectiveInternalFormatFromInternalFormatAndType(TexInternalFormat internalformat,
+                                                 TexType type)
+{
+    TexType typeOfInternalFormat = TypeFromInternalFormat(internalformat);
+    if (typeOfInternalFormat == LOCAL_GL_NONE)
+        return EffectiveInternalFormatFromUnsizedInternalFormatAndType(internalformat, type);
+
+    if (typeOfInternalFormat == type)
+        return internalformat;
+
+    return LOCAL_GL_NONE;
+}
+
+/**
+ * Convert effective internalformat into GL function parameters
+ * valid for underlying driver.
+ */
+void
+DriverFormatsFromEffectiveInternalFormat(gl::GLContext* gl,
+                                         TexInternalFormat effectiveinternalformat,
+                                         GLenum* const out_driverInternalFormat,
+                                         GLenum* const out_driverFormat,
+                                         GLenum* const out_driverType)
+{
+    MOZ_ASSERT(out_driverInternalFormat);
+    MOZ_ASSERT(out_driverFormat);
+    MOZ_ASSERT(out_driverType);
+
+    TexInternalFormat unsizedinternalformat = LOCAL_GL_NONE;
+    TexType type = LOCAL_GL_NONE;
+
+    UnsizedInternalFormatAndTypeFromEffectiveInternalFormat(effectiveinternalformat,
+                                                            &unsizedinternalformat,
+                                                            &type);
+
+    // driverType: almost always the generic type that we just got, except on ES
+    // we must replace HALF_FLOAT by HALF_FLOAT_OES
+    GLenum driverType = type.get();
+    if (gl->IsGLES() && type == LOCAL_GL_HALF_FLOAT)
+        driverType = LOCAL_GL_HALF_FLOAT_OES;
+
+    // driverFormat: always just the unsized internalformat that we just got
+    GLenum driverFormat = unsizedinternalformat.get();
+
+    // driverInternalFormat: almost always the same as driverFormat, but on desktop GL,
+    // in some cases we must pass a different value. On ES, they are equal by definition
+    // as it is an error to pass internalformat!=format.
+    GLenum driverInternalFormat = driverFormat;
+    if (gl->IsCompatibilityProfile()) {
+        // Cases where desktop OpenGL requires a tweak to 'format'
+        if (driverFormat == LOCAL_GL_SRGB)
+            driverFormat = LOCAL_GL_RGB;
+        else if (driverFormat == LOCAL_GL_SRGB_ALPHA)
+            driverFormat = LOCAL_GL_RGBA;
+
+        // WebGL2's new formats are not legal values for internalformat,
+        // as using unsized internalformat is deprecated.
+        if (driverFormat == LOCAL_GL_RED ||
+            driverFormat == LOCAL_GL_RG ||
+            driverFormat == LOCAL_GL_RED_INTEGER ||
+            driverFormat == LOCAL_GL_RG_INTEGER ||
+            driverFormat == LOCAL_GL_RGB_INTEGER ||
+            driverFormat == LOCAL_GL_RGBA_INTEGER)
+        {
+            driverInternalFormat = effectiveinternalformat.get();
+        }
+
+        // Cases where desktop OpenGL requires a sized internalformat,
+        // as opposed to the unsized internalformat that had the same
+        // GLenum value as 'format', in order to get the precise
+        // semantics that we want. For example, for floating-point formats,
+        // we seem to need a sized internalformat to get non-clamped floating
+        // point texture sampling. Can't find the spec reference for that,
+        // but that's at least the case on my NVIDIA driver version 331.
+        if (unsizedinternalformat == LOCAL_GL_DEPTH_COMPONENT ||
+            unsizedinternalformat == LOCAL_GL_DEPTH_STENCIL ||
+            type == LOCAL_GL_FLOAT ||
+            type == LOCAL_GL_HALF_FLOAT)
+        {
+            driverInternalFormat = effectiveinternalformat.get();
+        }
+    }
+
+    // OpenGL core profile removed texture formats ALPHA, LUMINANCE and LUMINANCE_ALPHA
+    if (gl->IsCoreProfile()) {
+        switch (driverFormat) {
+        case LOCAL_GL_ALPHA:
+        case LOCAL_GL_LUMINANCE:
+            driverInternalFormat = driverFormat = LOCAL_GL_RED;
+            break;
+
+        case LOCAL_GL_LUMINANCE_ALPHA:
+            driverInternalFormat = driverFormat = LOCAL_GL_RG;
+            break;
+        }
+    }
+
+    *out_driverInternalFormat = driverInternalFormat;
+    *out_driverFormat = driverFormat;
+    *out_driverType = driverType;
+}
+
+// Map R to A
+static const GLenum kLegacyAlphaSwizzle[4] = {
+    LOCAL_GL_ZERO, LOCAL_GL_ZERO, LOCAL_GL_ZERO, LOCAL_GL_RED
+};
+// Map R to RGB
+static const GLenum kLegacyLuminanceSwizzle[4] = {
+    LOCAL_GL_RED, LOCAL_GL_RED, LOCAL_GL_RED, LOCAL_GL_ONE
+};
+// Map R to RGB, G to A
+static const GLenum kLegacyLuminanceAlphaSwizzle[4] = {
+    LOCAL_GL_RED, LOCAL_GL_RED, LOCAL_GL_RED, LOCAL_GL_GREEN
+};
+
+void
+SetLegacyTextureSwizzle(gl::GLContext* gl, GLenum target, GLenum internalformat)
+{
+    if (!gl->IsCoreProfile())
+        return;
+
+    /* Only support swizzling on core profiles. */
+    // Bug 1159117: Fix this.
+    // MOZ_RELEASE_ASSERT(gl->IsSupported(gl::GLFeature::texture_swizzle));
+
+    switch (internalformat) {
+    case LOCAL_GL_ALPHA:
+        gl->fTexParameteriv(target, LOCAL_GL_TEXTURE_SWIZZLE_RGBA,
+                            (GLint*) kLegacyAlphaSwizzle);
+        break;
+
+    case LOCAL_GL_LUMINANCE:
+        gl->fTexParameteriv(target, LOCAL_GL_TEXTURE_SWIZZLE_RGBA,
+                            (GLint*) kLegacyLuminanceSwizzle);
+        break;
+
+    case LOCAL_GL_LUMINANCE_ALPHA:
+        gl->fTexParameteriv(target, LOCAL_GL_TEXTURE_SWIZZLE_RGBA,
+                            (GLint*) kLegacyLuminanceAlphaSwizzle);
+        break;
+    }
+}
+
+/**
+ * Return the bits per texel for format & type combination.
+ * Assumes that format & type are a valid combination as checked with
+ * ValidateTexImageFormatAndType().
+ */
+size_t
+GetBitsPerTexel(TexInternalFormat effectiveinternalformat)
+{
+    switch (effectiveinternalformat.get()) {
+    case LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1:
+    case LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1:
+        return 2;
+
+    case LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
+    case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
+    case LOCAL_GL_ATC_RGB:
+    case LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1:
+    case LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1:
+    case LOCAL_GL_ETC1_RGB8_OES:
+        return 4;
+
+    case LOCAL_GL_ALPHA8:
+    case LOCAL_GL_LUMINANCE8:
+    case LOCAL_GL_R8:
+    case LOCAL_GL_R8I:
+    case LOCAL_GL_R8UI:
+    case LOCAL_GL_R8_SNORM:
+    case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
+    case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
+    case LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA:
+    case LOCAL_GL_ATC_RGBA_INTERPOLATED_ALPHA:
+        return 8;
+
+    case LOCAL_GL_LUMINANCE8_ALPHA8:
+    case LOCAL_GL_RGBA4:
+    case LOCAL_GL_RGB5_A1:
+    case LOCAL_GL_DEPTH_COMPONENT16:
+    case LOCAL_GL_RG8:
+    case LOCAL_GL_R16I:
+    case LOCAL_GL_R16UI:
+    case LOCAL_GL_RGB565:
+    case LOCAL_GL_R16F:
+    case LOCAL_GL_RG8I:
+    case LOCAL_GL_RG8UI:
+    case LOCAL_GL_RG8_SNORM:
+    case LOCAL_GL_ALPHA16F_EXT:
+    case LOCAL_GL_LUMINANCE16F_EXT:
+        return 16;
+
+    case LOCAL_GL_RGB8:
+    case LOCAL_GL_DEPTH_COMPONENT24:
+    case LOCAL_GL_SRGB8:
+    case LOCAL_GL_RGB8UI:
+    case LOCAL_GL_RGB8I:
+    case LOCAL_GL_RGB8_SNORM:
+        return 24;
+
+    case LOCAL_GL_RGBA8:
+    case LOCAL_GL_RGB10_A2:
+    case LOCAL_GL_R32F:
+    case LOCAL_GL_RG16F:
+    case LOCAL_GL_R32I:
+    case LOCAL_GL_R32UI:
+    case LOCAL_GL_RG16I:
+    case LOCAL_GL_RG16UI:
+    case LOCAL_GL_DEPTH24_STENCIL8:
+    case LOCAL_GL_R11F_G11F_B10F:
+    case LOCAL_GL_RGB9_E5:
+    case LOCAL_GL_SRGB8_ALPHA8:
+    case LOCAL_GL_DEPTH_COMPONENT32F:
+    case LOCAL_GL_RGBA8UI:
+    case LOCAL_GL_RGBA8I:
+    case LOCAL_GL_RGBA8_SNORM:
+    case LOCAL_GL_RGB10_A2UI:
+    case LOCAL_GL_LUMINANCE_ALPHA16F_EXT:
+    case LOCAL_GL_ALPHA32F_EXT:
+    case LOCAL_GL_LUMINANCE32F_EXT:
+        return 32;
+
+    case LOCAL_GL_DEPTH32F_STENCIL8:
+        return 40;
+
+    case LOCAL_GL_RGB16F:
+    case LOCAL_GL_RGB16UI:
+    case LOCAL_GL_RGB16I:
+        return 48;
+
+    case LOCAL_GL_RG32F:
+    case LOCAL_GL_RG32I:
+    case LOCAL_GL_RG32UI:
+    case LOCAL_GL_RGBA16F:
+    case LOCAL_GL_RGBA16UI:
+    case LOCAL_GL_RGBA16I:
+    case LOCAL_GL_LUMINANCE_ALPHA32F_EXT:
+        return 64;
+
+    case LOCAL_GL_RGB32F:
+    case LOCAL_GL_RGB32UI:
+    case LOCAL_GL_RGB32I:
+        return 96;
+
+    case LOCAL_GL_RGBA32F:
+    case LOCAL_GL_RGBA32UI:
+    case LOCAL_GL_RGBA32I:
+        return 128;
+
+    default:
+        MOZ_ASSERT(false, "Unhandled format");
+        return 0;
+    }
+}
+
 void
 WebGLContext::GenerateWarning(const char* fmt, ...)
 {
     va_list ap;
     va_start(ap, fmt);
 
     GenerateWarning(fmt, ap);
 
@@ -105,16 +515,37 @@ bool
 WebGLContext::ShouldGenerateWarnings() const
 {
     if (mMaxWarnings == -1)
         return true;
 
     return mAlreadyGeneratedWarnings < mMaxWarnings;
 }
 
+CheckedUint32
+WebGLContext::GetImageSize(GLsizei height, GLsizei width, GLsizei depth,
+                           uint32_t pixelSize, uint32_t packOrUnpackAlignment)
+{
+    CheckedUint32 checked_plainRowSize = CheckedUint32(width) * pixelSize;
+
+    // alignedRowSize = row size rounded up to next multiple of packAlignment
+    CheckedUint32 checked_alignedRowSize = RoundedToNextMultipleOf(checked_plainRowSize, packOrUnpackAlignment);
+
+    // if height is 0, we don't need any memory to store this; without this check, we'll get an overflow
+    CheckedUint32 checked_2dImageSize = 0;
+    if (height >= 1) {
+        checked_2dImageSize = (height-1) * checked_alignedRowSize +
+                              checked_plainRowSize;
+    }
+
+    // FIXME - we should honor UNPACK_IMAGE_HEIGHT
+    CheckedUint32 checked_imageSize = checked_2dImageSize * depth;
+    return checked_imageSize;
+}
+
 void
 WebGLContext::SynthesizeGLError(GLenum err)
 {
     /* ES2 section 2.5 "GL Errors" states that implementations can have
      * multiple 'flags', as errors might be caught in different parts of
      * a distributed implementation.
      * We're signing up as a distributed implementation here, with
      * separate flags for WebGL and the underlying GLContext.
@@ -676,17 +1107,17 @@ AssertUintParamCorrect(gl::GLContext*, G
 void
 WebGLContext::AssertCachedBindings()
 {
 #ifdef DEBUG
     MakeContextCurrent();
 
     GetAndFlushUnderlyingGLErrors();
 
-    if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::OES_vertex_array_object)) {
+    if (IsExtensionEnabled(WebGLExtensionID::OES_vertex_array_object)) {
         GLuint bound = mBoundVertexArray ? mBoundVertexArray->GLName() : 0;
         AssertUintParamCorrect(gl, LOCAL_GL_VERTEX_ARRAY_BINDING, bound);
     }
 
     // Bound object state
     if (IsWebGL2()) {
         GLuint bound = mBoundDrawFramebuffer ? mBoundDrawFramebuffer->mGLName
                                              : 0;
@@ -732,30 +1163,20 @@ WebGLContext::AssertCachedBindings()
 void
 WebGLContext::AssertCachedState()
 {
 #ifdef DEBUG
     MakeContextCurrent();
 
     GetAndFlushUnderlyingGLErrors();
 
-    ////////////////
-
-    AssertUintParamCorrect(gl, LOCAL_GL_MAX_COLOR_ATTACHMENTS, mGLMaxColorAttachments);
-    AssertUintParamCorrect(gl, LOCAL_GL_MAX_DRAW_BUFFERS, mGLMaxDrawBuffers);
-
-    if (IsWebGL2() ||
-        IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers))
-    {
-        MOZ_ASSERT(mImplMaxColorAttachments == std::min(mGLMaxColorAttachments,
-                                                        mGLMaxDrawBuffers));
-        MOZ_ASSERT(mImplMaxDrawBuffers == mGLMaxDrawBuffers);
-    } else {
-        MOZ_ASSERT(mImplMaxColorAttachments == 1);
-        MOZ_ASSERT(mImplMaxDrawBuffers == 1);
+    // extensions
+    if (IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers)) {
+        AssertUintParamCorrect(gl, LOCAL_GL_MAX_COLOR_ATTACHMENTS, mGLMaxColorAttachments);
+        AssertUintParamCorrect(gl, LOCAL_GL_MAX_DRAW_BUFFERS, mGLMaxDrawBuffers);
     }
 
     // Draw state
     MOZ_ASSERT(gl->fIsEnabled(LOCAL_GL_DEPTH_TEST) == mDepthTestEnabled);
     MOZ_ASSERT(gl->fIsEnabled(LOCAL_GL_DITHER) == mDitherEnabled);
     MOZ_ASSERT_IF(IsWebGL2(),
                   gl->fIsEnabled(LOCAL_GL_RASTERIZER_DISCARD) == mRasterizerDiscardEnabled);
     MOZ_ASSERT(gl->fIsEnabled(LOCAL_GL_SCISSOR_TEST) == mScissorTestEnabled);
@@ -808,29 +1229,18 @@ WebGLContext::AssertCachedState()
     // Viewport
     GLint int4[4] = {0, 0, 0, 0};
     gl->fGetIntegerv(LOCAL_GL_VIEWPORT, int4);
     MOZ_ASSERT(int4[0] == mViewportX &&
                int4[1] == mViewportY &&
                int4[2] == mViewportWidth &&
                int4[3] == mViewportHeight);
 
-    AssertUintParamCorrect(gl, LOCAL_GL_PACK_ALIGNMENT, mPixelStore_PackAlignment);
-    AssertUintParamCorrect(gl, LOCAL_GL_UNPACK_ALIGNMENT, mPixelStore_UnpackAlignment);
-
-    if (IsWebGL2()) {
-        AssertUintParamCorrect(gl, LOCAL_GL_UNPACK_IMAGE_HEIGHT, mPixelStore_UnpackImageHeight);
-        AssertUintParamCorrect(gl, LOCAL_GL_UNPACK_SKIP_IMAGES , mPixelStore_UnpackSkipImages);
-        AssertUintParamCorrect(gl, LOCAL_GL_UNPACK_ROW_LENGTH  , mPixelStore_UnpackRowLength);
-        AssertUintParamCorrect(gl, LOCAL_GL_UNPACK_SKIP_ROWS   , mPixelStore_UnpackSkipRows);
-        AssertUintParamCorrect(gl, LOCAL_GL_UNPACK_SKIP_PIXELS , mPixelStore_UnpackSkipPixels);
-        AssertUintParamCorrect(gl, LOCAL_GL_PACK_ROW_LENGTH    , mPixelStore_PackRowLength);
-        AssertUintParamCorrect(gl, LOCAL_GL_PACK_SKIP_ROWS     , mPixelStore_PackSkipRows);
-        AssertUintParamCorrect(gl, LOCAL_GL_PACK_SKIP_PIXELS   , mPixelStore_PackSkipPixels);
-    }
+    AssertUintParamCorrect(gl, LOCAL_GL_PACK_ALIGNMENT, mPixelStorePackAlignment);
+    AssertUintParamCorrect(gl, LOCAL_GL_UNPACK_ALIGNMENT, mPixelStoreUnpackAlignment);
 
     MOZ_ASSERT(!GetAndFlushUnderlyingGLErrors());
 #endif
 }
 
 const char*
 InfoFrom(WebGLTexImageFunc func, WebGLTexDimensions dims)
 {
--- a/dom/canvas/WebGLContextUtils.h
+++ b/dom/canvas/WebGLContextUtils.h
@@ -10,16 +10,45 @@
 
 #include "mozilla/Assertions.h"
 #include "mozilla/dom/BindingUtils.h"
 
 #include "WebGLStrongTypes.h"
 
 namespace mozilla {
 
+bool IsGLDepthFormat(TexInternalFormat webGLFormat);
+bool IsGLDepthStencilFormat(TexInternalFormat webGLFormat);
+bool FormatHasAlpha(TexInternalFormat webGLFormat);
+
+void
+DriverFormatsFromEffectiveInternalFormat(gl::GLContext* gl,
+                                         TexInternalFormat internalformat,
+                                         GLenum* const out_driverInternalFormat,
+                                         GLenum* const out_driverFormat,
+                                         GLenum* const out_driverType);
+TexInternalFormat
+EffectiveInternalFormatFromInternalFormatAndType(TexInternalFormat internalformat,
+                                                 TexType type);
+TexInternalFormat
+EffectiveInternalFormatFromUnsizedInternalFormatAndType(TexInternalFormat internalformat,
+                                                        TexType type);
+void
+UnsizedInternalFormatAndTypeFromEffectiveInternalFormat(TexInternalFormat effectiveinternalformat,
+                                                        TexInternalFormat* const out_internalformat,
+                                                        TexType* const out_type);
+TexType TypeFromInternalFormat(TexInternalFormat internalformat);
+
+TexInternalFormat
+UnsizedInternalFormatFromInternalFormat(TexInternalFormat internalformat);
+
+void SetLegacyTextureSwizzle(gl::GLContext* gl, GLenum target, GLenum internalformat);
+
+size_t GetBitsPerTexel(TexInternalFormat effectiveinternalformat);
+
 // For use with the different texture calls, i.e.
 //   TexImage2D, CopyTex[Sub]Image2D, ...
 // that take a "target" parameter. This parameter is not always the same as
 // the texture binding location, like GL_TEXTURE_2D or GL_TEXTURE_CUBE_MAP.
 // For example, cube maps would pass GL_TEXTURE_CUBE_MAP_[POS|NEG]_[X|Y|Z]
 // instead of just GL_TEXTURE_CUBE_MAP.
 //
 // This function converts the texture image target to the texture target a.k.a.
@@ -27,16 +56,19 @@ namespace mozilla {
 // the currently bound texture is appropriate for this texImageTarget.
 //
 // Returns GL_NONE if passed an invalid texture image target
 TexTarget TexImageTargetToTexTarget(TexImageTarget texImageTarget);
 
 // Helper function to create a JS::Value from a C string
 JS::Value StringValue(JSContext* cx, const char* str, ErrorResult& rv);
 
+bool IsCompressedTextureFormat(GLenum format);
+bool IsTextureFormatCompressed(TexInternalFormat format);
+
 struct GLComponents
 {
     unsigned char mComponents;
 
     enum Components {
         Red     = (1 << 0),
         Green   = (1 << 1),
         Blue    = (1 << 2),
@@ -59,17 +91,17 @@ struct GLComponents
 template <typename WebGLObjectType>
 JS::Value
 WebGLContext::WebGLObjectAsJSValue(JSContext* cx, const WebGLObjectType* object,
                                    ErrorResult& rv) const
 {
     if (!object)
         return JS::NullValue();
 
-    MOZ_ASSERT(this == object->mContext);
+    MOZ_ASSERT(this == object->Context());
     JS::Rooted<JS::Value> v(cx);
     JS::Rooted<JSObject*> wrapper(cx, GetWrapper());
     JSAutoCompartment ac(cx, wrapper);
     if (!dom::GetOrCreateDOMReflector(cx, const_cast<WebGLObjectType*>(object), &v)) {
         rv.Throw(NS_ERROR_FAILURE);
         return JS::NullValue();
     }
     return v;
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -30,32 +30,142 @@
 #include "WebGLVertexAttribData.h"
 
 #if defined(MOZ_WIDGET_COCOA)
 #include "nsCocoaFeatures.h"
 #endif
 
 namespace mozilla {
 
+/**
+ * Return the block size for format.
+ */
+static void
+BlockSizeFor(GLenum format, GLint* const out_blockWidth,
+             GLint* const out_blockHeight)
+{
+    MOZ_ASSERT(out_blockWidth && out_blockHeight);
+
+    switch (format) {
+    case LOCAL_GL_ATC_RGB:
+    case LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA:
+    case LOCAL_GL_ATC_RGBA_INTERPOLATED_ALPHA:
+    case LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
+    case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
+    case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
+    case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
+        *out_blockWidth = 4;
+        *out_blockHeight = 4;
+        break;
+
+    case LOCAL_GL_ETC1_RGB8_OES:
+        // 4x4 blocks, but no 4-multiple requirement.
+        break;
+
+    default:
+        break;
+    }
+}
+
+static bool
+IsCompressedFunc(WebGLTexImageFunc func)
+{
+    return func == WebGLTexImageFunc::CompTexImage ||
+           func == WebGLTexImageFunc::CompTexSubImage;
+}
+
+/**
+ * Same as ErrorInvalidEnum but uses WebGLContext::EnumName to print displayable
+ * name for \a glenum.
+ */
+static void
+ErrorInvalidEnumWithName(WebGLContext* ctx, const char* msg, GLenum glenum,
+                         WebGLTexImageFunc func, WebGLTexDimensions dims)
+{
+    const char* name = WebGLContext::EnumName(glenum);
+    if (name) {
+        ctx->ErrorInvalidEnum("%s: %s %s", InfoFrom(func, dims), msg, name);
+    } else {
+        ctx->ErrorInvalidEnum("%s: %s 0x%04x", InfoFrom(func, dims), msg,
+                              glenum);
+    }
+}
+
+/**
+ * Return true if the format is valid for source calls.
+ */
+static bool
+IsAllowedFromSource(GLenum format, WebGLTexImageFunc func)
+{
+    switch (format) {
+    case LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
+    case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
+    case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
+    case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
+    case LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1:
+    case LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1:
+    case LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1:
+    case LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1:
+        return (func == WebGLTexImageFunc::CompTexImage ||
+                func == WebGLTexImageFunc::CompTexSubImage);
+
+    case LOCAL_GL_ATC_RGB:
+    case LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA:
+    case LOCAL_GL_ATC_RGBA_INTERPOLATED_ALPHA:
+    case LOCAL_GL_ETC1_RGB8_OES:
+        return func == WebGLTexImageFunc::CompTexImage;
+    }
+
+    return true;
+}
+
+/**
+ * Returns true if func is a CopyTexImage variant.
+ */
+static bool
+IsCopyFunc(WebGLTexImageFunc func)
+{
+    return (func == WebGLTexImageFunc::CopyTexImage ||
+            func == WebGLTexImageFunc::CopyTexSubImage);
+}
+
+/**
+ * Returns true if func is a SubImage variant.
+ */
+static bool
+IsSubFunc(WebGLTexImageFunc func)
+{
+    return (func == WebGLTexImageFunc::TexSubImage ||
+            func == WebGLTexImageFunc::CopyTexSubImage ||
+            func == WebGLTexImageFunc::CompTexSubImage);
+}
+
+/**
+ * returns true is target is a texture cube map target.
+ */
+static bool
+IsTexImageCubemapTarget(GLenum texImageTarget)
+{
+    return (texImageTarget >= LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X &&
+            texImageTarget <= LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z);
+}
+
 bool
 WebGLContext::ValidateBlendEquationEnum(GLenum mode, const char* info)
 {
     switch (mode) {
     case LOCAL_GL_FUNC_ADD:
     case LOCAL_GL_FUNC_SUBTRACT:
     case LOCAL_GL_FUNC_REVERSE_SUBTRACT:
         return true;
 
     case LOCAL_GL_MIN:
     case LOCAL_GL_MAX:
-        if (IsWebGL2() ||
-            IsExtensionEnabled(WebGLExtensionID::EXT_blend_minmax))
-        {
+        if (IsExtensionEnabled(WebGLExtensionID::EXT_blend_minmax))
             return true;
-        }
 
         break;
 
     default:
         break;
     }
 
     ErrorInvalidEnumInfo(info, mode);
@@ -258,20 +368,23 @@ WebGLContext::ValidateDrawModeEnum(GLenu
         return true;
 
     default:
         ErrorInvalidEnumInfo(info, mode);
         return false;
     }
 }
 
+/**
+ * Return true if the framebuffer attachment is valid. Attachment must
+ * be one of depth/stencil/depth_stencil/color attachment.
+ */
 bool
 WebGLContext::ValidateFramebufferAttachment(const WebGLFramebuffer* fb, GLenum attachment,
-                                            const char* funcName,
-                                            bool badColorAttachmentIsInvalidOp)
+                                            const char* funcName)
 {
     if (!fb) {
         switch (attachment) {
         case LOCAL_GL_COLOR:
         case LOCAL_GL_DEPTH:
         case LOCAL_GL_STENCIL:
             return true;
 
@@ -284,31 +397,21 @@ WebGLContext::ValidateFramebufferAttachm
 
     if (attachment == LOCAL_GL_DEPTH_ATTACHMENT ||
         attachment == LOCAL_GL_STENCIL_ATTACHMENT ||
         attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT)
     {
         return true;
     }
 
-    if (attachment >= LOCAL_GL_COLOR_ATTACHMENT0 &&
-        attachment <= LastColorAttachmentEnum())
-    {
+    if (attachment >= LOCAL_GL_COLOR_ATTACHMENT0 && attachment <= LastColorAttachment())
         return true;
-    }
 
-    if (badColorAttachmentIsInvalidOp &&
-        attachment >= LOCAL_GL_COLOR_ATTACHMENT0)
-    {
-        const uint32_t offset = attachment - LOCAL_GL_COLOR_ATTACHMENT0;
-        ErrorInvalidOperation("%s: Bad color attachment: COLOR_ATTACHMENT%u. (0x%04x)",
-                              funcName, offset, attachment);
-    } else {
-        ErrorInvalidEnum("%s: attachment: Bad attachment 0x%x.", funcName, attachment);
-    }
+    ErrorInvalidEnum("%s: attachment: invalid enum value 0x%x.", funcName,
+                     attachment);
     return false;
 }
 
 /**
  * Return true if pname is valid for GetSamplerParameter calls.
  */
 bool
 WebGLContext::ValidateSamplerParameterName(GLenum pname, const char* info)
@@ -413,16 +516,917 @@ WebGLContext::ValidateSamplerParameterPa
         }
 
     default:
         ErrorInvalidEnum("%s: invalid pname: %s", info, EnumName(pname));
         return false;
     }
 }
 
+
+/**
+ * Return true if format is a valid texture image format for source,
+ * taking into account enabled WebGL extensions.
+ */
+bool
+WebGLContext::ValidateTexImageFormat(GLenum format, WebGLTexImageFunc func,
+                                     WebGLTexDimensions dims)
+{
+    /* Core WebGL texture formats */
+    if (format == LOCAL_GL_ALPHA ||
+        format == LOCAL_GL_RGB ||
+        format == LOCAL_GL_RGBA ||
+        format == LOCAL_GL_LUMINANCE ||
+        format == LOCAL_GL_LUMINANCE_ALPHA)
+    {
+        return true;
+    }
+
+    /* WebGL2 new formats */
+    if (format == LOCAL_GL_RED ||
+        format == LOCAL_GL_RG ||
+        format == LOCAL_GL_RED_INTEGER ||
+        format == LOCAL_GL_RG_INTEGER ||
+        format == LOCAL_GL_RGB_INTEGER ||
+        format == LOCAL_GL_RGBA_INTEGER)
+    {
+        if (IsWebGL2())
+            return true;
+
+        ErrorInvalidEnum("%s: Invalid format %s: Requires WebGL version 2.0 or"
+                         " newer.", InfoFrom(func, dims), EnumName(format));
+        return false;
+    }
+
+    /* WEBGL_depth_texture added formats */
+    if (format == LOCAL_GL_DEPTH_COMPONENT ||
+        format == LOCAL_GL_DEPTH_STENCIL)
+    {
+        if (!IsExtensionEnabled(WebGLExtensionID::WEBGL_depth_texture)) {
+            ErrorInvalidEnum("%s: Invalid format %s: Requires that"
+                             " WEBGL_depth_texture is enabled.",
+                             InfoFrom(func, dims), EnumName(format));
+            return false;
+        }
+
+        // If WEBGL_depth_texture is enabled, then it is not allowed to be used
+        // with the copyTexImage, or copyTexSubImage methods, and it is not
+        // allowed with texSubImage in WebGL1.
+        if ((func == WebGLTexImageFunc::TexSubImage && !IsWebGL2()) ||
+            func == WebGLTexImageFunc::CopyTexImage ||
+            func == WebGLTexImageFunc::CopyTexSubImage)
+        {
+            ErrorInvalidOperation("%s: format %s is not supported",
+                                  InfoFrom(func, dims), EnumName(format));
+            return false;
+        }
+
+        return true;
+    }
+
+    // Needs to be below the depth_texture check because an invalid operation
+    // error needs to be generated instead of invalid enum.
+    // Only core formats are valid for CopyTex[Sub]Image.
+    // TODO: Revisit this once color_buffer_[half_]float lands.
+    if (IsCopyFunc(func)) {
+        ErrorInvalidEnumWithName(this, "invalid format", format, func, dims);
+        return false;
+    }
+
+    // EXT_sRGB added formats
+    if (format == LOCAL_GL_SRGB ||
+        format == LOCAL_GL_SRGB_ALPHA)
+    {
+        if (IsExtensionEnabled(WebGLExtensionID::EXT_sRGB))
+            return true;
+
+        ErrorInvalidEnum("%s: Invalid format %s: Requires that EXT_sRGB is"
+                         " enabled.", InfoFrom(func, dims),
+                         WebGLContext::EnumName(format));
+        return false;
+    }
+
+    ErrorInvalidEnumWithName(this, "invalid format", format, func, dims);
+    return false;
+}
+
+/**
+ * Return true if type is a valid texture image type for source,
+ * taking into account enabled WebGL extensions.
+ */
+bool
+WebGLContext::ValidateTexImageType(GLenum type, WebGLTexImageFunc func,
+                                   WebGLTexDimensions dims)
+{
+    /* Core WebGL texture types */
+    if (type == LOCAL_GL_UNSIGNED_BYTE ||
+        type == LOCAL_GL_UNSIGNED_SHORT_5_6_5 ||
+        type == LOCAL_GL_UNSIGNED_SHORT_4_4_4_4 ||
+        type == LOCAL_GL_UNSIGNED_SHORT_5_5_5_1)
+    {
+        return true;
+    }
+
+    /* WebGL2 new types */
+    if (type == LOCAL_GL_BYTE ||
+        type == LOCAL_GL_SHORT ||
+        type == LOCAL_GL_INT ||
+        type == LOCAL_GL_FLOAT_32_UNSIGNED_INT_24_8_REV ||
+        type == LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV ||
+        type == LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV ||
+        type == LOCAL_GL_UNSIGNED_INT_5_9_9_9_REV)
+    {
+        if (IsWebGL2())
+            return true;
+
+        ErrorInvalidEnum("%s: Invalid type %s: Requires WebGL version 2.0 or"
+                         " newer.", InfoFrom(func, dims),
+                         WebGLContext::EnumName(type));
+        return false;
+    }
+
+    /* OES_texture_float added types */
+    if (type == LOCAL_GL_FLOAT) {
+        if (IsExtensionEnabled(WebGLExtensionID::OES_texture_float))
+            return true;
+
+        ErrorInvalidEnum("%s: Invalid type %s: Requires that OES_texture_float"
+                         " is enabled.",
+                         InfoFrom(func, dims), WebGLContext::EnumName(type));
+        return false;
+    }
+
+    /* OES_texture_half_float add types */
+    if (type == LOCAL_GL_HALF_FLOAT) {
+        if (IsExtensionEnabled(WebGLExtensionID::OES_texture_half_float))
+            return true;
+
+        ErrorInvalidEnum("%s: Invalid type %s: Requires that"
+                         " OES_texture_half_float is enabled.",
+                         InfoFrom(func, dims), WebGLContext::EnumName(type));
+        return false;
+    }
+
+    /* WEBGL_depth_texture added types */
+    if (type == LOCAL_GL_UNSIGNED_SHORT ||
+        type == LOCAL_GL_UNSIGNED_INT ||
+        type == LOCAL_GL_UNSIGNED_INT_24_8)
+    {
+        if (IsExtensionEnabled(WebGLExtensionID::WEBGL_depth_texture))
+            return true;
+
+        ErrorInvalidEnum("%s: Invalid type %s: Requires that"
+                         " WEBGL_depth_texture is enabled.",
+                         InfoFrom(func, dims), WebGLContext::EnumName(type));
+        return false;
+    }
+
+    ErrorInvalidEnumWithName(this, "invalid type", type, func, dims);
+    return false;
+}
+
+/**
+ * Validate texture image sizing extra constraints for
+ * CompressedTex(Sub)?Image.
+ */
+// TODO: WebGL 2
+bool
+WebGLContext::ValidateCompTexImageSize(GLint level, GLenum format,
+                                       GLint xoffset, GLint yoffset,
+                                       GLsizei width, GLsizei height,
+                                       GLsizei levelWidth, GLsizei levelHeight,
+                                       WebGLTexImageFunc func,
+                                       WebGLTexDimensions dims)
+{
+    // Negative parameters must already have been handled above
+    MOZ_ASSERT(xoffset >= 0 && yoffset >= 0 &&
+               width >= 0 && height >= 0);
+
+    if (xoffset + width > (GLint) levelWidth) {
+        ErrorInvalidValue("%s: xoffset + width must be <= levelWidth.",
+                          InfoFrom(func, dims));
+        return false;
+    }
+
+    if (yoffset + height > (GLint) levelHeight) {
+        ErrorInvalidValue("%s: yoffset + height must be <= levelHeight.",
+                          InfoFrom(func, dims));
+        return false;
+    }
+
+    GLint blockWidth = 1;
+    GLint blockHeight = 1;
+    BlockSizeFor(format, &blockWidth, &blockHeight);
+
+    // If blockWidth || blockHeight != 1, then the compressed format had
+    // block-based constraints to be checked. (For example, PVRTC is compressed
+    // but isn't a block-based format)
+    if (blockWidth != 1 || blockHeight != 1) {
+        // Offsets must be multiple of block size.
+        if (xoffset % blockWidth != 0) {
+            ErrorInvalidOperation("%s: xoffset must be multiple of %d.",
+                                  InfoFrom(func, dims), blockWidth);
+            return false;
+        }
+
+        if (yoffset % blockHeight != 0) {
+            ErrorInvalidOperation("%s: yoffset must be multiple of %d.",
+                                  InfoFrom(func, dims), blockHeight);
+            return false;
+        }
+
+        /* The size must be a multiple of blockWidth and blockHeight, or must be
+         * using offset+size that exactly hits the edge. Important for small
+         * mipmap levels.
+         *
+         * From the WEBGL_compressed_texture_s3tc spec:
+         *     When level equals zero width and height must be a multiple of 4.
+         *     When level is greater than 0 width and height must be 0, 1, 2 or
+         *     a multiple of 4. If they are not an INVALID_OPERATION error is
+         *     generated."
+         */
+        if (level == 0) {
+            if (width % blockWidth != 0) {
+                ErrorInvalidOperation("%s: Width of level 0 must be a multiple"
+                                      " of %d.", InfoFrom(func, dims),
+                                      blockWidth);
+                return false;
+            }
+
+            if (height % blockHeight != 0) {
+                ErrorInvalidOperation("%s: Height of level 0 must be a multiple"
+                                      " of %d.", InfoFrom(func, dims),
+                                      blockHeight);
+                return false;
+            }
+        } else if (level > 0) {
+            if (width % blockWidth != 0 && width > 2) {
+                ErrorInvalidOperation("%s: Width of level %d must be a multiple"
+                                      " of %d, or be 0, 1, or 2.",
+                                      InfoFrom(func, dims), level, blockWidth);
+                return false;
+            }
+
+            if (height % blockHeight != 0 && height > 2) {
+                ErrorInvalidOperation("%s: Height of level %d must be a"
+                                      " multiple of %d, or be 0, 1, or 2.",
+                                      InfoFrom(func, dims), level, blockHeight);
+                return false;
+            }
+        }
+
+        if (IsSubFunc(func)) {
+            if ((xoffset % blockWidth) != 0) {
+                ErrorInvalidOperation("%s: xoffset must be a multiple of %d.",
+                                      InfoFrom(func, dims), blockWidth);
+                return false;
+            }
+
+            if (yoffset % blockHeight != 0) {
+                ErrorInvalidOperation("%s: yoffset must be a multiple of %d.",
+                                      InfoFrom(func, dims), blockHeight);
+                return false;
+            }
+        }
+    }
+
+    switch (format) {
+    case LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1:
+    case LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1:
+    case LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1:
+    case LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1:
+        if (!IsPOTAssumingNonnegative(width) ||
+            !IsPOTAssumingNonnegative(height))
+        {
+            ErrorInvalidValue("%s: Width and height must be powers of two.",
+                              InfoFrom(func, dims));
+            return false;
+        }
+    }
+
+    return true;
+}
+
+/**
+ * Return true if the enough data is present to satisfy compressed
+ * texture format constraints.
+ */
+bool
+WebGLContext::ValidateCompTexImageDataSize(GLint level, GLenum format,
+                                           GLsizei width, GLsizei height,
+                                           uint32_t byteLength,
+                                           WebGLTexImageFunc func,
+                                           WebGLTexDimensions dims)
+{
+    // negative width and height must already have been handled above
+    MOZ_ASSERT(width >= 0 && height >= 0);
+
+    CheckedUint32 required_byteLength = 0;
+
+    switch (format) {
+    case LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
+    case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
+    case LOCAL_GL_ATC_RGB:
+    case LOCAL_GL_ETC1_RGB8_OES:
+        required_byteLength = ((CheckedUint32(width) + 3) / 4) *
+                              ((CheckedUint32(height) + 3) / 4) * 8;
+        break;
+
+    case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
+    case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
+    case LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA:
+    case LOCAL_GL_ATC_RGBA_INTERPOLATED_ALPHA:
+        required_byteLength = ((CheckedUint32(width) + 3) / 4) *
+                              ((CheckedUint32(height) + 3) / 4) * 16;
+        break;
+
+    case LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1:
+    case LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1:
+        required_byteLength = CheckedUint32(std::max(width, 8)) *
+                              CheckedUint32(std::max(height, 8)) / 2;
+        break;
+
+    case LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1:
+    case LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1:
+        required_byteLength = CheckedUint32(std::max(width, 16)) *
+                              CheckedUint32(std::max(height, 8)) / 4;
+        break;
+    }
+
+    if (!required_byteLength.isValid() ||
+        required_byteLength.value() != byteLength)
+    {
+        ErrorInvalidValue("%s: Data size does not match dimensions.",
+                          InfoFrom(func, dims));
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * Validate the width, height, and depth of a texture image, \return
+ * true is valid, false otherwise.
+ * Used by all the (Compressed|Copy)?Tex(Sub)?Image functions.
+ * Target and level must have been validated before calling.
+ */
+bool
+WebGLContext::ValidateTexImageSize(TexImageTarget texImageTarget, GLint level,
+                                   GLint width, GLint height, GLint depth,
+                                   WebGLTexImageFunc func,
+                                   WebGLTexDimensions dims)
+{
+    MOZ_ASSERT(level >= 0, "level should already be validated");
+
+    /* Bug 966630: maxTextureSize >> level runs into "undefined"
+     * behaviour depending on ISA. For example, on Intel shifts
+     * amounts are mod 64 (in 64-bit mode on 64-bit dest) and mod 32
+     * otherwise. This means 16384 >> 0x10000001 == 8192 which isn't
+     * what would be expected. Make the required behaviour explicit by
+     * clamping to a shift of 31 bits if level is greater than that
+     * ammount. This will give 0 that if (!maxAllowedSize) is
+     * expecting.
+     */
+
+    if (level > 31)
+        level = 31;
+
+    auto texTarget = TexImageTargetToTexTarget(texImageTarget);
+    const GLuint maxTexImageSize = MaxTextureSizeForTarget(texTarget) >> level;
+
+    const bool isCubemapTarget = IsTexImageCubemapTarget(texImageTarget.get());
+    const bool isSub = IsSubFunc(func);
+
+    if (!isSub && isCubemapTarget && (width != height)) {
+        /* GL ES Version 2.0.25 - 3.7.1 Texture Image Specification
+         *   "When the target parameter to TexImage2D is one of the
+         *   six cube map two-dimensional image targets, the error
+         *   INVALID_VALUE is generated if the width and height
+         *   parameters are not equal."
+         */
+        ErrorInvalidValue("%s: For cube maps, width must equal height.",
+                          InfoFrom(func, dims));
+        return false;
+    }
+
+    if (texImageTarget == LOCAL_GL_TEXTURE_2D || isCubemapTarget) {
+        /* GL ES Version 2.0.25 - 3.7.1 Texture Image Specification
+         *   "If wt and ht are the specified image width and height,
+         *   and if either wt or ht are less than zero, then the error
+         *   INVALID_VALUE is generated."
+         */
+        if (width < 0) {
+            ErrorInvalidValue("%s: Width must be >= 0.", InfoFrom(func, dims));
+            return false;
+        }
+
+        if (height < 0) {
+            ErrorInvalidValue("%s: Height must be >= 0.", InfoFrom(func, dims));
+            return false;
+        }
+
+        /* GL ES Version 2.0.25 - 3.7.1 Texture Image Specification
+         *   "The maximum allowable width and height of a
+         *   two-dimensional texture image must be at least 2**(k−lod)
+         *   for image arrays of level zero through k, where k is the
+         *   log base 2 of MAX_TEXTURE_SIZE. and lod is the
+         *   level-of-detail of the image array. It may be zero for
+         *   image arrays of any level-of-detail greater than k. The
+         *   error INVALID_VALUE is generated if the specified image
+         *   is too large to be stored under any conditions.
+         */
+        if (width > (int) maxTexImageSize) {
+            ErrorInvalidValue("%s: The maximum width for level %d is %u.",
+                              InfoFrom(func, dims), level, maxTexImageSize);
+            return false;
+        }
+
+        if (height > (int) maxTexImageSize) {
+            ErrorInvalidValue("%s: The maximum height for level %d is %u.",
+                              InfoFrom(func, dims), level, maxTexImageSize);
+            return false;
+        }
+
+        /* GL ES Version 2.0.25 - 3.7.1 Texture Image Specification
+         *   "If level is greater than zero, and either width or
+         *   height is not a power-of-two, the error INVALID_VALUE is
+         *   generated."
+         *
+         * This restriction does not apply to GL ES Version 3.0+.
+         */
+        if (!IsWebGL2() && level > 0) {
+            if (!IsPOTAssumingNonnegative(width)) {
+                ErrorInvalidValue("%s: For level > 0, width of %d must be a"
+                                  " power of two.", InfoFrom(func, dims),
+                                  width);
+                return false;
+            }
+
+            if (!IsPOTAssumingNonnegative(height)) {
+                ErrorInvalidValue("%s: For level > 0, height of %d must be a"
+                                  " power of two.", InfoFrom(func, dims),
+                                  height);
+                return false;
+            }
+        }
+    }
+
+    // TODO: WebGL 2
+    if (texImageTarget == LOCAL_GL_TEXTURE_3D) {
+        if (depth < 0) {
+            ErrorInvalidValue("%s: Depth must be >= 0.", InfoFrom(func, dims));
+            return false;
+        }
+
+        if (!IsWebGL2() && !IsPOTAssumingNonnegative(depth)) {
+            ErrorInvalidValue("%s: Depth of %d must be a power of two.",
+                              InfoFrom(func, dims), depth);
+            return false;
+        }
+    }
+
+    return true;
+}
+
+/**
+ * Validate texture image sizing for Tex(Sub)?Image variants.
+ */
+// TODO: WebGL 2. Update this to handle 3D textures.
+bool
+WebGLContext::ValidateTexSubImageSize(GLint xoffset, GLint yoffset,
+                                      GLint /*zoffset*/, GLsizei width,
+                                      GLsizei height, GLsizei /*depth*/,
+                                      GLsizei baseWidth, GLsizei baseHeight,
+                                      GLsizei /*baseDepth*/,
+                                      WebGLTexImageFunc func,
+                                      WebGLTexDimensions dims)
+{
+    /* GL ES Version 2.0.25 - 3.7.1 Texture Image Specification
+     *   "Taking wt and ht to be the specified width and height of the
+     *   texture array, and taking x, y, w, and h to be the xoffset,
+     *   yoffset, width, and height argument values, any of the
+     *   following relationships generates the error INVALID_VALUE:
+     *       x < 0
+     *       x + w > wt
+     *       y < 0
+     *       y + h > ht"
+     */
+
+    if (xoffset < 0) {
+        ErrorInvalidValue("%s: xoffset must be >= 0.", InfoFrom(func, dims));
+        return false;
+    }
+
+    if (yoffset < 0) {
+        ErrorInvalidValue("%s: yoffset must be >= 0.", InfoFrom(func, dims));
+        return false;
+    }
+
+    if (!CanvasUtils::CheckSaneSubrectSize(xoffset, yoffset, width, height,
+                                           baseWidth, baseHeight))
+    {
+        ErrorInvalidValue("%s: Subtexture rectangle out-of-bounds.",
+                          InfoFrom(func, dims));
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * Perform validation of format/type combinations for TexImage variants.
+ * Returns true if the format/type is a valid combination, false otherwise.
+ */
+bool
+WebGLContext::ValidateTexImageFormatAndType(GLenum format, GLenum type,
+                                            WebGLTexImageFunc func,
+                                            WebGLTexDimensions dims)
+{
+    if (type == LOCAL_GL_HALF_FLOAT_OES) {
+        type = LOCAL_GL_HALF_FLOAT;
+    }
+
+    if (IsCompressedFunc(func) || IsCopyFunc(func)) {
+        MOZ_ASSERT(type == LOCAL_GL_NONE && format == LOCAL_GL_NONE);
+        return true;
+    }
+
+    if (!ValidateTexImageFormat(format, func, dims) ||
+        !ValidateTexImageType(type, func, dims))
+    {
+        return false;
+    }
+
+    // Here we're reinterpreting format as an unsized internalformat;
+    // these are the same in practice and there's no point in having the
+    // same code implemented twice.
+    TexInternalFormat effective =
+        EffectiveInternalFormatFromInternalFormatAndType(format, type);
+
+    if (effective != LOCAL_GL_NONE)
+        return true;
+
+    ErrorInvalidOperation("%s: Invalid combination of format %s and type %s.",
+                          InfoFrom(func, dims), WebGLContext::EnumName(format),
+                          WebGLContext::EnumName(type));
+    return false;
+}
+
+bool
+WebGLContext::ValidateCompTexImageInternalFormat(GLenum format,
+                                                 WebGLTexImageFunc func,
+                                                 WebGLTexDimensions dims)
+{
+    if (!IsCompressedTextureFormat(format)) {
+        ErrorInvalidEnum("%s: Invalid compressed texture format: %s",
+                         InfoFrom(func, dims), WebGLContext::EnumName(format));
+        return false;
+    }
+
+    /* WEBGL_compressed_texture_atc added formats */
+    if (format == LOCAL_GL_ATC_RGB ||
+        format == LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA ||
+        format == LOCAL_GL_ATC_RGBA_INTERPOLATED_ALPHA)
+    {
+        if (IsExtensionEnabled(WebGLExtensionID::WEBGL_compressed_texture_atc))
+            return true;
+
+        ErrorInvalidEnum("%s: Invalid format %s: Requires that"
+                         " WEBGL_compressed_texture_atc is enabled.",
+                         InfoFrom(func, dims), WebGLContext::EnumName(format));
+        return false;
+    }
+
+    // WEBGL_compressed_texture_etc1
+    if (format == LOCAL_GL_ETC1_RGB8_OES) {
+        if (IsExtensionEnabled(WebGLExtensionID::WEBGL_compressed_texture_etc1))
+            return true;
+
+        ErrorInvalidEnum("%s: Invalid format %s: Requires that"
+                         " WEBGL_compressed_texture_etc1 is enabled.",
+                         InfoFrom(func, dims), WebGLContext::EnumName(format));
+        return false;
+    }
+
+
+    if (format == LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1 ||
+        format == LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1 ||
+        format == LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1 ||
+        format == LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1)
+    {
+        if (IsExtensionEnabled(WebGLExtensionID::WEBGL_compressed_texture_pvrtc))
+            return true;
+
+        ErrorInvalidEnum("%s: Invalid format %s: Requires that"
+                         " WEBGL_compressed_texture_pvrtc is enabled.",
+                         InfoFrom(func, dims), WebGLContext::EnumName(format));
+        return false;
+    }
+
+
+    if (format == LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT ||
+        format == LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT ||
+        format == LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT ||
+        format == LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT)
+    {
+        if (IsExtensionEnabled(WebGLExtensionID::WEBGL_compressed_texture_s3tc))
+            return true;
+
+        ErrorInvalidEnum("%s: Invalid format %s: Requires that"
+                         " WEBGL_compressed_texture_s3tc is enabled.",
+                         InfoFrom(func, dims), WebGLContext::EnumName(format));
+        return false;
+    }
+
+    MOZ_ASSERT(false);
+    return false;
+}
+
+bool
+WebGLContext::ValidateCopyTexImageInternalFormat(GLenum format,
+                                                 WebGLTexImageFunc func,
+                                                 WebGLTexDimensions dims)
+{
+    switch (format) {
+    case LOCAL_GL_RGBA:
+    case LOCAL_GL_RGB:
+    case LOCAL_GL_LUMINANCE_ALPHA:
+    case LOCAL_GL_LUMINANCE:
+    case LOCAL_GL_ALPHA:
+        return true;
+    }
+    // In CopyTexImage, internalFormat is a function parameter,
+    // so a bad value is an INVALID_ENUM error.
+    // In CopyTexSubImage, internalFormat is part of existing state,
+    // so this is an INVALID_OPERATION error.
+    GenerateWarning("%s: Invalid texture internal format: %s",
+                    InfoFrom(func, dims), WebGLContext::EnumName(format));
+
+    GLenum error;
+    if (func == WebGLTexImageFunc::CopyTexImage)
+        error = LOCAL_GL_INVALID_ENUM;
+    else
+        error = LOCAL_GL_INVALID_OPERATION;
+
+    SynthesizeGLError(error);
+    return false;
+}
+/**
+ * Return true if format, type and jsArrayType are a valid combination.
+ * Also returns the size for texel of format and type (in bytes) via
+ * \a texelSize.
+ *
+ * It is assumed that type has previously been validated.
+ */
+bool
+WebGLContext::ValidateTexInputData(GLenum type, js::Scalar::Type jsArrayType,
+                                   WebGLTexImageFunc func,
+                                   WebGLTexDimensions dims)
+{
+    // We're using js::Scalar::MaxTypedArrayViewType as dummy value for when
+    // the tex source wasn't a typed array.
+    if (jsArrayType == js::Scalar::MaxTypedArrayViewType)
+        return true;
+
+    const char invalidTypedArray[] = "%s: Invalid typed array type for given"
+                                     " texture data type.";
+
+    bool validInput = false;
+    switch (type) {
+    case LOCAL_GL_UNSIGNED_BYTE:
+        validInput = jsArrayType == js::Scalar::Uint8;
+        break;
+
+    case LOCAL_GL_BYTE:
+        validInput = jsArrayType == js::Scalar::Int8;
+        break;
+
+    case LOCAL_GL_HALF_FLOAT:
+    case LOCAL_GL_UNSIGNED_SHORT:
+    case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
+    case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
+    case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
+        validInput = jsArrayType == js::Scalar::Uint16;
+        break;
+
+    case LOCAL_GL_SHORT:
+        validInput = jsArrayType == js::Scalar::Int16;
+        break;
+
+    case LOCAL_GL_UNSIGNED_INT:
+    case LOCAL_GL_UNSIGNED_INT_24_8:
+    case LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV:
+    case LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV:
+    case LOCAL_GL_UNSIGNED_INT_5_9_9_9_REV:
+        validInput = jsArrayType == js::Scalar::Uint32;
+        break;
+
+    case LOCAL_GL_INT:
+        validInput = jsArrayType == js::Scalar::Int32;
+        break;
+
+    case LOCAL_GL_FLOAT:
+        validInput = jsArrayType == js::Scalar::Float32;
+        break;
+
+    default:
+        break;
+    }
+
+    if (!validInput)
+        ErrorInvalidOperation(invalidTypedArray, InfoFrom(func, dims));
+
+    return validInput;
+}
+
+/**
+ * Checks specific for the CopyTex[Sub]Image2D functions.
+ * Verifies:
+ * - Framebuffer is complete and has valid read planes
+ * - Copy format is a subset of framebuffer format (i.e. all required components
+ *   are available)
+ */
+bool
+WebGLContext::ValidateCopyTexImage(GLenum format, WebGLTexImageFunc func,
+                                   WebGLTexDimensions dims)
+{
+    MOZ_ASSERT(IsCopyFunc(func));
+
+    // Default framebuffer format
+    GLenum fboFormat = mOptions.alpha ? LOCAL_GL_RGBA : LOCAL_GL_RGB;
+
+    if (mBoundReadFramebuffer) {
+        TexInternalFormat srcFormat;
+        if (!mBoundReadFramebuffer->ValidateForRead(InfoFrom(func, dims), &srcFormat))
+            return false;
+
+        fboFormat = srcFormat.get();
+    }
+
+    // Make sure the format of the framebuffer is a superset of the format
+    // requested by the CopyTex[Sub]Image2D functions.
+    const GLComponents formatComps = GLComponents(format);
+    const GLComponents fboComps = GLComponents(fboFormat);
+    if (!formatComps.IsSubsetOf(fboComps)) {
+        ErrorInvalidOperation("%s: Format %s is not a subset of the current"
+                              " framebuffer format, which is %s.",
+                              InfoFrom(func, dims), EnumName(format),
+                              EnumName(fboFormat));
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * Test the gl(Copy|Compressed)?Tex[Sub]?Image[23]() parameters for errors.
+ * Verifies each of the parameters against the WebGL standard and enabled
+ * extensions.
+ */
+// TODO: Texture dims is here for future expansion in WebGL 2.0
+bool
+WebGLContext::ValidateTexImage(TexImageTarget texImageTarget, GLint level,
+                               GLenum internalFormat, GLint xoffset,
+                               GLint yoffset, GLint zoffset, GLint width,
+                               GLint height, GLint depth, GLint border,
+                               GLenum format, GLenum type,
+                               WebGLTexImageFunc func,
+                               WebGLTexDimensions dims)
+{
+    const char* info = InfoFrom(func, dims);
+
+    // Check level
+    if (level < 0) {
+        ErrorInvalidValue("%s: `level` must be >= 0.", info);
+        return false;
+    }
+
+    // Check border
+    if (border != 0) {
+        ErrorInvalidValue("%s: `border` must be 0.", info);
+        return false;
+    }
+
+    // Check incoming image format and type
+    if (!ValidateTexImageFormatAndType(format, type, func, dims))
+        return false;
+
+    if (!TexInternalFormat::IsValueLegal(internalFormat)) {
+        ErrorInvalidEnum("%s: Invalid `internalformat` enum %s.", info,
+                         EnumName(internalFormat));
+        return false;
+    }
+    TexInternalFormat unsizedInternalFormat =
+        UnsizedInternalFormatFromInternalFormat(internalFormat);
+
+    if (IsCompressedFunc(func)) {
+        if (!ValidateCompTexImageInternalFormat(internalFormat, func, dims))
+            return false;
+
+    } else if (IsCopyFunc(func)) {
+        if (!ValidateCopyTexImageInternalFormat(unsizedInternalFormat.get(),
+                                                func, dims))
+        {
+            return false;
+        }
+    } else if (format != unsizedInternalFormat) {
+        if (IsWebGL2()) {
+            // In WebGL2, it's OK to have `internalFormat != format` if
+            // internalFormat is the sized internal format corresponding to the
+            // (format, type) pair according to Table 3.2 in the OpenGL ES 3.0.3
+            // spec.
+            auto effectiveFormat = EffectiveInternalFormatFromInternalFormatAndType(format,
+                                                                                    type);
+            if (internalFormat != effectiveFormat) {
+                bool exceptionallyAllowed = false;
+                if (internalFormat == LOCAL_GL_SRGB8_ALPHA8 &&
+                    format == LOCAL_GL_RGBA &&
+                    type == LOCAL_GL_UNSIGNED_BYTE)
+                {
+                    exceptionallyAllowed = true;
+                }
+                else if (internalFormat == LOCAL_GL_SRGB8 &&
+                         format == LOCAL_GL_RGB &&
+                         type == LOCAL_GL_UNSIGNED_BYTE)
+                {
+                    exceptionallyAllowed = true;
+                }
+                if (!exceptionallyAllowed) {
+                    ErrorInvalidOperation("%s: `internalformat` does not match"
+                                          " `format` and `type`.", info);
+                    return false;
+                }
+            }
+        } else {
+            // In WebGL 1, format must be equal to internalformat.
+            ErrorInvalidOperation("%s: `internalformat` does not match"
+                                  " `format`.", info);
+            return false;
+        }
+    }
+
+    // Check texture image size
+    if (!ValidateTexImageSize(texImageTarget, level, width, height, 0, func,
+                              dims))
+    {
+        return false;
+    }
+
+    /* 5.14.8 Texture objects - WebGL Spec.
+     *   "If an attempt is made to call these functions with no
+     *    WebGLTexture bound (see above), an INVALID_OPERATION error
+     *    is generated."
+     */
+    WebGLTexture* tex = ActiveBoundTextureForTexImageTarget(texImageTarget);
+    if (!tex) {
+        ErrorInvalidOperation("%s: No texture is bound to target %s.", info,
+                              WebGLContext::EnumName(texImageTarget.get()));
+        return false;
+    }
+
+    if (IsSubFunc(func)) {
+        if (!tex->HasImageInfoAt(texImageTarget, level)) {
+            ErrorInvalidOperation("%s: No texture image previously defined for"
+                                  " target %s at level %d.", info,
+                                  WebGLContext::EnumName(texImageTarget.get()),
+                                                         level);
+            return false;
+        }
+
+        const auto& imageInfo = tex->ImageInfoAt(texImageTarget, level);
+        if (!ValidateTexSubImageSize(xoffset, yoffset, zoffset, width, height,
+                                     depth, imageInfo.Width(),
+                                     imageInfo.Height(), 0, func, dims))
+        {
+            return false;
+        }
+    }
+
+    // Additional checks for depth textures
+    if (texImageTarget != LOCAL_GL_TEXTURE_2D &&
+        (format == LOCAL_GL_DEPTH_COMPONENT ||
+         format == LOCAL_GL_DEPTH_STENCIL))
+    {
+        ErrorInvalidOperation("%s: With format of %s, target must be"
+                              " TEXTURE_2D.", info,
+                              WebGLContext::EnumName(format));
+        return false;
+    }
+
+    // Additional checks for compressed textures
+    if (!IsAllowedFromSource(internalFormat, func)) {
+        ErrorInvalidOperation("%s: Invalid format %s for this operation.",
+                              info, WebGLContext::EnumName(format));
+        return false;
+    }
+
+    // Parameters are OK
+    return true;
+}
+
 bool
 WebGLContext::ValidateUniformLocation(WebGLUniformLocation* loc, const char* funcName)
 {
     /* GLES 2.0.25, p38:
      *   If the value of location is -1, the Uniform* commands will silently
      *   ignore the data passed in, and the current uniform values will not be
      *   changed.
      */
@@ -568,17 +1572,17 @@ WebGLContext::ValidateAttribPointer(bool
         return false;
     }
 
     GLsizei requiredAlignment = 0;
     if (!ValidateAttribPointerType(integerMode, type, &requiredAlignment, info))
         return false;
 
     // requiredAlignment should always be a power of two
-    MOZ_ASSERT(IsPowerOfTwo(requiredAlignment));
+    MOZ_ASSERT(IsPOTAssumingNonnegative(requiredAlignment));
     GLsizei requiredAlignmentMask = requiredAlignment - 1;
 
     if (size < 1 || size > 4) {
         ErrorInvalidValue("%s: invalid element size", info);
         return false;
     }
 
     // see WebGL spec section 6.6 "Vertex Attribute Data Stride"
@@ -648,17 +1652,17 @@ bool
 WebGLContext::InitAndValidateGL()
 {
     if (!gl)
         return false;
 
     // Unconditionally create a new format usage authority. This is
     // important when restoring contexts and extensions need to add
     // formats back into the authority.
-    mFormatUsage = CreateFormatUsage(gl);
+    mFormatUsage = CreateFormatUsage();
 
     GLenum error = gl->fGetError();
     if (error != LOCAL_GL_NO_ERROR) {
         GenerateWarning("GL error 0x%x occurred during OpenGL context"
                         " initialization, before WebGL initialization!", error);
         return false;
     }
 
@@ -710,26 +1714,23 @@ WebGLContext::InitAndValidateGL()
     AssertUintParamCorrect(gl, LOCAL_GL_STENCIL_BACK_WRITEMASK,  mStencilWriteMaskBack);
 
     mDitherEnabled = true;
     mRasterizerDiscardEnabled = false;
     mScissorTestEnabled = false;
 
     // Bindings, etc.
     mActiveTexture = 0;
-    mDefaultFB_DrawBuffer0 = LOCAL_GL_BACK;
-
     mEmitContextLostErrorOnce = true;
     mWebGLError = LOCAL_GL_NO_ERROR;
     mUnderlyingGLError = LOCAL_GL_NO_ERROR;
 
     mBound2DTextures.Clear();
     mBoundCubeMapTextures.Clear();
     mBound3DTextures.Clear();
-    mBound2DArrayTextures.Clear();
     mBoundSamplers.Clear();
 
     mBoundArrayBuffer = nullptr;
     mBoundTransformFeedbackBuffer = nullptr;
     mCurrentProgram = nullptr;
 
     mBoundDrawFramebuffer = nullptr;
     mBoundReadFramebuffer = nullptr;
@@ -764,88 +1765,51 @@ WebGLContext::InitAndValidateGL()
         GenerateWarning("GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: %d is < 8!",
                         mGLMaxTextureUnits);
         return false;
     }
 
     mBound2DTextures.SetLength(mGLMaxTextureUnits);
     mBoundCubeMapTextures.SetLength(mGLMaxTextureUnits);
     mBound3DTextures.SetLength(mGLMaxTextureUnits);
-    mBound2DArrayTextures.SetLength(mGLMaxTextureUnits);
     mBoundSamplers.SetLength(mGLMaxTextureUnits);
 
-    ////////////////
-
     if (MinCapabilityMode()) {
-        mImplMaxTextureSize = MINVALUE_GL_MAX_TEXTURE_SIZE;
-        mImplMaxCubeMapTextureSize = MINVALUE_GL_MAX_CUBE_MAP_TEXTURE_SIZE;
-        mImplMaxRenderbufferSize = MINVALUE_GL_MAX_RENDERBUFFER_SIZE;
-
-        mImplMax3DTextureSize = MINVALUE_GL_MAX_3D_TEXTURE_SIZE;
-        mImplMaxArrayTextureLayers = MINVALUE_GL_MAX_ARRAY_TEXTURE_LAYERS;
-
+        mGLMaxTextureSize = MINVALUE_GL_MAX_TEXTURE_SIZE;
+        mGLMaxCubeMapTextureSize = MINVALUE_GL_MAX_CUBE_MAP_TEXTURE_SIZE;
+        mGLMaxRenderbufferSize = MINVALUE_GL_MAX_RENDERBUFFER_SIZE;
         mGLMaxTextureImageUnits = MINVALUE_GL_MAX_TEXTURE_IMAGE_UNITS;
         mGLMaxVertexTextureImageUnits = MINVALUE_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS;
-
         mGLMaxSamples = 1;
     } else {
-        gl->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, (GLint*)&mImplMaxTextureSize);
-        gl->fGetIntegerv(LOCAL_GL_MAX_CUBE_MAP_TEXTURE_SIZE, (GLint*)&mImplMaxCubeMapTextureSize);
-        gl->fGetIntegerv(LOCAL_GL_MAX_RENDERBUFFER_SIZE, (GLint*)&mImplMaxRenderbufferSize);
-
-        if (!gl->GetPotentialInteger(LOCAL_GL_MAX_3D_TEXTURE_SIZE, (GLint*)&mImplMax3DTextureSize))
-            mImplMax3DTextureSize = 0;
-        if (!gl->GetPotentialInteger(LOCAL_GL_MAX_ARRAY_TEXTURE_LAYERS, (GLint*)&mImplMaxArrayTextureLayers))
-            mImplMaxArrayTextureLayers = 0;
-
+        gl->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, &mGLMaxTextureSize);
+        gl->fGetIntegerv(LOCAL_GL_MAX_CUBE_MAP_TEXTURE_SIZE, &mGLMaxCubeMapTextureSize);
+        gl->fGetIntegerv(LOCAL_GL_MAX_RENDERBUFFER_SIZE, &mGLMaxRenderbufferSize);
         gl->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_IMAGE_UNITS, &mGLMaxTextureImageUnits);
         gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &mGLMaxVertexTextureImageUnits);
 
         if (!gl->GetPotentialInteger(LOCAL_GL_MAX_SAMPLES, (GLint*)&mGLMaxSamples))
             mGLMaxSamples = 1;
     }
 
-    // If we don't support a target, its max size is 0. We should only floor-to-POT if the
-    // value if it's non-zero. (NB log2(0) is -Inf, so zero isn't an integer power-of-two)
-    const auto fnFloorPOTIfSupported = [](uint32_t& val) {
-        if (val) {
-            val = FloorPOT(val);
-        }
-    };
-
-    fnFloorPOTIfSupported(mImplMaxTextureSize);
-    fnFloorPOTIfSupported(mImplMaxCubeMapTextureSize);
-    fnFloorPOTIfSupported(mImplMaxRenderbufferSize);
-
-    fnFloorPOTIfSupported(mImplMax3DTextureSize);
-    fnFloorPOTIfSupported(mImplMaxArrayTextureLayers);
-
-    ////////////////
-
-    mGLMaxColorAttachments = 1;
-    mGLMaxDrawBuffers = 1;
-    gl->GetPotentialInteger(LOCAL_GL_MAX_COLOR_ATTACHMENTS,
-                            (GLint*)&mGLMaxColorAttachments);
-    gl->GetPotentialInteger(LOCAL_GL_MAX_DRAW_BUFFERS, (GLint*)&mGLMaxDrawBuffers);
-
-    if (MinCapabilityMode()) {
-        mGLMaxColorAttachments = std::min(mGLMaxColorAttachments,
-                                          kMinMaxColorAttachments);
-        mGLMaxDrawBuffers = std::min(mGLMaxDrawBuffers, kMinMaxDrawBuffers);
+    // Calculate log2 of mGLMaxTextureSize and mGLMaxCubeMapTextureSize
+    mGLMaxTextureSizeLog2 = 0;
+    int32_t tempSize = mGLMaxTextureSize;
+    while (tempSize >>= 1) {
+        ++mGLMaxTextureSizeLog2;
     }
 
-    if (IsWebGL2()) {
-        mImplMaxColorAttachments = mGLMaxColorAttachments;
-        mImplMaxDrawBuffers = std::min(mGLMaxDrawBuffers, mImplMaxColorAttachments);
-    } else {
-        mImplMaxColorAttachments = 1;
-        mImplMaxDrawBuffers = 1;
+    mGLMaxCubeMapTextureSizeLog2 = 0;
+    tempSize = mGLMaxCubeMapTextureSize;
+    while (tempSize >>= 1) {
+        ++mGLMaxCubeMapTextureSizeLog2;
     }
 
-    ////////////////
+    mGLMaxTextureSize = FloorPOT(mGLMaxTextureSize);
+    mGLMaxRenderbufferSize = FloorPOT(mGLMaxRenderbufferSize);
 
     if (MinCapabilityMode()) {
         mGLMaxFragmentUniformVectors = MINVALUE_GL_MAX_FRAGMENT_UNIFORM_VECTORS;
         mGLMaxVertexUniformVectors = MINVALUE_GL_MAX_VERTEX_UNIFORM_VECTORS;
         mGLMaxVaryingVectors = MINVALUE_GL_MAX_VARYING_VECTORS;
     } else {
         if (gl->IsSupported(gl::GLFeature::ES2_compatibility)) {
             gl->fGetIntegerv(LOCAL_GL_MAX_FRAGMENT_UNIFORM_VECTORS, &mGLMaxFragmentUniformVectors);
@@ -966,32 +1930,16 @@ WebGLContext::InitAndValidateGL()
     // vertex array object (the name zero) is also deprecated. [...]"
 
     if (gl->IsCoreProfile()) {
         MakeContextCurrent();
         mDefaultVertexArray->GenVertexArray();
         mDefaultVertexArray->BindVertexArray();
     }
 
-    mPixelStore_FlipY = false;
-    mPixelStore_PremultiplyAlpha = false;
-    mPixelStore_ColorspaceConversion = BROWSER_DEFAULT_WEBGL;
-
-    // GLES 3.0.4, p259:
-    mPixelStore_UnpackImageHeight = 0;
-    mPixelStore_UnpackSkipImages = 0;
-    mPixelStore_UnpackRowLength = 0;
-    mPixelStore_UnpackSkipRows = 0;
-    mPixelStore_UnpackSkipPixels = 0;
-    mPixelStore_UnpackAlignment = 4;
-    mPixelStore_PackRowLength = 0;
-    mPixelStore_PackSkipRows = 0;
-    mPixelStore_PackSkipPixels = 0;
-    mPixelStore_PackAlignment = 4;
-
     return true;
 }
 
 bool
 WebGLContext::ValidateFramebufferTarget(GLenum target,
                                         const char* const info)
 {
     bool isValid = true;
--- a/dom/canvas/WebGLContextVertices.cpp
+++ b/dom/canvas/WebGLContextVertices.cpp
@@ -353,21 +353,18 @@ WebGLContext::GetVertexAttrib(JSContext*
 
     case LOCAL_GL_VERTEX_ATTRIB_ARRAY_TYPE:
         if (!mBoundVertexArray->mAttribs[index].enabled)
             return JS::NumberValue(uint32_t(LOCAL_GL_FLOAT));
 
         return JS::NumberValue(uint32_t(mBoundVertexArray->mAttribs[index].type));
 
     case LOCAL_GL_VERTEX_ATTRIB_ARRAY_DIVISOR:
-        if (IsWebGL2() ||
-            IsExtensionEnabled(WebGLExtensionID::ANGLE_instanced_arrays))
-        {
+        if (IsExtensionEnabled(WebGLExtensionID::ANGLE_instanced_arrays))
             return JS::Int32Value(mBoundVertexArray->mAttribs[index].divisor);
-        }
         break;
 
     case LOCAL_GL_CURRENT_VERTEX_ATTRIB:
         {
             JS::RootedObject obj(cx);
             switch (mVertexAttribType[index]) {
             case LOCAL_GL_FLOAT:
                 obj = GetVertexAttribFloat32Array(cx, index);
--- a/dom/canvas/WebGLExtensionColorBufferFloat.cpp
+++ b/dom/canvas/WebGLExtensionColorBufferFloat.cpp
@@ -4,41 +4,42 @@
 
 #include "WebGLExtensions.h"
 
 #include "GLContext.h"
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "WebGLContext.h"
 #include "WebGLFormats.h"
 
-#ifdef FOO
-#error FOO is already defined! We use FOO() macros to keep things succinct in this file.
-#endif
+namespace mozilla {
 
-namespace mozilla {
+using mozilla::webgl::EffectiveFormat;
 
 WebGLExtensionColorBufferFloat::WebGLExtensionColorBufferFloat(WebGLContext* webgl)
     : WebGLExtensionBase(webgl)
 {
     MOZ_ASSERT(IsSupported(webgl), "Don't construct extension if unsupported.");
 
-    auto& fua = webgl->mFormatUsage;
+    webgl::FormatUsageAuthority* authority = webgl->mFormatUsage.get();
 
-    auto fnUpdateUsage = [&fua](GLenum sizedFormat, webgl::EffectiveFormat effFormat) {
-        auto usage = fua->EditUsage(effFormat);
-        usage->isRenderable = true;
-        fua->AllowRBFormat(sizedFormat, usage);
+    auto updateUsage = [authority](EffectiveFormat effectiveFormat) {
+        webgl::FormatUsageInfo* usage = authority->GetUsage(effectiveFormat);
+        MOZ_ASSERT(usage);
+        usage->asRenderbuffer = usage->isRenderable = true;
     };
 
-#define FOO(x) fnUpdateUsage(LOCAL_GL_ ## x, webgl::EffectiveFormat::x)
+    // Ensure require formats are initialized.
+    WebGLExtensionTextureFloat::InitWebGLFormats(authority);
 
-    // The extension doesn't actually add RGB32F; only RGBA32F.
-    FOO(RGBA32F);
-
-#undef FOO
+    // Update usage to allow asRenderbuffer and isRenderable
+    updateUsage(EffectiveFormat::RGBA32F);
+    updateUsage(EffectiveFormat::RGB32F);
+    updateUsage(EffectiveFormat::Luminance32FAlpha32F);
+    updateUsage(EffectiveFormat::Luminance32F);
+    updateUsage(EffectiveFormat::Alpha32F);
 }
 
 WebGLExtensionColorBufferFloat::~WebGLExtensionColorBufferFloat()
 {
 }
 
 bool
 WebGLExtensionColorBufferFloat::IsSupported(const WebGLContext* webgl)
--- a/dom/canvas/WebGLExtensionColorBufferHalfFloat.cpp
+++ b/dom/canvas/WebGLExtensionColorBufferHalfFloat.cpp
@@ -4,41 +4,42 @@
 
 #include "WebGLExtensions.h"
 
 #include "GLContext.h"
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "WebGLContext.h"
 #include "WebGLFormats.h"
 
-#ifdef FOO
-#error FOO is already defined! We use FOO() macros to keep things succinct in this file.
-#endif
+namespace mozilla {
 
-namespace mozilla {
+using mozilla::webgl::EffectiveFormat;
 
 WebGLExtensionColorBufferHalfFloat::WebGLExtensionColorBufferHalfFloat(WebGLContext* webgl)
     : WebGLExtensionBase(webgl)
 {
     MOZ_ASSERT(IsSupported(webgl), "Don't construct extension if unsupported.");
 
-    auto& fua = webgl->mFormatUsage;
+    webgl::FormatUsageAuthority* authority = webgl->mFormatUsage.get();
 
-    auto fnUpdateUsage = [&fua](GLenum sizedFormat, webgl::EffectiveFormat effFormat) {
-        auto usage = fua->EditUsage(effFormat);
-        usage->isRenderable = true;
-        fua->AllowRBFormat(sizedFormat, usage);
+    auto updateUsage = [authority](EffectiveFormat effectiveFormat) {
+        webgl::FormatUsageInfo* usage = authority->GetUsage(effectiveFormat);
+        MOZ_ASSERT(usage);
+        usage->asRenderbuffer = usage->isRenderable = true;
     };
 
-#define FOO(x) fnUpdateUsage(LOCAL_GL_ ## x, webgl::EffectiveFormat::x)
+    // Ensure require formats are initialized.
+    WebGLExtensionTextureHalfFloat::InitWebGLFormats(authority);
 
-    FOO(RGBA16F);
-    FOO(RGB16F);
-
-#undef FOO
+    // Update usage to allow asRenderbuffer and isRenderable
+    updateUsage(EffectiveFormat::RGBA16F);
+    updateUsage(EffectiveFormat::RGB16F);
+    updateUsage(EffectiveFormat::Luminance16FAlpha16F);
+    updateUsage(EffectiveFormat::Luminance16F);
+    updateUsage(EffectiveFormat::Alpha16F);
 }
 
 WebGLExtensionColorBufferHalfFloat::~WebGLExtensionColorBufferHalfFloat()
 {
 }
 
 bool
 WebGLExtensionColorBufferHalfFloat::IsSupported(const WebGLContext* webgl)
--- a/dom/canvas/WebGLExtensionCompressedTextureATC.cpp
+++ b/dom/canvas/WebGLExtensionCompressedTextureATC.cpp
@@ -2,43 +2,24 @@
  * 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 "WebGLExtensions.h"
 
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "WebGLContext.h"
 
-#ifdef FOO
-#error FOO is already defined! We use FOO() macros to keep things succinct in this file.
-#endif
-
 namespace mozilla {
 
 WebGLExtensionCompressedTextureATC::WebGLExtensionCompressedTextureATC(WebGLContext* webgl)
     : WebGLExtensionBase(webgl)
 {
-    RefPtr<WebGLContext> webgl_ = webgl; // Bug 1201275
-    const auto fnAdd = [&webgl_](GLenum sizedFormat, webgl::EffectiveFormat effFormat) {
-        auto& fua = webgl_->mFormatUsage;
-
-        auto usage = fua->EditUsage(effFormat);
-        usage->isFilterable = true;
-        fua->AllowSizedTexFormat(sizedFormat, usage);
-
-        webgl_->mCompressedTextureFormats.AppendElement(sizedFormat);
-    };
-
-#define FOO(x) LOCAL_GL_ ## x, webgl::EffectiveFormat::x
-
-    fnAdd(FOO(ATC_RGB_AMD));
-    fnAdd(FOO(ATC_RGBA_EXPLICIT_ALPHA_AMD));
-    fnAdd(FOO(ATC_RGBA_INTERPOLATED_ALPHA_AMD));
-
-#undef FOO
+    webgl->mCompressedTextureFormats.AppendElement(LOCAL_GL_ATC_RGB);
+    webgl->mCompressedTextureFormats.AppendElement(LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA);
+    webgl->mCompressedTextureFormats.AppendElement(LOCAL_GL_ATC_RGBA_INTERPOLATED_ALPHA);
 }
 
 WebGLExtensionCompressedTextureATC::~WebGLExtensionCompressedTextureATC()
 {
 }
 
 IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionCompressedTextureATC, WEBGL_compressed_texture_atc)
 
--- a/dom/canvas/WebGLExtensionCompressedTextureETC1.cpp
+++ b/dom/canvas/WebGLExtensionCompressedTextureETC1.cpp
@@ -2,41 +2,22 @@
  * 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 "WebGLExtensions.h"
 
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "WebGLContext.h"
 
-#ifdef FOO
-#error FOO is already defined! We use FOO() macros to keep things succinct in this file.
-#endif
-
 namespace mozilla {
 
 WebGLExtensionCompressedTextureETC1::WebGLExtensionCompressedTextureETC1(WebGLContext* webgl)
     : WebGLExtensionBase(webgl)
 {
-    RefPtr<WebGLContext> webgl_ = webgl; // Bug 1201275
-    const auto fnAdd = [&webgl_](GLenum sizedFormat, webgl::EffectiveFormat effFormat) {
-        auto& fua = webgl_->mFormatUsage;
-
-        auto usage = fua->EditUsage(effFormat);
-        usage->isFilterable = true;
-        fua->AllowSizedTexFormat(sizedFormat, usage);
-
-        webgl_->mCompressedTextureFormats.AppendElement(sizedFormat);
-    };
-
-#define FOO(x) LOCAL_GL_ ## x, webgl::EffectiveFormat::x
-
-    fnAdd(FOO(ETC1_RGB8_OES));
-
-#undef FOO
+    webgl->mCompressedTextureFormats.AppendElement(LOCAL_GL_ETC1_RGB8_OES);
 }
 
 WebGLExtensionCompressedTextureETC1::~WebGLExtensionCompressedTextureETC1()
 {
 }
 
 IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionCompressedTextureETC1, WEBGL_compressed_texture_etc1)
 
--- a/dom/canvas/WebGLExtensionCompressedTexturePVRTC.cpp
+++ b/dom/canvas/WebGLExtensionCompressedTexturePVRTC.cpp
@@ -2,44 +2,25 @@
  * 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 "WebGLExtensions.h"
 
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "WebGLContext.h"
 
-#ifdef FOO
-#error FOO is already defined! We use FOO() macros to keep things succinct in this file.
-#endif
-
 namespace mozilla {
 
 WebGLExtensionCompressedTexturePVRTC::WebGLExtensionCompressedTexturePVRTC(WebGLContext* webgl)
     : WebGLExtensionBase(webgl)
 {
-    RefPtr<WebGLContext> webgl_ = webgl; // Bug 1201275
-    const auto fnAdd = [&webgl_](GLenum sizedFormat, webgl::EffectiveFormat effFormat) {
-        auto& fua = webgl_->mFormatUsage;
-
-        auto usage = fua->EditUsage(effFormat);
-        usage->isFilterable = true;
-        fua->AllowSizedTexFormat(sizedFormat, usage);
-
-        webgl_->mCompressedTextureFormats.AppendElement(sizedFormat);
-    };
-
-#define FOO(x) LOCAL_GL_ ## x, webgl::EffectiveFormat::x
-
-    fnAdd(FOO(COMPRESSED_RGB_PVRTC_4BPPV1));
-    fnAdd(FOO(COMPRESSED_RGB_PVRTC_2BPPV1));
-    fnAdd(FOO(COMPRESSED_RGBA_PVRTC_4BPPV1));
-    fnAdd(FOO(COMPRESSED_RGBA_PVRTC_2BPPV1));
-
-#undef FOO
+    webgl->mCompressedTextureFormats.AppendElement(LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1);
+    webgl->mCompressedTextureFormats.AppendElement(LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1);
+    webgl->mCompressedTextureFormats.AppendElement(LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1);
+    webgl->mCompressedTextureFormats.AppendElement(LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1);
 }
 
 WebGLExtensionCompressedTexturePVRTC::~WebGLExtensionCompressedTexturePVRTC()
 {
 }
 
 IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionCompressedTexturePVRTC, WEBGL_compressed_texture_pvrtc)
 
--- a/dom/canvas/WebGLExtensionCompressedTextureS3TC.cpp
+++ b/dom/canvas/WebGLExtensionCompressedTextureS3TC.cpp
@@ -2,44 +2,25 @@
  * 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 "WebGLExtensions.h"
 
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "WebGLContext.h"
 
-#ifdef FOO
-#error FOO is already defined! We use FOO() macros to keep things succinct in this file.
-#endif
-
 namespace mozilla {
 
 WebGLExtensionCompressedTextureS3TC::WebGLExtensionCompressedTextureS3TC(WebGLContext* webgl)
     : WebGLExtensionBase(webgl)
 {
-    RefPtr<WebGLContext> webgl_ = webgl; // Bug 1201275
-    const auto fnAdd = [&webgl_](GLenum sizedFormat, webgl::EffectiveFormat effFormat) {
-        auto& fua = webgl_->mFormatUsage;
-
-        auto usage = fua->EditUsage(effFormat);
-        usage->isFilterable = true;
-        fua->AllowSizedTexFormat(sizedFormat, usage);
-
-        webgl_->mCompressedTextureFormats.AppendElement(sizedFormat);
-    };
-
-#define FOO(x) LOCAL_GL_ ## x, webgl::EffectiveFormat::x
-
-    fnAdd(FOO(COMPRESSED_RGB_S3TC_DXT1_EXT));
-    fnAdd(FOO(COMPRESSED_RGBA_S3TC_DXT1_EXT));
-    fnAdd(FOO(COMPRESSED_RGBA_S3TC_DXT3_EXT));
-    fnAdd(FOO(COMPRESSED_RGBA_S3TC_DXT5_EXT));
-
-#undef FOO
+    webgl->mCompressedTextureFormats.AppendElement(LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT);
+    webgl->mCompressedTextureFormats.AppendElement(LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT);
+    webgl->mCompressedTextureFormats.AppendElement(LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT);
+    webgl->mCompressedTextureFormats.AppendElement(LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT);
 }
 
 WebGLExtensionCompressedTextureS3TC::~WebGLExtensionCompressedTextureS3TC()
 {
 }
 
 IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionCompressedTextureS3TC, WEBGL_compressed_texture_s3tc)
 
--- a/dom/canvas/WebGLExtensionDepthTexture.cpp
+++ b/dom/canvas/WebGLExtensionDepthTexture.cpp
@@ -8,39 +8,16 @@
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "WebGLContext.h"
 
 namespace mozilla {
 
 WebGLExtensionDepthTexture::WebGLExtensionDepthTexture(WebGLContext* webgl)
     : WebGLExtensionBase(webgl)
 {
-    auto& fua = webgl->mFormatUsage;
-
-    const auto fnAdd = [&fua](webgl::EffectiveFormat effFormat, GLenum unpackFormat,
-                              GLenum unpackType)
-    {
-        auto usage = fua->EditUsage(effFormat);
-        usage->isRenderable = true;
-
-        const webgl::PackingInfo pi = {unpackFormat, unpackType};
-        const webgl::DriverUnpackInfo dui = {unpackFormat, unpackFormat, unpackType};
-        fua->AddTexUnpack(usage, pi, dui);
-
-        fua->AllowUnsizedTexFormat(pi, usage);
-    };
-
-    fnAdd(webgl::EffectiveFormat::DEPTH_COMPONENT16, LOCAL_GL_DEPTH_COMPONENT,
-          LOCAL_GL_UNSIGNED_SHORT);
-
-    fnAdd(webgl::EffectiveFormat::DEPTH_COMPONENT24, LOCAL_GL_DEPTH_COMPONENT,
-          LOCAL_GL_UNSIGNED_INT);
-
-    fnAdd(webgl::EffectiveFormat::DEPTH24_STENCIL8, LOCAL_GL_DEPTH_STENCIL,
-          LOCAL_GL_UNSIGNED_INT_24_8);
 }
 
 WebGLExtensionDepthTexture::~WebGLExtensionDepthTexture()
 {
 }
 
 IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionDepthTexture, WEBGL_depth_texture)
 
--- a/dom/canvas/WebGLExtensionDrawBuffers.cpp
+++ b/dom/canvas/WebGLExtensionDrawBuffers.cpp
@@ -15,22 +15,36 @@
 
 namespace mozilla {
 
 WebGLExtensionDrawBuffers::WebGLExtensionDrawBuffers(WebGLContext* webgl)
     : WebGLExtensionBase(webgl)
 {
     MOZ_ASSERT(IsSupported(webgl), "Don't construct extension if unsupported.");
 
-    // WEBGL_draw_buffers:
-    // "The value of the MAX_COLOR_ATTACHMENTS_WEBGL parameter must be greater than or
-    //  equal to that of the MAX_DRAW_BUFFERS_WEBGL parameter."
-    webgl->mImplMaxColorAttachments = webgl->mGLMaxColorAttachments;
-    webgl->mImplMaxDrawBuffers = std::min(webgl->mGLMaxDrawBuffers,
-                                          webgl->mImplMaxColorAttachments);
+    GLint maxColorAttachments = 0;
+    GLint maxDrawBuffers = 0;
+
+    webgl->MakeContextCurrent();
+    gl::GLContext* gl = webgl->GL();
+
+    gl->fGetIntegerv(LOCAL_GL_MAX_COLOR_ATTACHMENTS, &maxColorAttachments);
+    gl->fGetIntegerv(LOCAL_GL_MAX_DRAW_BUFFERS, &maxDrawBuffers);
+
+    // WEBGL_draw_buffers specifications don't give a maximal value reachable by MAX_COLOR_ATTACHMENTS.
+    maxColorAttachments = std::min(maxColorAttachments, GLint(WebGLContext::kMaxColorAttachments));
+
+    if (webgl->MinCapabilityMode())
+        maxColorAttachments = std::min(maxColorAttachments, GLint(kMinColorAttachments));
+
+    // WEBGL_draw_buffers specifications request MAX_COLOR_ATTACHMENTS >= MAX_DRAW_BUFFERS.
+    maxDrawBuffers = std::min(maxDrawBuffers, GLint(maxColorAttachments));
+
+    webgl->mGLMaxColorAttachments = maxColorAttachments;
+    webgl->mGLMaxDrawBuffers = maxDrawBuffers;
 }
 
 WebGLExtensionDrawBuffers::~WebGLExtensionDrawBuffers()
 {
 }
 
 void
 WebGLExtensionDrawBuffers::DrawBuffersWEBGL(const dom::Sequence<GLenum>& buffers)
@@ -46,21 +60,29 @@ WebGLExtensionDrawBuffers::DrawBuffersWE
 bool
 WebGLExtensionDrawBuffers::IsSupported(const WebGLContext* webgl)
 {
     gl::GLContext* gl = webgl->GL();
 
     if (!gl->IsSupported(gl::GLFeature::draw_buffers))
         return false;
 
+    GLint supportedColorAttachments = 0;
+    GLint supportedDrawBuffers = 0;
+
+    webgl->MakeContextCurrent();
+
+    gl->fGetIntegerv(LOCAL_GL_MAX_COLOR_ATTACHMENTS, &supportedColorAttachments);
+    gl->fGetIntegerv(LOCAL_GL_MAX_COLOR_ATTACHMENTS, &supportedDrawBuffers);
+
     // WEBGL_draw_buffers requires at least 4 color attachments.
-    if (webgl->mGLMaxDrawBuffers < webgl->kMinMaxDrawBuffers ||
-        webgl->mGLMaxColorAttachments < webgl->kMinMaxColorAttachments)
-    {
+    if (size_t(supportedColorAttachments) < kMinColorAttachments)
         return false;
-    }
+
+    if (size_t(supportedDrawBuffers) < kMinDrawBuffers)
+        return false;
 
     return true;
 }
 
 IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionDrawBuffers, WEBGL_draw_buffers)
 
 } // namespace mozilla
--- a/dom/canvas/WebGLExtensionSRGB.cpp
<