Pull in aggressive refactor from tex-format-tables. draft
authorjdashg <jdashg+github@gmail.com>
Thu, 17 Dec 2015 16:16:50 -0800
changeset 316070 0f5b6c06d6d8fc23a061a460e9181562a37b29df
parent 316069 ff840b3cb42c2373eef0aeac3a85d074fea2944f
child 316071 bc034dc85ea7e7c210546c720e3b0b9b77e04132
push id8514
push userjgilbert@mozilla.com
push dateFri, 18 Dec 2015 00:24:33 +0000
milestone45.0a1
Pull in aggressive refactor from tex-format-tables.
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/WebGLContextGL.cpp
dom/canvas/WebGLContextState.cpp
dom/canvas/WebGLContextTextures.cpp
dom/canvas/WebGLContextUtils.cpp
dom/canvas/WebGLContextUtils.h
dom/canvas/WebGLContextValidate.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/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/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/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/gl/ScopedGLHelpers.cpp
gfx/gl/ScopedGLHelpers.h
new file mode 100644
--- /dev/null
+++ b/dom/canvas/TexUnpackBlob.cpp
@@ -0,0 +1,744 @@
+/* -*- 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 "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)
+{
+    auto internalFormat = dui->internalFormat;
+    auto unpackFormat = dui->unpackFormat;
+    auto unpackType = dui->unpackType;
+
+    if (isSubImage) {
+        return DoTexSubImage(gl, target, level, xOffset, yOffset, zOffset, width, height,
+                             depth, unpackFormat, unpackType, data);
+    } else {
+        return DoTexImage(gl, target, level, internalFormat, width, height, depth,
+                          unpackFormat, unpackType, data);
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// TexUnpackBuffer
+
+static bool
+GetPackedSizeForUnpack(uint32_t bytesPerPixel, uint8_t rowByteAlignment,
+                       uint32_t maybeStridePixelsPerRow, uint32_t maybeStrideRowsPerImage,
+                       uint32_t skipPixelsPerRow, uint32_t skipRowsPerImage,
+                       uint32_t skipImages, uint32_t usedPixelsPerRow,
+                       uint32_t usedRowsPerImage, uint32_t usedImages,
+                       uint32_t* const out_packedBytes)
+{
+    MOZ_RELEASE_ASSERT(rowByteAlignment != 0);
+
+    if (!usedPixelsPerRow || !usedRowsPerImage || !usedImages) {
+        *out_packedBytes = 0;
+        return true;
+    }
+    // Now we know there's at least one image.
+
+    CheckedUint32 pixelsPerRow = CheckedUint32(skipPixelsPerRow) + usedPixelsPerRow;
+    CheckedUint32 rowsPerImage = CheckedUint32(skipRowsPerImage) + usedRowsPerImage;
+    CheckedUint32 images = CheckedUint32(skipImages) + usedImages;
+
+    MOZ_ASSERT_IF(maybeStridePixelsPerRow,
+                  (pixelsPerRow.isValid() &&
+                   maybeStridePixelsPerRow >= pixelsPerRow.value()));
+
+    MOZ_ASSERT_IF(maybeStrideRowsPerImage,
+                  (rowsPerImage.isValid() &&
+                   maybeStrideRowsPerImage >= rowsPerImage.value()));
+
+    CheckedUint32 stridePixelsPerRow = maybeStridePixelsPerRow ? maybeStridePixelsPerRow
+                                                               : pixelsPerRow;
+    CheckedUint32 strideRowsPerImage = maybeStrideRowsPerImage ? maybeStrideRowsPerImage
+                                                               : rowsPerImage;
+
+    CheckedUint32 strideBytesPerRow = bytesPerPixel * stridePixelsPerRow;
+    strideBytesPerRow = RoundUpToMultipleOf(strideBytesPerRow, rowByteAlignment);
+
+    CheckedUint32 strideBytesPerImage = strideBytesPerRow * strideRowsPerImage;
+
+    CheckedUint32 lastRowBytes = CheckedUint32(bytesPerPixel) * pixelsPerRow;
+    CheckedUint32 lastImageBytes = strideBytesPerRow * (rowsPerImage - 1) + lastRowBytes;
+
+    CheckedUint32 packedBytes = strideBytesPerImage * (images - 1) + lastImageBytes;
+
+    if (!packedBytes.isValid())
+        return false;
+
+    *out_packedBytes = packedBytes.value();
+    return true;
+}
+
+////////////////////
+
+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 rowByteAlignment        = webgl->mPixelStore_UnpackAlignment;
+    const auto maybeStridePixelsPerRow = webgl->mPixelStore_UnpackRowLength;
+    const auto maybeStrideRowsPerImage = webgl->mPixelStore_UnpackImageHeight;
+    const auto skipPixelsPerRow        = webgl->mPixelStore_UnpackSkipPixels;
+    const auto skipRowsPerImage        = webgl->mPixelStore_UnpackSkipRows;
+    const auto skipImages              = isFunc3D ? webgl->mPixelStore_UnpackSkipImages
+                                                  : 0;
+    const auto usedPixelsPerRow = mWidth;
+    const auto usedRowsPerImage = mHeight;
+    const auto usedImages       = mDepth;
+
+    uint32_t bytesNeeded;
+    if (!GetPackedSizeForUnpack(bytesPerPixel, rowByteAlignment,
+                                maybeStridePixelsPerRow, maybeStrideRowsPerImage,
+                                skipPixelsPerRow, skipRowsPerImage, skipImages,
+                                usedPixelsPerRow, usedRowsPerImage, usedImages,
+                                &bytesNeeded))
+    {
+        webgl->ErrorInvalidOperation("%s: Overflow while computing the needed buffer"
+                                     " size.",
+                                     funcName);
+        return false;
+    }
+
+    if (bytesNeeded > mByteCount) {
+        webgl->ErrorInvalidOperation("%s: Provided buffer is too small. (needs %u, has"
+                                     " %u)",
+                                     funcName, bytesNeeded, 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, 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();
+        break;
+    } while (false);
+
+    GLenum error = DoTexOrSubImage(isSubImage, gl, target, level, dui, xOffset, yOffset,
+                                   zOffset, mWidth, mHeight, mDepth, uploadBytes);
+    *out_glError = error;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+// TexUnpackDataSurface
+
+static bool
+GuessAlignment(const void* data, size_t width, size_t stride, size_t maxAlignment,
+               size_t* const out_alignment)
+{
+    size_t alignmentGuess = maxAlignment;
+    while (alignmentGuess) {
+        size_t guessStride = RoundUpToMultipleOf(width, 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*/ void
+TexUnpackSurface::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;
+}
+
+/*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)
+{
+    *out_glError = 0;
+
+    if (isSurfAlphaPremult != webgl->mPixelStore_PremultiplyAlpha)
+        return false;
+
+    gl::OriginPos srcOrigin, dstOrigin;
+    OriginsForDOM(webgl, &srcOrigin, &dstOrigin);
+    if (srcOrigin != dstOrigin)
+        return false;
+
+    gl::GLContext* gl = webgl->gl;
+
+    // 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 (dui->internalFormat == LOCAL_GL_RGBA &&
+            dui->unpackFormat == LOCAL_GL_RGBA &&
+            dui->unpackType == LOCAL_GL_UNSIGNED_BYTE)
+        {
+            if (gl->IsANGLE()) {
+                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;
+    }
+
+    if (!chosenDUI)
+        return false;
+
+    gfx::DataSourceSurface::ScopedMap map(surf, gfx::DataSourceSurface::MapType::READ);
+    if (!map.IsMapped())
+        return false;
+
+    const GLint kMaxUnpackAlignment = 8;
+    size_t unpackAlignment;
+    if (!GuessAlignment(map.GetData(), width, map.GetStride(), kMaxUnpackAlignment,
+                        &unpackAlignment))
+    {
+        return false;
+    }
+
+    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;
+    }
+
+    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;
+        }
+
+    case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
+        switch (packingFormat) {
+        case LOCAL_GL_RGB:
+            *out_texelFormat = WebGLTexelFormat::RGB565;
+            return true;
+
+        default:
+            break;
+        }
+
+    case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
+        switch (packingFormat) {
+        case LOCAL_GL_RGBA:
+            *out_texelFormat = WebGLTexelFormat::RGBA5551;
+            return true;
+
+        default:
+            break;
+        }
+
+    case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
+        switch (packingFormat) {
+        case LOCAL_GL_RGBA:
+            *out_texelFormat = WebGLTexelFormat::RGBA4444;
+            return true;
+
+        default:
+            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;
+        }
+
+    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;
+        }
+
+    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:
+
+    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)
+{ }
+
+void
+TexUnpackSurface::TexOrSubImage(bool isSubImage, 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;
+
+    // TODO: Do blitting of the native SourceSurface.
+
+    RefPtr<gfx::DataSourceSurface> dataSurf = mSurf->GetDataSurface();
+    MOZ_ASSERT(dataSurf);
+
+    WebGLContext* webgl = tex->mContext;
+
+    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 {
+            MOZ_CRASH("Failed to convert surface.");
+        }
+        return;
+    }
+
+    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
new file mode 100644
--- /dev/null
+++ b/dom/canvas/TexUnpackBlob.h
@@ -0,0 +1,135 @@
+/* -*- 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 "GLDefs.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/gfx/Point.h"
+#include "WebGLStrongTypes.h"
+
+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 bool ValidateUnpack(WebGLContext* webgl, const char* funcName, bool isFunc3D,
+                                const webgl::PackingInfo& pi) = 0;
+
+    virtual void TexOrSubImage(bool isSubImage, const char* funcName, WebGLTexture* tex,
+                               TexImageTarget target, GLint level,
+                               const webgl::DriverUnpackInfo* dui, GLint xOffset,
+                               GLint yOffset, GLint zOffset,
+                               GLenum* const out_glError) = 0;
+};
+
+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, 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 bool ValidateUnpack(WebGLContext* webgl, const char* funcName, bool isFunc3D,
+                                const webgl::PackingInfo& pi) override
+    {
+        return true;
+    }
+
+    virtual void TexOrSubImage(bool isSubImage, 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);
+
+    static void OriginsForDOM(WebGLContext* webgl, gl::OriginPos* const out_src,
+                              gl::OriginPos* const out_dst);
+};
+
+} // 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() const
+WebGL1Context::CreateFormatUsage(gl::GLContext* gl) const
 {
-    return webgl::FormatUsageAuthority::CreateForWebGL1();
+    return webgl::FormatUsageAuthority::CreateForWebGL1(gl);
 }
 
 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,17 +13,19 @@ namespace mozilla {
 class WebGL1Context
     : public WebGLContext
 {
 public:
     static WebGL1Context* Create();
 
 private:
     WebGL1Context();
-    virtual UniquePtr<webgl::FormatUsageAuthority> CreateFormatUsage() const override;
+
+    virtual UniquePtr<webgl::FormatUsageAuthority>
+    CreateFormatUsage(gl::GLContext* gl) 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() const
+WebGL2Context::CreateFormatUsage(gl::GLContext* gl) const
 {
-    return webgl::FormatUsageAuthority::CreateForWebGL2();
+    return webgl::FormatUsageAuthority::CreateForWebGL2(gl);
 }
 
 /*static*/ bool
 WebGL2Context::IsSupported()
 {
     return Preferences::GetBool("webgl.enable-prototype-webgl2", false);
 }
 
@@ -159,26 +159,16 @@ WebGLContext::InitWebGL2()
     }
 
     // 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;
 
     return true;
 }
--- a/dom/canvas/WebGL2Context.h
+++ b/dom/canvas/WebGL2Context.h
@@ -3,22 +3,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #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 {
@@ -91,47 +85,57 @@ 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 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 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 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, 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);
+                              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);
 
 
     // -------------------------------------------------------------------------
     // Programs and shaders - WebGL2ContextPrograms.cpp
     GLint GetFragDataLocation(WebGLProgram* program, const nsAString& name);
 
 
     // -------------------------------------------------------------------------
@@ -378,17 +382,18 @@ public:
     already_AddRefed<WebGLVertexArrayObject> CreateVertexArray();
     void DeleteVertexArray(WebGLVertexArrayObject* vertexArray);
     bool IsVertexArray(WebGLVertexArrayObject* vertexArray);
     void BindVertexArray(WebGLVertexArrayObject* vertexArray);
 */
 
 private:
     WebGL2Context();
-    virtual UniquePtr<webgl::FormatUsageAuthority> CreateFormatUsage() const override;
+    virtual UniquePtr<webgl::FormatUsageAuthority>
+    CreateFormatUsage(gl::GLContext* gl) 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
@@ -23,46 +23,68 @@ GetFBInfoForBlit(const WebGLFramebuffer*
     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().get();
-    } else {
-        *out_colorFormat = nullptr;
+        *out_colorFormat = attachment.Format()->format;
     }
 
     if (fb->DepthStencilAttachment().IsDefined()) {
         const auto& attachment = fb->DepthStencilAttachment();
-        *out_depthFormat = attachment.Format().get();
+        *out_depthFormat = attachment.Format()->format;
         *out_stencilFormat = *out_depthFormat;
     } else {
         if (fb->DepthAttachment().IsDefined()) {
             const auto& attachment = fb->DepthAttachment();
-            *out_depthFormat = attachment.Format().get();
-        } else {
-            *out_depthFormat = nullptr;
+            *out_depthFormat = attachment.Format()->format;
         }
 
         if (fb->StencilAttachment().IsDefined()) {
             const auto& attachment = fb->StencilAttachment();
-            *out_stencilFormat = attachment.Format().get();
-        } else {
-            *out_stencilFormat = nullptr;
+            *out_stencilFormat = attachment.Format()->format;
         }
     }
     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;
@@ -96,91 +118,80 @@ 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;
-    const webgl::FormatInfo* srcDepthFormat;
-    const webgl::FormatInfo* srcStencilFormat;
+    const webgl::FormatInfo* srcColorFormat = nullptr;
+    const webgl::FormatInfo* srcDepthFormat = nullptr;
+    const webgl::FormatInfo* srcStencilFormat = nullptr;
 
     if (mBoundReadFramebuffer) {
         if (!GetFBInfoForBlit(mBoundReadFramebuffer, this, "READ_FRAMEBUFFER",
                               &srcSamples, &srcColorFormat, &srcDepthFormat,
                               &srcStencilFormat))
         {
             return;
         }
     } else {
         srcSamples = 1; // Always 1.
 
-        // 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;
-            }
-        }
+        GetBackbufferFormats(mOptions, &srcColorFormat, &srcDepthFormat,
+                             &srcStencilFormat);
     }
 
     GLsizei dstSamples;
-    GLenum dstColorFormat = 0;
-    GLenum dstDepthFormat = 0;
-    GLenum dstStencilFormat = 0;
+    const webgl::FormatInfo* dstColorFormat = nullptr;
+    const webgl::FormatInfo* dstDepthFormat = nullptr;
+    const webgl::FormatInfo* dstStencilFormat = nullptr;
 
     if (mBoundDrawFramebuffer) {
         if (!GetFBInfoForBlit(mBoundDrawFramebuffer, this, "DRAW_FRAMEBUFFER",
                               &dstSamples, &dstColorFormat, &dstDepthFormat,
                               &dstStencilFormat))
         {
             return;
         }
     } else {
         dstSamples = gl->Screen()->Samples();
 
-        // 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;
-            }
-        }
+        GetBackbufferFormats(mOptions, &dstColorFormat, &dstDepthFormat,
+                             &dstStencilFormat);
     }
 
+    if (mask & LOCAL_GL_COLOR_BUFFER_BIT) {
+        const auto fnSignlessType = [](const webgl::FormatInfo* format) {
+            if (!format)
+                return webgl::ComponentType::None;
 
-    if (mask & LOCAL_GL_COLOR_BUFFER_BIT) {
-        const GLenum srcColorType = srcColorFormat ? ValueTypeForFormat(srcColorFormat)
-                                                   : 0;
-        const GLenum dstColorType = dstColorFormat ? ValueTypeForFormat(dstColorFormat)
-                                                   : 0;
-        if (dstColorType != srcColorType) {
-            ErrorInvalidOperation("blitFramebuffer: Color buffer value type"
+            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"
                                   " mismatch.");
             return;
         }
 
-        const bool srcIsInt = srcColorType == LOCAL_GL_INT ||
-                              srcColorType == LOCAL_GL_UNSIGNED_INT;
+        const bool srcIsInt = (srcType == webgl::ComponentType::Int);
         if (srcIsInt && filter != LOCAL_GL_NEAREST) {
             ErrorInvalidOperation("blitFramebuffer: Integer read buffers can only"
                                   " be filtered with NEAREST.");
             return;
         }
     }
 
     /* GLES 3.0.4, p199:
@@ -280,24 +291,24 @@ WebGL2Context::FramebufferTextureLayer(G
                                      "texture object.");
         }
 
         if (level < 0)
             return ErrorInvalidValue("framebufferTextureLayer: layer must be >= 0.");
 
         switch (texture->Target().get()) {
         case LOCAL_GL_TEXTURE_3D:
-            if ((GLuint) layer >= mGLMax3DTextureSize) {
+            if (uint32_t(layer) >= mImplMax3DTextureSize) {
                 return ErrorInvalidValue("framebufferTextureLayer: layer must be < "
                                          "MAX_3D_TEXTURE_SIZE");
             }
             break;
 
         case LOCAL_GL_TEXTURE_2D_ARRAY:
-            if ((GLuint) layer >= mGLMaxArrayTextureLayers) {
+            if (uint32_t(layer) >= mImplMaxArrayTextureLayers) {
                 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.");
@@ -330,17 +341,17 @@ WebGL2Context::FramebufferTextureLayer(G
     fb->FramebufferTextureLayer(attachment, texture, level, layer);
 }
 
 JS::Value
 WebGL2Context::GetFramebufferAttachmentParameter(JSContext* cx,
                                                  GLenum target,
                                                  GLenum attachment,
                                                  GLenum pname,
-                                                 ErrorResult& rv)
+                                                 ErrorResult& 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."
@@ -354,81 +365,99 @@ WebGL2Context::GetFramebufferAttachmentP
 
     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);
+        return boundFB->GetAttachmentParameter(cx, target, attachment, pname, &out_error);
     }
 
+    ////////////////
     // 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. */
+    // 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;
+    case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT:
         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:
+        if (attachment != LOCAL_GL_BACK)
+            return JS::Int32Value(0);
+
+        return JS::Int32Value(8);
+
     case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE:
+        if (attachment != LOCAL_GL_BACK)
+            return JS::Int32Value(0);
+
+        return JS::Int32Value(mOptions.alpha ? 8 : 0);
+
     case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE:
+        if (attachment != LOCAL_GL_DEPTH &&
+            attachment != LOCAL_GL_DEPTH_STENCIL_ATTACHMENT)
+        {
+            return JS::Int32Value(0);
+        }
+
+        return JS::Int32Value(mOptions.depth ? 16 : 0);
+
     case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE:
-        return JS::Int32Value(webgl::GetComponentSize(effectiveFormat, pname));
+        if (attachment != LOCAL_GL_STENCIL &&
+            attachment != LOCAL_GL_DEPTH_STENCIL_ATTACHMENT)
+        {
+            return JS::Int32Value(0);
+        }
+
+        return JS::Int32Value(mOptions.stencil ? 8 : 0);
 
     case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE:
-        if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT &&
-            pname == LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE)
-        {
+        switch (attachment) {
+        case LOCAL_GL_BACK:    return JS::Int32Value(LOCAL_GL_UNSIGNED_NORMALIZED);
+        case LOCAL_GL_DEPTH:   return JS::Int32Value(LOCAL_GL_UNSIGNED_NORMALIZED);
+        case LOCAL_GL_STENCIL: return JS::Int32Value(LOCAL_GL_UNSIGNED_INT);
+
+        case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT:
             ErrorInvalidOperation("getFramebufferAttachmentParameter: Querying "
                                   "FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE against "
                                   "DEPTH_STENCIL_ATTACHMENT is an error.");
             return JS::NullValue();
         }
-
-        return JS::Int32Value(webgl::GetComponentType(effectiveFormat));
+        MOZ_CRASH("unreachable");
 
     case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING:
-        return JS::Int32Value(webgl::GetColorEncoding(effectiveFormat));
+        if (attachment != LOCAL_GL_BACK)
+            return JS::Int32Value(0);
+
+        return JS::Int32Value(LOCAL_GL_LINEAR);
     }
 
-    /* Any combinations of framebuffer type and pname not described above will generate an
-       INVALID_ENUM error. */
-    ErrorInvalidEnum("getFramebufferAttachmentParameter: Invalid combination of ");
+    // Any combinations of framebuffer type and pname not described above will generate an
+    // INVALID_ENUM error.
+    ErrorInvalidEnum("getFramebufferAttachmentParameter: Invalid combination of params.");
     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)
 {
--- a/dom/canvas/WebGL2ContextState.cpp
+++ b/dom/canvas/WebGL2ContextState.cpp
@@ -54,18 +54,16 @@ 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:
@@ -84,16 +82,22 @@ 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);
     }
 
--- a/dom/canvas/WebGL2ContextTextures.cpp
+++ b/dom/canvas/WebGL2ContextTextures.cpp
@@ -5,112 +5,236 @@
 
 #include "GLContext.h"
 #include "WebGL2Context.h"
 #include "WebGLContextUtils.h"
 #include "WebGLTexture.h"
 
 namespace mozilla {
 
+static bool
+ValidateTargetMatchesFuncDims(WebGLContext* webgl, const char* funcName, uint8_t funcDims,
+                              GLenum rawTexTarget)
+{
+    uint8_t targetDims = 0;
+    switch (rawTexTarget) {
+    case LOCAL_GL_TEXTURE_2D:
+    case LOCAL_GL_TEXTURE_CUBE_MAP:
+        targetDims = 2;
+        break;
+
+    case LOCAL_GL_TEXTURE_3D:
+        targetDims = 3;
+        break;
+    }
+
+    if (targetDims != funcDims) {
+        webgl->ErrorInvalidEnum("%s: Invalid texTarget.", funcName);
+        return false;
+    }
+
+    return true;
+}
+
 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";
-    TexTarget texTarget;
-    WebGLTexture* tex;
-    if (!ValidateTexTarget(this, rawTexTarget, funcName, &texTarget, &tex))
+    const uint8_t funcDims = 2;
+
+    if (!ValidateTargetMatchesFuncDims(this, funcName, funcDims, rawTexTarget))
         return;
 
-    tex->TexStorage2D(texTarget, levels, internalFormat, width, height);
+    TexTarget target;
+    WebGLTexture* tex;
+    if (!ValidateTexTarget(this, funcName, rawTexTarget, &target, &tex))
+        return;
+
+    const GLsizei depth = 1;
+    tex->TexStorage(funcName, target, levels, internalFormat, width, height, depth);
 }
 
 void
 WebGL2Context::TexStorage3D(GLenum rawTexTarget, GLsizei levels, GLenum internalFormat,
                             GLsizei width, GLsizei height, GLsizei depth)
 {
     const char funcName[] = "texStorage3D";
-    TexTarget texTarget;
-    WebGLTexture* tex;
-    if (!ValidateTexTarget(this, rawTexTarget, funcName, &texTarget, &tex))
+    const uint8_t funcDims = 3;
+
+    if (!ValidateTargetMatchesFuncDims(this, funcName, funcDims, rawTexTarget))
         return;
 
-    tex->TexStorage3D(texTarget, levels, internalFormat, width, height, depth);
+    TexTarget target;
+    WebGLTexture* tex;
+    if (!ValidateTexTarget(this, funcName, rawTexTarget, &target, &tex))
+        return;
+
+    tex->TexStorage(funcName, target, 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,
-                          ErrorResult& out_rv)
+                          GLsizei width, GLsizei height, GLsizei depth, GLint border,
+                          GLenum unpackFormat, GLenum unpackType,
+                          const dom::Nullable<dom::ArrayBufferViewOrSharedArrayBufferView>& maybeView)
 {
     const char funcName[] = "texImage3D";
-    TexImageTarget texImageTarget;
+    const uint8_t funcDims = 3;
+
+    TexImageTarget target;
     WebGLTexture* tex;
-    if (!ValidateTexImageTarget(this, rawTexImageTarget, funcName, &texImageTarget, &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))
     {
         return;
     }
 
-    tex->TexImage3D(texImageTarget, level, internalFormat, width, height, depth, border,
-                    unpackFormat, unpackType, maybeView, &out_rv);
+    const bool isSubImage = true;
+    const GLenum internalFormat = 0;
+    tex->TexOrSubImage(isSubImage, funcName, target, level, internalFormat, xOffset,
+                       yOffset, zOffset, unpackFormat, unpackType, imageData);
 }
 
 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)
+WebGL2Context::TexSubImage3D(GLenum rawTexImageTarget, GLint level, GLint xOffset,
+                             GLint yOffset, GLint zOffset, GLenum unpackFormat,
+                             GLenum unpackType, dom::Element* elem,
+                             ErrorResult* const out_rv)
 {
     const char funcName[] = "texSubImage3D";
-    TexImageTarget texImageTarget;
+    const uint8_t funcDims = 3;
+
+    TexImageTarget target;
     WebGLTexture* tex;
-    if (!ValidateTexImageTarget(this, rawTexImageTarget, funcName, &texImageTarget, &tex))
+    if (!ValidateTexImageTarget(this, funcName, funcDims, rawTexImageTarget, &target,
+                                &tex))
     {
         return;
     }
 
-    tex->TexSubImage3D(texImageTarget, level, xOffset, yOffset, zOffset, width, height,
-                       depth, unpackFormat, unpackType, maybeView, &out_rv);
-
+    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::TexSubImage3D(GLenum target, GLint level,
-                             GLint xOffset, GLint yOffset, GLint zOffset,
-                             GLenum unpackFormat, GLenum unpackType, dom::ImageData* imageData,
-                             ErrorResult& out_rv)
+WebGL2Context::CompressedTexImage3D(GLenum rawTexImageTarget, GLint level,
+                                    GLenum internalFormat, GLsizei width, GLsizei height,
+                                    GLsizei depth, GLint border,
+                                    const dom::ArrayBufferViewOrSharedArrayBufferView& view)
 {
-    GenerateWarning("texSubImage3D: Not implemented.");
+    const char funcName[] = "compressedTexImage3D";
+    const uint8_t funcDims = 3;
+
+    TexImageTarget target;
+    WebGLTexture* tex;
+    if (!ValidateTexImageTarget(this, funcName, funcDims, rawTexImageTarget, &target,
+                                &tex))
+    {
+        return;
+    }
+
+    tex->CompressedTexImage(funcName, target, level, internalFormat, width, height, depth,
+                            border, view);
 }
 
 void
-WebGL2Context::CopyTexSubImage3D(GLenum target, GLint level,
-                                 GLint xOffset, GLint yOffset, GLint zOffset,
-                                 GLint x, GLint y, GLsizei width, GLsizei height)
+WebGL2Context::CompressedTexSubImage3D(GLenum rawTexImageTarget, GLint level,
+                                       GLint xOffset, GLint yOffset, GLint zOffset,
+                                       GLsizei width, GLsizei height, GLsizei depth,
+                                       GLenum sizedUnpackFormat,
+                                       const dom::ArrayBufferViewOrSharedArrayBufferView& view)
 {
-    GenerateWarning("copyTexSubImage3D: Not implemented.");
+    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);
 }
 
 void
-WebGL2Context::CompressedTexImage3D(GLenum target, GLint level, GLenum internalFormat,
-                                    GLsizei width, GLsizei height, GLsizei depth,
-                                    GLint border, GLsizei imageSize, const dom::ArrayBufferViewOrSharedArrayBufferView& view)
+WebGL2Context::CopyTexSubImage3D(GLenum rawTexImageTarget, GLint level, GLint xOffset,
+                                 GLint yOffset, GLint zOffset, GLint x, GLint y,
+                                 GLsizei width, GLsizei height)
 {
-    GenerateWarning("compressedTexImage3D: Not implemented.");
-}
+    const char funcName[] = "copyTexSubImage3D";
+    const uint8_t funcDims = 3;
 
-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.");
+    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);
 }
 
 bool
 WebGL2Context::IsTexParamValid(GLenum pname) const
 {
     switch (pname) {
     case LOCAL_GL_TEXTURE_BASE_LEVEL:
     case LOCAL_GL_TEXTURE_COMPARE_FUNC:
--- 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 Context();
+        return mContext;
     }
 
     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
@@ -106,19 +106,16 @@ WebGLContext::WebGLContext()
     mGeneration = 0;
     mInvalidated = false;
     mCapturedFrameInvalidated = false;
     mShouldPresent = true;
     mResetLayer = true;
     mOptionsFrozen = false;
 
     mActiveTexture = 0;
-    mPixelStoreFlipY = false;
-    mPixelStorePremultiplyAlpha = false;
-    mPixelStoreColorspaceConversion = BROWSER_DEFAULT_WEBGL;
 
     mVertexAttrib0Vector[0] = 0;
     mVertexAttrib0Vector[1] = 0;
     mVertexAttrib0Vector[2] = 0;
     mVertexAttrib0Vector[3] = 1;
     mFakeVertexAttrib0BufferObjectVector[0] = 0;
     mFakeVertexAttrib0BufferObjectVector[1] = 0;
     mFakeVertexAttrib0BufferObjectVector[2] = 0;
@@ -135,41 +132,16 @@ 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);
@@ -1816,36 +1788,42 @@ WebGLContext::DidRefresh()
     if (gl) {
         gl->FlushIfHeavyGLCallsSinceLastFlush();
     }
 }
 
 size_t
 RoundUpToMultipleOf(size_t value, size_t multiple)
 {
-    size_t overshoot = value + multiple - 1;
-    return overshoot - (overshoot % multiple);
+    return ((value + multiple - 1) / multiple) * multiple;
 }
 
 CheckedUint32
-RoundedToNextMultipleOf(CheckedUint32 x, CheckedUint32 y)
+RoundedToNextMultipleOf(CheckedUint32 value, CheckedUint32 multiple)
 {
-    return ((x + y - 1) / y) * y;
+    return ((value + multiple - 1) / multiple) * multiple;
 }
 
 bool
 WebGLContext::ValidateCurFBForRead(const char* funcName,
-                                   TexInternalFormat* const out_format,
+                                   const webgl::FormatUsageInfo** const out_format,
                                    uint32_t* const out_width, uint32_t* const out_height)
 {
     if (!mBoundReadFramebuffer) {
         ClearBackbufferIfNeeded();
-        // 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.
-        *out_format = mOptions.alpha ? LOCAL_GL_RGBA8 : LOCAL_GL_RGB8;
+
+        // 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);
 }
@@ -1883,16 +1861,203 @@ 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)
+{
+    gl::GLContext* gl = mWebGL->gl;
+
+    if (mWebGL->mPixelStore_UnpackAlignment != 4) gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT   , 4);
+
+    if (mWebGL->IsWebGL2()) {
+        if (mWebGL->mPixelStore_UnpackRowLength   != 0) gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH  , 0);
+        if (mWebGL->mPixelStore_UnpackImageHeight != 0) gl->fPixelStorei(LOCAL_GL_UNPACK_IMAGE_HEIGHT, 0);
+        if (mWebGL->mPixelStore_UnpackSkipPixels  != 0) gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_PIXELS , 0);
+        if (mWebGL->mPixelStore_UnpackSkipRows    != 0) gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_ROWS   , 0);
+        if (mWebGL->mPixelStore_UnpackSkipImages  != 0) gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES , 0);
+
+        if (mWebGL->mBoundPixelUnpackBuffer) gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, 0);
+    }
+}
+
+void
+ScopedUnpackReset::UnwrapImpl()
+{
+    // Check that we're not falling out of scope after the current context changed.
+    MOZ_ASSERT(mGL->IsCurrent());
+
+    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);
+    }
+}
+
+////////////////////////////////////////
+
+bool
+GuessAlignmentFromStride(size_t width, size_t stride, size_t maxAlignment,
+                         size_t* const out_alignment)
+{
+    size_t alignmentGuess = maxAlignment;
+    while (alignmentGuess) {
+        size_t guessStride = RoundUpToMultipleOf(width, alignmentGuess);
+        if (guessStride == stride) {
+            *out_alignment = alignmentGuess;
+            return true;
+        }
+        alignmentGuess /= 2;
+    }
+    return false;
+}
+
+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 auto unpackFormat = driverUnpackInfo->unpackFormat;
+    const auto unpackType = driverUnpackInfo->unpackType;
+    const webgl::PackingInfo packing = { unpackFormat, unpackType };
+
+    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);
+
+        const auto internalFormat = driverUnpackInfo->internalFormat;
+        error = DoTexImage(gl, target, level, internalFormat, width, height, depth,
+                           unpackFormat, unpackType, zeros.get());
+    } else {
+        error = DoTexSubImage(gl, target, level, xOffset, yOffset, zOffset, width, height,
+                              depth, unpackFormat, unpackType, zeros.get());
+    }
+    if (error)
+        return false;
+
+    return true;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // XPCOM goop
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(WebGLContext)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(WebGLContext)
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLContext,
   mCanvasElement,
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -21,26 +21,29 @@
 #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"
@@ -63,27 +66,34 @@ 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 ScopedUnpackReset;
 class WebGLActiveInfo;
 class WebGLContextLossHandler;
 class WebGLBuffer;
 class WebGLExtensionBase;
 class WebGLFramebuffer;
 class WebGLProgram;
 class WebGLQuery;
 class WebGLRenderbuffer;
@@ -106,16 +116,19 @@ template<typename> struct Nullable;
 
 namespace gfx {
 class SourceSurface;
 } // namespace gfx
 
 namespace webgl {
 struct LinkedProgramInfo;
 class ShaderValidator;
+class TexUnpackBuffer;
+class TexUnpackImage;
+class TexUnpackSrcSurface;
 } // namespace webgl
 
 WebGLTexelFormat GetWebGLTexelFormat(TexInternalFormat format);
 
 void AssertUintParamCorrect(gl::GLContext* gl, GLenum pname, GLuint shadow);
 
 struct WebGLContextOptions
 {
@@ -518,16 +531,21 @@ 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,
@@ -880,56 +898,72 @@ 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& out_rv);
+                    ErrorResult&);
     void TexImage2D(GLenum texImageTarget, GLint level, GLenum internalFormat,
                     GLenum unpackFormat, GLenum unpackType, dom::ImageData* imageData,
-                    ErrorResult& out_rv);
+                    ErrorResult&);
     void TexImage2D(GLenum texImageTarget, GLint level, GLenum internalFormat,
                     GLenum unpackFormat, GLenum unpackType, dom::Element* elem,
-                    ErrorResult* const out_rv);
-
+                    ErrorResult* const out_error);
 
     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& out_rv);
+                       ErrorResult&);
     void TexSubImage2D(GLenum texImageTarget, GLint level, GLint xOffset, GLint yOffset,
                        GLenum unpackFormat, GLenum unpackType, dom::ImageData* imageData,
-                       ErrorResult& out_rv);
+                       ErrorResult&);
     void TexSubImage2D(GLenum texImageTarget, GLint level, GLint xOffset, GLint yOffset,
                        GLenum unpackFormat, GLenum unpackType, dom::Element* elem,
-                       ErrorResult* const out_rv);
+                       ErrorResult* const out_error);
 
     // Allow whatever element unpackTypes the bindings are willing to pass
     // us in Tex(Sub)Image2D
-    template<typename ElementT>
-    void TexImage2D(GLenum texImageTarget, GLint level, GLenum internalFormat,
-                    GLenum unpackFormat, GLenum unpackType, ElementT& elem,
-                    ErrorResult& out_rv)
+    template<typename T>
+    inline void
+    TexImage2D(GLenum texImageTarget, GLint level, GLenum internalFormat,
+               GLenum unpackFormat, GLenum unpackType, T& elem, ErrorResult& out_error)
     {
         TexImage2D(texImageTarget, level, internalFormat, unpackFormat, unpackType, &elem,
-                   &out_rv);
+                   &out_error);
     }
-    template<typename ElementT>
-    void TexSubImage2D(GLenum texImageTarget, GLint level, GLint xOffset, GLint yOffset,
-                       GLenum unpackFormat, GLenum unpackType, ElementT& elem,
-                       ErrorResult& 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)
     {
         TexSubImage2D(texImageTarget, level, xOffset, yOffset, unpackFormat, unpackType,
-                      &elem, &out_rv);
+                      &elem, &out_error);
     }
 
+    // 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);
@@ -1023,20 +1057,16 @@ private:
 
 // -----------------------------------------------------------------------------
 // PROTECTED
 protected:
     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;
     }
 
@@ -1071,44 +1101,44 @@ protected:
 
     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;
-    GLuint  mGLMax3DTextureSize;
-    GLuint  mGLMaxArrayTextureLayers;
+
+    // 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;
 
 public:
     GLuint MaxVertexAttribs() const {
         return mGLMaxVertexAttribs;
     }
 
     GLuint GLMaxTextureUnits() const {
         return mGLMaxTextureUnits;
     }
 
-
     bool IsFormatValidForFB(TexInternalFormat format) 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.
@@ -1238,57 +1268,55 @@ protected:
                                       uint32_t byteLength,
                                       WebGLTexImageFunc func,
                                       WebGLTexDimensions dims);
 
     bool ValidateUniformLocationForProgram(WebGLUniformLocation* location,
                                            WebGLProgram* program,
                                            const char* funcName);
 
-    bool ValidateCurFBForRead(const char* funcName, TexInternalFormat* const out_format,
+    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);
 
-    template<class ElementType>
+public:
     nsLayoutUtils::SurfaceFromElementResult
-    SurfaceFromElement(ElementType* element) {
-        MOZ_ASSERT(element);
+    SurfaceFromElement(dom::Element* elem)
+    {
+        uint32_t flags = nsLayoutUtils::SFE_WANT_IMAGE_SURFACE;
 
-        uint32_t flags = nsLayoutUtils::SFE_WANT_IMAGE_SURFACE;
-        if (mPixelStoreColorspaceConversion == LOCAL_GL_NONE)
+        if (mPixelStore_ColorspaceConversion == LOCAL_GL_NONE)
             flags |= nsLayoutUtils::SFE_NO_COLORSPACE_CONVERSION;
-        if (!mPixelStorePremultiplyAlpha)
+
+        if (!mPixelStore_PremultiplyAlpha)
             flags |= nsLayoutUtils::SFE_PREFER_NO_PREMULTIPLY_ALPHA;
 
-        return nsLayoutUtils::SurfaceFromElement(element, flags);
-    }
-
-    template<class ElementType>
-    nsLayoutUtils::SurfaceFromElementResult
-    SurfaceFromElement(ElementType& element) {
-       return SurfaceFromElement(&element);
+        gfx::DrawTarget* idealDrawTarget = nullptr; // Don't care for now.
+        return nsLayoutUtils::SurfaceFromElement(elem, flags, idealDrawTarget);
     }
 
     nsresult
-    SurfaceFromElementResultToImageSurface(nsLayoutUtils::SurfaceFromElementResult& res,
-                                           RefPtr<gfx::DataSourceSurface>& imageOut,
-                                           WebGLTexelFormat* format);
+    SurfaceFromElementResultToImageSurface(const nsLayoutUtils::SurfaceFromElementResult& res,
+                                           RefPtr<gfx::DataSourceSurface>* const out_image,
+                                           WebGLTexelFormat* const out_format);
 
+protected:
     // 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);
 
@@ -1317,35 +1345,26 @@ 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<WebGLSampler> > mBoundSamplers;
 
     void ResolveTexturesForDraw() const;
@@ -1377,24 +1396,37 @@ 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 mPixelStorePackAlignment;
-    uint32_t mPixelStoreUnpackAlignment;
-    uint32_t mPixelStoreColorspaceConversion;
-    bool mPixelStoreFlipY;
-    bool mPixelStorePremultiplyAlpha;
+    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;
 
+    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;
+
+public:
     // Fake-black
-public:
     class FakeBlackTexture {
         gl::GLContext* const mGL;
         GLuint mGLName;
 
     public:
         FakeBlackTexture(gl::GLContext* gl, TexTarget target, GLenum format);
         ~FakeBlackTexture();
         GLuint GLName() const { return mGLName; }
@@ -1405,17 +1437,17 @@ public:
 protected:
     bool mCanSkipFakeBlack;
 
     UniquePtr<FakeBlackTexture> mFakeBlack_2D_Opaque;
     UniquePtr<FakeBlackTexture> mFakeBlack_2D_Alpha;
     UniquePtr<FakeBlackTexture> mFakeBlack_CubeMap_Opaque;
     UniquePtr<FakeBlackTexture> mFakeBlack_CubeMap_Alpha;
 
-    void BindFakeBlackTextures();
+    bool BindFakeBlackTextures(const char* funcName);
     void UnbindFakeBlackTextures();
 
     // Generic Vertex Attributes
     UniquePtr<GLenum[]> mVertexAttribType;
     GLfloat mVertexAttrib0Vector[4];
     GLfloat mFakeVertexAttrib0BufferObjectVector[4];
     size_t mFakeVertexAttrib0BufferObjectSize;
     GLuint mFakeVertexAttrib0BufferObject;
@@ -1522,19 +1554,24 @@ 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() const = 0;
+
+    virtual UniquePtr<webgl::FormatUsageAuthority>
+    CreateFormatUsage(gl::GLContext* gl) const = 0;
 
     // Friend list
+    friend class ScopedUnpackReset;
+    friend class webgl::TexUnpackBytes;
+    friend class webgl::TexUnpackSurface;
     friend class WebGLTexture;
     friend class WebGLFramebuffer;
     friend class WebGLRenderbuffer;
     friend class WebGLProgram;
     friend class WebGLQuery;
     friend class WebGLBuffer;
     friend class WebGLSampler;
     friend class WebGLShader;
@@ -1619,29 +1656,103 @@ WebGLContext::ValidateObject(const char*
     if (!object) {
         ErrorInvalidValue("%s: null object passed as argument", info);
         return false;
     }
 
     return ValidateObjectAssumeNonNull(info, object);
 }
 
-size_t RoundUpToMultipleOf(size_t value, size_t multiple);
+// 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;
+}
 
 bool
-ValidateTexTarget(WebGLContext* webgl, GLenum rawTexTarget, const char* funcName,
+ValidateTexTarget(WebGLContext* webgl, const char* funcName, GLenum rawTexTarget,
                   TexTarget* const out_texTarget, WebGLTexture** const out_tex);
 bool
-ValidateTexImageTarget(WebGLContext* webgl, GLenum rawTexImageTarget,
-                       const char* funcName, TexImageTarget* const out_texImageTarget,
+ValidateTexImageTarget(WebGLContext* webgl, const char* funcName, uint8_t funcDims,
+                       GLenum rawTexImageTarget, TexImageTarget* const out_texImageTarget,
                        WebGLTexture** const out_tex);
 
-// Returns x rounded to the next highest multiple of y.
-CheckedUint32 RoundedToNextMultipleOf(CheckedUint32 x, CheckedUint32 y);
+
+bool
+GuessAlignmentFromStride(size_t width, size_t stride, size_t maxAlignment,
+                         size_t* const out_alignment);
+
+class UniqueBuffer
+{
+    void* mBuffer;
+
+public:
+    UniqueBuffer()
+        : mBuffer(nullptr)
+    { }
+
+    explicit UniqueBuffer(void* buffer)
+        : mBuffer(buffer)
+    { }
+
+    explicit 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();
+};
 
 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
@@ -110,17 +110,18 @@ WebGLContext::DrawArrays_check(GLint fir
     if (!DoFakeVertexAttrib0(checked_firstPlusCount.value())) {
         return false;
     }
 
     if (!DrawInstanced_check(info)) {
         return false;
     }
 
-    BindFakeBlackTextures();
+    if (!BindFakeBlackTextures(info))
+        return false;
 
     return true;
 }
 
 void
 WebGLContext::DrawArrays(GLenum mode, GLint first, GLsizei count)
 {
     if (IsContextLost())
@@ -291,17 +292,18 @@ WebGLContext::DrawElements_check(GLsizei
     if (!DoFakeVertexAttrib0(mMaxFetchedVertices)) {
         return false;
     }
 
     if (!DrawInstanced_check(info)) {
         return false;
     }
 
-    BindFakeBlackTextures();
+    if (!BindFakeBlackTextures(info))
+        return false;
 
     return true;
 }
 
 void
 WebGLContext::DrawElements(GLenum mode, GLsizei count, GLenum type,
                            WebGLintptr byteOffset)
 {
@@ -653,59 +655,59 @@ WebGLContext::UndoFakeVertexAttrib0()
     } else {
         gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
     }
 
     gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mBoundArrayBuffer ? mBoundArrayBuffer->mGLName : 0);
 }
 
 static bool
-BindFakeBlackHelper(gl::GLContext* gl, TexTarget target,
+BindFakeBlackHelper(const char* funcName, gl::GLContext* gl, TexTarget target,
                     const nsTArray<WebGLRefPtr<WebGLTexture>>& texArray,
                     WebGLContext::FakeBlackTexture* opaqueTex,
-                    WebGLContext::FakeBlackTexture* alphaTex)
+                    WebGLContext::FakeBlackTexture* alphaTex, bool* const out_wasNeeded)
 {
     MOZ_ASSERT(opaqueTex && alphaTex);
 
-    bool wasNeeded = false;
+    *out_wasNeeded = false;
 
     int32_t len = texArray.Length();
     for (int32_t i = 0; i < len; i++) {
         WebGLTexture* tex = texArray[i];
         if (!tex)
             continue;
 
         WebGLTextureFakeBlackStatus status;
-        if (!tex->ResolveFakeBlackStatus(&status))
-            return true; // Technically this should propagate up as a critical failure.
-                         // I believe this is currently harmless.
+        if (!tex->ResolveFakeBlackStatus(funcName, &status))
+            return false; // World is currently exploding...
+
         MOZ_ASSERT(status != WebGLTextureFakeBlackStatus::Unknown);
 
         if (MOZ_LIKELY(status == WebGLTextureFakeBlackStatus::NotNeeded))
             continue;
 
         // Ok, needs fake-black.
-        wasNeeded = true;
+        *out_wasNeeded = true;
 
         // Default to (0, 0, 0, 1) for incomplete textures, as well as uninit'd alpha-less
         // formats.
         WebGLContext::FakeBlackTexture* fakeTex = opaqueTex;
 
         if (status == WebGLTextureFakeBlackStatus::UninitializedImageData) {
             const auto& imageInfo = tex->BaseImageInfo();
-            if (FormatHasAlpha(imageInfo.mFormat)) {
+            if (imageInfo.mFormat->format->hasAlpha) {
                 fakeTex = alphaTex;
             }
         }
 
         gl->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
         gl->fBindTexture(target.get(), fakeTex->GLName());
     }
 
-    return wasNeeded;
+    return true;
 }
 
 static bool
 UnbindFakeBlackHelper(gl::GLContext* gl, TexTarget target,
                       const nsTArray<WebGLRefPtr<WebGLTexture>>& texArray)
 {
     bool changedActiveTex = false;
 
@@ -724,44 +726,59 @@ UnbindFakeBlackHelper(gl::GLContext* gl,
         changedActiveTex = true;
 
         gl->fBindTexture(target.get(), tex->mGLName);
     }
 
     return changedActiveTex;
 }
 
-void
-WebGLContext::BindFakeBlackTextures()
+bool
+WebGLContext::BindFakeBlackTextures(const char* funcName)
 {
     if (mCanSkipFakeBlack)
-        return;
+        return true;
 
     if (!mFakeBlack_2D_Opaque) {
         typedef FakeBlackTexture T;
 
         mFakeBlack_2D_Opaque = MakeUnique<T>(gl, LOCAL_GL_TEXTURE_2D, LOCAL_GL_RGB);
         mFakeBlack_2D_Alpha = MakeUnique<T>(gl, LOCAL_GL_TEXTURE_2D, LOCAL_GL_RGBA);
 
         mFakeBlack_CubeMap_Opaque = MakeUnique<T>(gl, LOCAL_GL_TEXTURE_CUBE_MAP,
                                                   LOCAL_GL_RGB);
         mFakeBlack_CubeMap_Alpha = MakeUnique<T>(gl, LOCAL_GL_TEXTURE_CUBE_MAP,
                                                  LOCAL_GL_RGBA);
     }
 
-    bool wasNeeded = false;
+    bool wasEverNeeded = false;
+    bool wasNeeded;
+
+    if (!BindFakeBlackHelper(funcName, gl, LOCAL_GL_TEXTURE_2D, mBound2DTextures,
+                             mFakeBlack_2D_Opaque.get(), mFakeBlack_2D_Alpha.get(),
+                             &wasNeeded))
+    {
+        goto FAIL;
+    }
+    wasEverNeeded |= wasNeeded;
 
-    wasNeeded |= BindFakeBlackHelper(gl, LOCAL_GL_TEXTURE_2D, mBound2DTextures,
-                                     mFakeBlack_2D_Opaque.get(),
-                                     mFakeBlack_2D_Alpha.get());
-    wasNeeded |= BindFakeBlackHelper(gl, LOCAL_GL_TEXTURE_CUBE_MAP, mBoundCubeMapTextures,
-                                     mFakeBlack_CubeMap_Opaque.get(),
-                                     mFakeBlack_CubeMap_Alpha.get());
+    if (!BindFakeBlackHelper(funcName, gl, LOCAL_GL_TEXTURE_CUBE_MAP,
+                             mBoundCubeMapTextures, mFakeBlack_CubeMap_Opaque.get(),
+                             mFakeBlack_CubeMap_Alpha.get(), &wasNeeded))
+    {
+        goto FAIL;
+    }
+    wasEverNeeded |= wasNeeded;
 
-    mCanSkipFakeBlack = !wasNeeded;
+    mCanSkipFakeBlack = !wasEverNeeded;
+    return true;
+
+FAIL:
+    ErrorOutOfMemory("%s: Failed to bind fake-black textures.");
+    return false;
 }
 
 void
 WebGLContext::UnbindFakeBlackTextures()
 {
     if (mCanSkipFakeBlack)
         return;
 
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -681,16 +681,30 @@ WebGLContext::GetBufferParameter(GLenum 
 
         default:
             ErrorInvalidEnumInfo("getBufferParameter: parameter", pname);
     }
 
     return JS::NullValue();
 }
 
+static JS::Value
+MissingAttachmentCausesInvalidOp(WebGLContext* webgl)
+{
+    webgl->ErrorInvalidOperation("getFramebufferAttachmentParameter: Valid pname, but"
+                                 " missing attachment.");
+    return JS::NullValue();
+}
+
+static JS::Value
+JSUint32Value(uint32_t val)
+{
+    return JS::NumberValue(val);
+}
+
 JS::Value
 WebGLContext::GetFramebufferAttachmentParameter(JSContext* cx,
                                                 GLenum target,
                                                 GLenum attachment,
                                                 GLenum pname,
                                                 ErrorResult& rv)
 {
     if (IsContextLost())
@@ -710,16 +724,17 @@ WebGLContext::GetFramebufferAttachmentPa
         fb = mBoundReadFramebuffer;
         break;
 
     default:
         MOZ_CRASH("Bad target.");
     }
 
     if (!fb) {
+        // This isn't actually true. GLES 3.0.4, p240: "If the default framebuffer[...]".
         ErrorInvalidOperation("getFramebufferAttachmentParameter: cannot query"
                               " framebuffer 0.");
         return JS::NullValue();
     }
 
     if (!ValidateFramebufferAttachment(fb, attachment,
                                        "getFramebufferAttachmentParameter"))
     {
@@ -732,176 +747,131 @@ WebGLContext::GetFramebufferAttachmentPa
     {
         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);
+    switch (pname) {
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE:
+        if (fba.Renderbuffer())
+            return JSUint32Value(LOCAL_GL_RENDERBUFFER);
+
+        if (fba.Texture())
+            return JSUint32Value(LOCAL_GL_TEXTURE);
+
+        return JSUint32Value(LOCAL_GL_NONE);
+
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME:
+        if (fba.Renderbuffer())
+            return WebGLObjectAsJSValue(cx, fba.Renderbuffer(), rv);
+
+        if (fba.Texture())
+            return WebGLObjectAsJSValue(cx, fba.Texture(), rv);
+
         return JS::NullValue();
-    } else if (fba.Texture()) {
-        switch (pname) {
-             case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT:
-                if (IsExtensionEnabled(WebGLExtensionID::EXT_sRGB)) {
-                    const TexInternalFormat effectiveInternalFormat =
-                        fba.Texture()->BaseImageInfo().mFormat;
-
-                    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()).mFormat;
-                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));
-            }
+    }
+
+
+    const bool hasAttachments = (fba.Renderbuffer() || fba.Texture());
+
+    switch (pname) {
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT:
+        if (!hasAttachments)
+            return MissingAttachmentCausesInvalidOp(this);
+
+        if (!IsWebGL2() && !IsExtensionEnabled(WebGLExtensionID::EXT_sRGB))
+            break;
+
+        if (!fba.IsDefined())
+            return JSUint32Value(LOCAL_GL_LINEAR);
+
+        if (fba.IsDefined()) {
+            if (fba.Format()->format->isSRGB)
+                return JSUint32Value(LOCAL_GL_SRGB_EXT);
+        }
+
+        return JSUint32Value(LOCAL_GL_LINEAR);
+
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE:
+        if (!hasAttachments)
+            return MissingAttachmentCausesInvalidOp(this);
+
+        if (!IsWebGL2() &&
+            !IsExtensionEnabled(WebGLExtensionID::EXT_color_buffer_half_float) &&
+            !IsExtensionEnabled(WebGLExtensionID::WEBGL_color_buffer_float))
+        {
+            break;
+        }
+
+        if (!fba.IsDefined())
+            return JSUint32Value(LOCAL_GL_LINEAR);
+
+        if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
+            ErrorInvalidOperation("getFramebufferAttachmentParameter: Cannot get component"
+                                  " type of a depth-stencil attachment.");
+            return JS::NullValue();
         }
 
-        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();
+        switch (fba.Format()->format->componentType) {
+        case webgl::ComponentType::Int:
+            return JSUint32Value(LOCAL_GL_INT);
+
+        case webgl::ComponentType::UInt:
+            return JSUint32Value(LOCAL_GL_UNSIGNED_INT);
+
+        case webgl::ComponentType::NormInt:
+            return JSUint32Value(LOCAL_GL_SIGNED_NORMALIZED);
+
+        case webgl::ComponentType::NormUInt:
+            return JSUint32Value(LOCAL_GL_UNSIGNED_NORMALIZED);
+
+        case webgl::ComponentType::Float:
+            return JSUint32Value(LOCAL_GL_FLOAT);
+
+        case webgl::ComponentType::None:
+            return JSUint32Value(LOCAL_GL_NONE);
         }
+        // Exhaustive switch means nothing's left.
+
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL:
+        if (!hasAttachments)
+            return MissingAttachmentCausesInvalidOp(this);
+
+        if (!fba.Texture())
+            break;
+
+        return JSUint32Value(fba.MipLevel());
+
+    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE:
+        if (!hasAttachments)
+            return MissingAttachmentCausesInvalidOp(this);
+
+        if (!fba.Texture())
+            break;
+
+        switch (fba.ImageTarget().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 JSUint32Value(fba.ImageTarget().get());
+        }
+
+        return JSUint32Value(0);
+
+    default:
+        break;
     }
 
+    ErrorInvalidEnumInfo("getFramebufferAttachmentParameter: pname", pname);
     return JS::NullValue();
 }
 
 JS::Value
 WebGLContext::GetRenderbufferParameter(GLenum target, GLenum pname)
 {
     if (IsContextLost())
         return JS::NullValue();
@@ -929,17 +899,17 @@ WebGLContext::GetRenderbufferParameter(G
         case LOCAL_GL_RENDERBUFFER_STENCIL_SIZE:
         {
             // 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());
+            return JS::NumberValue(mBoundRenderbuffer->GetInternalFormat());
         }
         default:
             ErrorInvalidEnumInfo("getRenderbufferParameter: parameter", pname);
     }
 
     return JS::NullValue();
 }
 
@@ -1182,147 +1152,248 @@ WebGLContext::LinkProgram(WebGLProgram* 
 }
 
 void
 WebGLContext::PixelStorei(GLenum pname, GLint param)
 {
     if (IsContextLost())
         return;
 
-    switch (pname) {
-        case UNPACK_FLIP_Y_WEBGL:
-            mPixelStoreFlipY = (param != 0);
+    if (IsWebGL2()) {
+        uint32_t* pValueSlot = nullptr;
+        switch (pname) {
+        case LOCAL_GL_UNPACK_IMAGE_HEIGHT:
+            pValueSlot = &mPixelStore_UnpackImageHeight;
             break;
-        case UNPACK_PREMULTIPLY_ALPHA_WEBGL:
-            mPixelStorePremultiplyAlpha = (param != 0);
+
+        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;
             break;
-        case UNPACK_COLORSPACE_CONVERSION_WEBGL:
-            if (param == LOCAL_GL_NONE || param == BROWSER_DEFAULT_WEBGL)
-                mPixelStoreColorspaceConversion = param;
-            else
-                return ErrorInvalidEnumInfo("pixelStorei: colorspace conversion parameter", param);
+
+        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;
             break;
-        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;
+
+        case LOCAL_GL_PACK_SKIP_PIXELS:
+            pValueSlot = &mPixelStore_PackSkipPixels;
+            break;
+        }
+
+        if (pValueSlot) {
+            if (param < 0) {
+                ErrorInvalidValue("pixelStorei: param must be >= 0.");
+                return;
+            }
+
             MakeContextCurrent();
             gl->fPixelStorei(pname, param);
-            break;
+            *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;
+
         default:
-            return ErrorInvalidEnumInfo("pixelStorei: parameter", pname);
+            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;
     }
+
+    ErrorInvalidEnumInfo("pixelStorei: parameter", pname);
 }
 
 // `width` in pixels.
 // `stride` in bytes.
-static bool
-SetFullAlpha(void* data, GLenum format, GLenum type, size_t width,
-             size_t height, size_t stride)
+static void
+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 true;
+        return;
     }
 
     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 true;
+        return;
     }
 
     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;
     }
 
-    if (readFormat == LOCAL_GL_RGBA &&
-        readType == LOCAL_GL_HALF_FLOAT &&
-        destFormat == LOCAL_GL_RGBA &&
-        destType == LOCAL_GL_FLOAT)
+    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)
     {
-        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]);
+        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);
 
         gl->fReadPixels(x, y, width, height, readFormat, readType, readBuffer.get());
 
-        size_t channelsPerRow = width * 4;
+        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();
+
         for (size_t j = 0; j < (size_t)height; j++) {
-            uint16_t* src = (uint16_t*)(readBuffer.get()) + j*readStride;
-            float* dst = (float*)(destBytes) + j*destStride;
-
-            uint16_t* srcEnd = src + channelsPerRow;
+            auto src = (const uint16_t*)srcRow;
+            auto dst = (float*)dstRow;
+
+            const auto srcEnd = src + channelsPerRow;
             while (src != srcEnd) {
                 *dst = unpackFromFloat16(*src);
-
                 ++src;
                 ++dst;
             }
+
+            srcRow += readStride.value();
+            dstRow += destStride.value();
         }
 
-        return;
+        return true;
     }
 
-    MOZ_CRASH("bad format/type");
+    gl->fReadPixels(x, y, width, height, destFormat, destType, destBytes);
+    return true;
 }
 
 static bool
 IsFormatAndTypeUnpackable(GLenum format, GLenum type)
 {
     switch (type) {
     case LOCAL_GL_UNSIGNED_BYTE:
     case LOCAL_GL_FLOAT:
@@ -1344,53 +1415,86 @@ 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 dom::ArrayBufferView& pixbuf = view.GetAsArrayBufferView();
-        pixbuf.ComputeLengthAndData();
-        *out_length = pixbuf.Length();
-        *out_data = pixbuf.Data();
-        *out_type = JS_GetArrayBufferViewType(pixbuf.Obj());
+        const auto& view2 = view.GetAsArrayBufferView();
+        view2.ComputeLengthAndData();
+
+        *out_length = view2.Length();
+        *out_data = view2.Data();
+        *out_type = JS_GetArrayBufferViewType(view2.Obj());
     } else {
-        const dom::SharedArrayBufferView& pixbuf = view.GetAsSharedArrayBufferView();
-        pixbuf.ComputeLengthAndData();
-        *out_length = pixbuf.Length();
-        *out_data = pixbuf.Data();
-        *out_type = JS_GetSharedArrayBufferViewType(pixbuf.Obj());
+        const auto& view2 = view.GetAsSharedArrayBufferView();
+        view2.ComputeLengthAndData();
+        *out_length = view2.Length();
+        *out_data = view2.Data();
+        *out_type = JS_GetSharedArrayBufferViewType(view2.Obj());
     }
 }
 
 void
-WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width,
-                         GLsizei height, GLenum format,
-                         GLenum type, const dom::Nullable<dom::ArrayBufferViewOrSharedArrayBufferView>& pixels,
-                         ErrorResult& rv)
+WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format,
+                         GLenum type,
+                         const dom::Nullable<dom::ArrayBufferViewOrSharedArrayBufferView>& pixels,
+                         ErrorResult& out_error)
 {
     if (IsContextLost())
         return;
 
     if (mCanvasElement &&
         mCanvasElement->IsWriteOnly() &&
         !nsContentUtils::IsCallerChrome())
     {
         GenerateWarning("readPixels: Not allowed");
-        return rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
+        out_error.Throw(NS_ERROR_DOM_SECURITY_ERR);
+        return;
     }
 
     if (width < 0 || height < 0)
         return ErrorInvalidValue("readPixels: negative size passed");
 
     if (pixels.IsNull())
         return ErrorInvalidValue("readPixels: null destination buffer");
 
@@ -1441,59 +1545,59 @@ WebGLContext::ReadPixels(GLint x, GLint 
         bytesPerPixel = 2*channels;
         requiredDataType = js::Scalar::Uint16;
         break;
 
     default:
         MOZ_CRASH("bad `type`");
     }
 
-    const dom::ArrayBufferViewOrSharedArrayBufferView &view = pixels.Value();
+    const auto& 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, &dataByteLen, &dataType);
+    ComputeLengthAndData(view, &data, &bytesAvailable, &dataType);
 
     // Check the pixels param type
     if (dataType != requiredDataType)
         return ErrorInvalidOperation("readPixels: Mismatched type/pixels types");
 
-    // 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");
+    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;
+    }
 
     if (!data) {
         ErrorOutOfMemory("readPixels: buffer storage is null. Did we run out of memory?");
-        return rv.Throw(NS_ERROR_OUT_OF_MEMORY);
+        out_error.Throw(NS_ERROR_OUT_OF_MEMORY);
+        return;
     }
 
     MakeContextCurrent();
 
-    TexInternalFormat srcFormat;
+    const webgl::FormatUsageInfo* srcFormat;
     uint32_t srcWidth;
     uint32_t srcHeight;
     if (!ValidateCurFBForRead("readPixels", &srcFormat, &srcWidth, &srcHeight))
         return;
 
-    const TexType srcType = TypeFromInternalFormat(srcFormat);
-    const bool isSrcTypeFloat = (srcType == LOCAL_GL_FLOAT ||
-                                 srcType == LOCAL_GL_HALF_FLOAT);
+    auto srcType = srcFormat->format->componentType;
+    const bool isSrcTypeFloat = (srcType == webgl::ComponentType::Float);
 
     // 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;
 
     GLenum auxReadFormat = mainReadFormat;
@@ -1509,103 +1613,99 @@ 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");
 
-    GLenum readType = type;
-    if (gl->WorkAroundDriverBugs() && gl->IsANGLE()) {
-        if (type == LOCAL_GL_FLOAT &&
-            auxReadFormat == format &&
-            auxReadType == LOCAL_GL_HALF_FLOAT)
-        {
-            readType = auxReadType;
-        }
+    // 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 == width && rwHeight == height) {
+        DoReadPixelsAndConvert(x, y, width, height, format, type, data, auxReadFormat,
+                               auxReadType);
+        return;
     }
 
-    // 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, srcWidth, srcHeight)) {
-        // 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 {
-        // 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 >= int32_t(srcWidth)
-            || x+width <= 0
-            || y >= int32_t(srcHeight)
-            || y+height <= 0)
-        {
-            // we are completely outside of range, can exit now with buffer filled with zeros
-            return DummyFramebufferOperation("readPixels");
+    // 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();
         }
-
-        // 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, int32_t(srcWidth));
-        GLsizei subrect_width  = subrect_end_x - subrect_x;
-
-        GLint   subrect_y      = std::max(y, 0);
-        GLint   subrect_end_y  = std::min(y+height, int32_t(srcHeight));
-        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");
+    } 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);
         }
-
-        // 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
+        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();
         }
     }
 
     // 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)
@@ -1616,20 +1716,17 @@ WebGLContext::ReadPixels(GLint x, GLint 
         needAlphaFilled = !mBoundReadFramebuffer->ColorAttachment(0).HasAlpha();
     } else {
         needAlphaFilled = !mOptions.alpha;
     }
 
     if (!needAlphaFilled)
         return;
 
-    size_t stride = checked_alignedRowSize.value(); // In bytes!
-    if (!SetFullAlpha(data, format, type, width, height, stride)) {
-        return rv.Throw(NS_ERROR_FAILURE);
-    }
+    SetFullAlpha(data, format, type, width, height, rowStride.value());
 }
 
 void
 WebGLContext::RenderbufferStorage_base(const char* funcName, GLenum target,
                                        GLsizei samples,
                                        GLenum internalFormat, GLsizei width,
                                        GLsizei height)
 {
@@ -1651,101 +1748,42 @@ WebGLContext::RenderbufferStorage_base(c
         return;
     }
 
     if (width < 0 || height < 0) {
         ErrorInvalidValue("%s: Width and height must be >= 0.", funcName);
         return;
     }
 
-    if (width > mGLMaxRenderbufferSize || height > mGLMaxRenderbufferSize) {
+    if (uint32_t(width) > mImplMaxRenderbufferSize ||
+        uint32_t(height) > mImplMaxRenderbufferSize)
+    {
         ErrorInvalidValue("%s: Width or height exceeds maximum renderbuffer"
                           " size.", funcName);
         return;
     }
 
-    // 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) {
+    const auto usage = mFormatUsage->GetRBUsage(internalFormat);
+    if (!usage) {
         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();
 
-    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);
+    GetAndFlushUnderlyingGLErrors();
+    mBoundRenderbuffer->RenderbufferStorage(samples, usage, width, height);
+    GLenum error = GetAndFlushUnderlyingGLErrors();
+    if (error) {
+        GenerateWarning("%s generated error %s", funcName,
+                        ErrorName(error));
+        return;
     }
-
-    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);
 }
@@ -1839,24 +1877,25 @@ WebGLContext::StencilOpSeparate(GLenum f
         !ValidateStencilOpEnum(dppass, "stencilOpSeparate: dppass"))
         return;
 
     MakeContextCurrent();
     gl->fStencilOpSeparate(face, sfail, dpfail, dppass);
 }
 
 nsresult
-WebGLContext::SurfaceFromElementResultToImageSurface(nsLayoutUtils::SurfaceFromElementResult& res,
-                                                     RefPtr<DataSourceSurface>& imageOut,
-                                                     WebGLTexelFormat* format)
+WebGLContext::SurfaceFromElementResultToImageSurface(const nsLayoutUtils::SurfaceFromElementResult& res,
+                                                     RefPtr<DataSourceSurface>* const out_image,
+                                                     WebGLTexelFormat* const out_format)
 {
-   *format = WebGLTexelFormat::None;
+    *out_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
@@ -1890,34 +1929,33 @@ WebGLContext::SurfaceFromElementResultTo
     }
 
     // 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
+            *out_format = WebGLTexelFormat::BGRA8; // careful, our ARGB means BGRA
             break;
         case SurfaceFormat::B8G8R8X8:
-            *format = WebGLTexelFormat::BGRX8; // careful, our RGB24 is not tightly packed. Whence BGRX8.
+            *out_format = WebGLTexelFormat::BGRX8; // careful, our RGB24 is not tightly packed. Whence BGRX8.
             break;
         case SurfaceFormat::A8:
-            *format = WebGLTexelFormat::A8;
+            *out_format = WebGLTexelFormat::A8;
             break;
         case SurfaceFormat::R5G6B5_UINT16:
-            *format = WebGLTexelFormat::RGB565;
+            *out_format = WebGLTexelFormat::RGB565;
             break;
         default:
             NS_ASSERTION(false, "Unsupported image format. Unimplemented.");
             return NS_ERROR_NOT_IMPLEMENTED;
     }
 
-    imageOut = data;
-
+    *out_image = data;
     return NS_OK;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // Uniform setters.
 
 void
 WebGLContext::Uniform1i(WebGLUniformLocation* loc, GLint a1)
--- a/dom/canvas/WebGLContextState.cpp
+++ b/dom/canvas/WebGLContextState.cpp
@@ -375,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(mGLMaxTextureSize);
+            return JS::Int32Value(mImplMaxTextureSize);
 
         case LOCAL_GL_MAX_CUBE_MAP_TEXTURE_SIZE:
-            return JS::Int32Value(mGLMaxCubeMapTextureSize);
+            return JS::Int32Value(mImplMaxCubeMapTextureSize);
 
         case LOCAL_GL_MAX_RENDERBUFFER_SIZE:
-            return JS::Int32Value(mGLMaxRenderbufferSize);
+            return JS::Int32Value(mImplMaxRenderbufferSize);
 
         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:
@@ -444,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(mPixelStoreFlipY);
+            return JS::BooleanValue(mPixelStore_FlipY);
         case UNPACK_PREMULTIPLY_ALPHA_WEBGL:
-            return JS::BooleanValue(mPixelStorePremultiplyAlpha);
+            return JS::BooleanValue(mPixelStore_PremultiplyAlpha);
 
         // uint, WebGL-specific
         case UNPACK_COLORSPACE_CONVERSION_WEBGL:
-            return JS::NumberValue(uint32_t(mPixelStoreColorspaceConversion));
+            return JS::NumberValue(uint32_t(mPixelStore_ColorspaceConversion));
 
         ////////////////////////////////
         // 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,17 +47,18 @@
 #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, GLenum rawTexTarget, TexTarget* const out)
+IsValidTexTarget(WebGLContext* webgl, GLenum rawTexTarget,
+                 TexTarget* const out)
 {
     switch (rawTexTarget) {
     case LOCAL_GL_TEXTURE_2D:
     case LOCAL_GL_TEXTURE_CUBE_MAP:
         break;
 
     case LOCAL_GL_TEXTURE_3D:
         if (!webgl->IsWebGL2())
@@ -69,45 +70,52 @@ IsValidTexTarget(WebGLContext* webgl, GL
         return false;
     }
 
     *out = rawTexTarget;
     return true;
 }
 
 static bool
-IsValidTexImageTarget(WebGLContext* webgl, GLenum rawTexImageTarget,
+IsValidTexImageTarget(WebGLContext* webgl, uint8_t funcDims, 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:
         if (!webgl->IsWebGL2())
             return false;
 
+        targetDims = 3;
         break;
 
     default:
         return false;
     }
 
+    if (targetDims != funcDims)
+        return false;
+
     *out = rawTexImageTarget;
     return true;
 }
 
 bool
-ValidateTexTarget(WebGLContext* webgl, GLenum rawTexTarget, const char* funcName,
+ValidateTexTarget(WebGLContext* webgl, const char* funcName, GLenum rawTexTarget,
                   TexTarget* const out_texTarget, WebGLTexture** const out_tex)
 {
     if (webgl->IsContextLost())
         return false;
 
     TexTarget texTarget;
     if (!IsValidTexTarget(webgl, rawTexTarget, &texTarget)) {
         webgl->ErrorInvalidEnum("%s: Invalid texTarget.", funcName);
@@ -121,25 +129,25 @@ ValidateTexTarget(WebGLContext* webgl, G
     }
 
     *out_texTarget = texTarget;
     *out_tex = tex;
     return true;
 }
 
 bool
-ValidateTexImageTarget(WebGLContext* webgl, GLenum rawTexImageTarget,
-                       const char* funcName, TexImageTarget* const out_texImageTarget,
+ValidateTexImageTarget(WebGLContext* webgl, const char* funcName, uint8_t funcDims,
+                       GLenum rawTexImageTarget, TexImageTarget* const out_texImageTarget,
                        WebGLTexture** const out_tex)
 {
     if (webgl->IsContextLost())
         return false;
 
     TexImageTarget texImageTarget;
-    if (!IsValidTexImageTarget(webgl, rawTexImageTarget, &texImageTarget)) {
+    if (!IsValidTexImageTarget(webgl, funcDims, 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;
@@ -214,30 +222,34 @@ WebGLContext::BindTexture(GLenum rawTarg
     }
 
     *currentTexPtr = newTex;
 }
 
 void
 WebGLContext::GenerateMipmap(GLenum rawTexTarget)
 {
+    const char funcName[] = "generateMipmap";
+
     TexTarget texTarget;
     WebGLTexture* tex;
-    if (!ValidateTexTarget(this, rawTexTarget, "texParameter", &texTarget, &tex))
+    if (!ValidateTexTarget(this, funcName, rawTexTarget, &texTarget, &tex))
         return;
 
     tex->GenerateMipmap(texTarget);
 }
 
 JS::Value
 WebGLContext::GetTexParameter(GLenum rawTexTarget, GLenum pname)
 {
+    const char funcName[] = "getTexParameter";
+
     TexTarget texTarget;
     WebGLTexture* tex;
-    if (!ValidateTexTarget(this, rawTexTarget, "texParameter", &texTarget, &tex))
+    if (!ValidateTexTarget(this, funcName, rawTexTarget, &texTarget, &tex))
         return JS::NullValue();
 
     if (!IsTexParamValid(pname)) {
         ErrorInvalidEnumInfo("getTexParameter: pname", pname);
         return JS::NullValue();
     }
 
     return tex->GetTexParameter(texTarget, pname);
@@ -256,214 +268,274 @@ WebGLContext::IsTexture(WebGLTexture* te
 }
 
 void
 WebGLContext::TexParameter_base(GLenum rawTexTarget, GLenum pname, GLint* maybeIntParam,
                                 GLfloat* maybeFloatParam)
 {
     MOZ_ASSERT(maybeIntParam || maybeFloatParam);
 
+    const char funcName[] = "texParameter";
+
     TexTarget texTarget;
     WebGLTexture* tex;
-    if (!ValidateTexTarget(this, rawTexTarget, "texParameter", &texTarget, &tex))
+    if (!ValidateTexTarget(this, funcName, rawTexTarget, &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& out_rv)
+                         ErrorResult&)
 {
-    TexImageTarget texImageTarget;
+    const char funcName[] = "texImage2D";
+    const uint8_t funcDims = 2;
+
+    TexImageTarget target;
     WebGLTexture* tex;
-    if (!ValidateTexImageTarget(this, rawTexImageTarget, "texImage2D", &texImageTarget,
+    if (!ValidateTexImageTarget(this, funcName, funcDims, rawTexImageTarget, &target,
                                 &tex))
     {
         return;
     }
 
-    tex->TexImage2D(texImageTarget, level, internalFormat, width, height, border,
-                    unpackFormat, unpackType, maybeView, &out_rv);
+    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);
 }
 
 void
 WebGLContext::TexImage2D(GLenum rawTexImageTarget, GLint level, GLenum internalFormat,
                          GLenum unpackFormat, GLenum unpackType,
-                         dom::ImageData* imageData, ErrorResult& out_rv)
+                         dom::ImageData* imageData, ErrorResult&)
 {
-    TexImageTarget texImageTarget;
+    const char funcName[] = "texImage2D";
+    const uint8_t funcDims = 2;
+
+    TexImageTarget target;
     WebGLTexture* tex;
-    if (!ValidateTexImageTarget(this, rawTexImageTarget, "texImage2D", &texImageTarget,
+    if (!ValidateTexImageTarget(this, funcName, funcDims, rawTexImageTarget, &target,
                                 &tex))
     {
         return;
     }
 
-    tex->TexImage2D(texImageTarget, level, internalFormat, unpackFormat, unpackType,
-                    imageData, &out_rv);
+    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);
 }
 
-
-//////////////////////////////////////////////////////////////////////////////////////////
-// TexSubImage
-
+void
+WebGLContext::TexImage2D(GLenum rawTexImageTarget, GLint level, GLenum internalFormat,
+                         GLenum unpackFormat, GLenum unpackType, dom::Element* elem,
+                         ErrorResult* const out_error)
+{
+    const char funcName[] = "texImage2D";
+    const uint8_t funcDims = 2;
 
-void
-WebGLContext::TexSubImage2D(GLenum rawTexImageTarget, GLint level, GLint xOffset,
-                            GLint yOffset, GLenum unpackFormat, GLenum unpackType,
-                            dom::Element* elem, ErrorResult* const out_rv)
-{
-    TexImageTarget texImageTarget;
+    TexImageTarget target;
     WebGLTexture* tex;
-    if (!ValidateTexImageTarget(this, rawTexImageTarget, "texSubImage2D", &texImageTarget,
+    if (!ValidateTexImageTarget(this, funcName, funcDims, rawTexImageTarget, &target,
                                 &tex))
     {
         return;
     }
 
-    tex->TexSubImage2D(texImageTarget, level, xOffset, yOffset, unpackFormat, unpackType,
-                       elem, out_rv);
+    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);
 }
 
+////////////////////////////////////////
+// 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& out_rv)
+                            ErrorResult&)
 {
-    TexImageTarget texImageTarget;
+    const char funcName[] = "texSubImage2D";
+    const uint8_t funcDims = 2;
+
+    TexImageTarget target;
     WebGLTexture* tex;
-    if (!ValidateTexImageTarget(this, rawTexImageTarget, "texSubImage2D", &texImageTarget,
+    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,
                                 &tex))
     {
         return;
     }
 
-    tex->TexSubImage2D(texImageTarget, level, xOffset, yOffset, width, height,
-                       unpackFormat, unpackType, maybeView, &out_rv);
+    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);
 }
 
 void
-WebGLContext::TexSubImage2D(GLenum rawTexImageTarget, GLint level, GLint xOffset, GLint yOffset,
-                            GLenum unpackFormat, GLenum unpackType, dom::ImageData* imageData,
-                            ErrorResult& out_rv)
+WebGLContext::TexSubImage2D(GLenum rawTexImageTarget, GLint level, GLint xOffset,
+                            GLint yOffset, GLenum unpackFormat, GLenum unpackType,
+                            dom::Element* elem, ErrorResult* const out_error)
 {
-    TexImageTarget texImageTarget;
+    const char funcName[] = "texSubImage2D";
+    const uint8_t funcDims = 2;
+
+    TexImageTarget target;
     WebGLTexture* tex;
-    if (!ValidateTexImageTarget(this, rawTexImageTarget, "texSubImage2D", &texImageTarget,
+    if (!ValidateTexImageTarget(this, funcName, funcDims, rawTexImageTarget, &target,
                                 &tex))
     {
         return;
     }
 
-    tex->TexSubImage2D(texImageTarget, level, xOffset, yOffset, unpackFormat, unpackType,
-                       imageData, &out_rv);
+    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);
 }
 
+////////////////////////////////////////
+// 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;
+
+    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)
 {
-    TexImageTarget texImageTarget;
+    const char funcName[] = "copyTexImage2D";
+    const uint8_t funcDims = 2;
+
+    TexImageTarget target;
     WebGLTexture* tex;
-    if (!ValidateTexImageTarget(this, rawTexImageTarget, "copyTexImage2D",
-                                &texImageTarget, &tex))
+    if (!ValidateTexImageTarget(this, funcName, funcDims, rawTexImageTarget, &target,
+                                &tex))
     {
         return;
     }
 
-    tex->CopyTexImage2D(texImageTarget, level, internalFormat, x, y, width, height,
-                        border);
+    tex->CopyTexImage2D(target, 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)
 {
-    TexImageTarget texImageTarget;
+    const char funcName[] = "copyTexSubImage2D";
+    const uint8_t funcDims = 2;
+
+    TexImageTarget target;
     WebGLTexture* tex;
-    if (!ValidateTexImageTarget(this, rawTexImageTarget, "copyTexSubImage2D",
-                                &texImageTarget, &tex))
+    if (!ValidateTexImageTarget(this, funcName, funcDims, rawTexImageTarget, &target,
+                                &tex))
     {
         return;
     }
 
-    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;
-    }
+    const GLint zOffset = 0;
 
-    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);
+    tex->CopyTexSubImage(funcName, target, level, xOffset, yOffset, zOffset, x, y, width,
+                         height);
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGLContextUtils.cpp
+++ b/dom/canvas/WebGLContextUtils.cpp
@@ -22,49 +22,16 @@
 #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;
-}
-
-bool
-FormatHasDepth(TexInternalFormat format)
-{
-    TexInternalFormat unsizedformat = UnsizedInternalFormatFromInternalFormat(format);
-    return unsizedformat == LOCAL_GL_DEPTH_COMPONENT ||
-           unsizedformat == LOCAL_GL_DEPTH_STENCIL;
-}
-
 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:
@@ -88,467 +55,16 @@ StringValue(JSContext* cx, const char* c
     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;
-}
-
-void
-CopyTexImageIntermediateFormatAndType(TexInternalFormat effectiveFormat,
-                                      TexFormat* const out_format,
-                                      TexType* const out_type)
-{
-    // GLES 3.0.4, p140, table 3.15 "ReadPixels `format` and `type` used during CopyTex*."
-    switch (effectiveFormat.get()) {
-    // Normalized fixed-point
-    case LOCAL_GL_R8:
-    case LOCAL_GL_RG8:
-    case LOCAL_GL_RGB8:
-    case LOCAL_GL_RGBA8:
-    case LOCAL_GL_ALPHA8:
-    case LOCAL_GL_LUMINANCE8:
-    case LOCAL_GL_LUMINANCE8_ALPHA8:
-
-    case LOCAL_GL_RGB565:
-    case LOCAL_GL_RGBA4:
-    case LOCAL_GL_RGB5_A1:
-
-    case LOCAL_GL_SRGB8:
-    case LOCAL_GL_SRGB8_ALPHA8:
-        *out_format = LOCAL_GL_RGBA;
-        *out_type   = LOCAL_GL_UNSIGNED_BYTE;
-        return;
-
-    // 10-bit normalized fixed-point
-    case LOCAL_GL_RGB10_A2:
-        *out_format = LOCAL_GL_RGBA;
-        *out_type   = LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV;
-        return;
-
-    // Signed integer.
-    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:
-        *out_format = LOCAL_GL_RGBA;
-        *out_type   = LOCAL_GL_INT;
-        return;
-
-    // Unsigned integer.
-    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:
-        *out_format = LOCAL_GL_RGBA;
-        *out_type   = LOCAL_GL_UNSIGNED_INT;
-        return;
-
-    // Float and half-float are added by EXT_color_buffer_float.
-    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:
-        *out_format = LOCAL_GL_RGBA;
-        *out_type   = LOCAL_GL_FLOAT;
-        return;
-
-    // Non-color-renderable
-    case LOCAL_GL_R8_SNORM:
-    case LOCAL_GL_RG8_SNORM:
-    case LOCAL_GL_RGB8_SNORM:
-    case LOCAL_GL_RGBA8_SNORM:
-    case LOCAL_GL_R11F_G11F_B10F:
-    case LOCAL_GL_RGB9_E5:
-
-    // Unsupported
-    case LOCAL_GL_DEPTH_COMPONENT16:
-    case LOCAL_GL_DEPTH_COMPONENT24:
-    case LOCAL_GL_DEPTH24_STENCIL8:
-    case LOCAL_GL_DEPTH_COMPONENT32F:
-    case LOCAL_GL_DEPTH32F_STENCIL8:
-    default:
-        MOZ_CRASH("Bad `effectiveFormat`.");
-    }
-}
-
-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;
-}
-
-/**
- * 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);
 
@@ -593,37 +109,16 @@ 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.
@@ -1307,18 +802,29 @@ 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, mPixelStorePackAlignment);
-    AssertUintParamCorrect(gl, LOCAL_GL_UNPACK_ALIGNMENT, mPixelStoreUnpackAlignment);
+    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);
+    }
 
     MOZ_ASSERT(!GetAndFlushUnderlyingGLErrors());
 #endif
 }
 
 const char*
 InfoFrom(WebGLTexImageFunc func, WebGLTexDimensions dims)
 {
--- a/dom/canvas/WebGLContextUtils.h
+++ b/dom/canvas/WebGLContextUtils.h
@@ -10,48 +10,16 @@
 
 #include "mozilla/Assertions.h"
 #include "mozilla/dom/BindingUtils.h"
 
 #include "WebGLStrongTypes.h"
 
 namespace mozilla {
 
-bool IsGLDepthFormat(TexInternalFormat format);
-bool IsGLDepthStencilFormat(TexInternalFormat format);
-bool FormatHasAlpha(TexInternalFormat format);
-bool FormatHasDepth(TexInternalFormat format);
-
-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 CopyTexImageIntermediateFormatAndType(TexInternalFormat effectiveFormat,
-                                           TexFormat* const out_format,
-                                           TexType* const out_type);
-
-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.
@@ -59,19 +27,16 @@ size_t GetBitsPerTexel(TexInternalFormat
 // 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),
@@ -94,17 +59,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->Context());
+    MOZ_ASSERT(this == object->mContext);
     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
@@ -516,918 +516,16 @@ 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 (!IsPowerOfTwo(width) ||
-            !IsPowerOfTwo(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 (!IsPowerOfTwo(width)) {
-                ErrorInvalidValue("%s: For level > 0, width of %d must be a"
-                                  " power of two.", InfoFrom(func, dims),
-                                  width);
-                return false;
-            }
-
-            if (!IsPowerOfTwo(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() && !IsPowerOfTwo(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(TexInternalFormat srcFormat,
-                                   TexInternalFormat dstformat, 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 (srcFormat) is a superset of the format
-    // requested by the CopyTex[Sub]Image2D functions (dstFormat).
-    const GLComponents srcComps = GLComponents(srcFormat);
-    const GLComponents dstComps = GLComponents(dstformat);
-    if (!dstComps.IsSubsetOf(srcComps)) {
-        ErrorInvalidOperation("%s: Format %s is not a subset of the current"
-                              " framebuffer format, which is %s.",
-                              InfoFrom(func, dims), EnumName(dstformat.get()),
-                              EnumName(srcFormat.get()));
-        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)) {
-        const auto& imageInfo = tex->ImageInfoAt(texImageTarget, level);
-        if (!imageInfo.IsDefined()) {
-            ErrorInvalidOperation("%s: No texture image previously defined for"
-                                  " target %s at level %d.", info,
-                                  WebGLContext::EnumName(texImageTarget.get()),
-                                                         level);
-            return false;
-        }
-
-        if (!ValidateTexSubImageSize(xoffset, yoffset, zoffset, width, height,
-                                     depth, imageInfo.mWidth,
-                                     imageInfo.mHeight, 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.
      */
@@ -1653,17 +751,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();
+    mFormatUsage = CreateFormatUsage(gl);
 
     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;
     }
 
@@ -1768,49 +866,64 @@ WebGLContext::InitAndValidateGL()
         return false;
     }
 
     mBound2DTextures.SetLength(mGLMaxTextureUnits);
     mBoundCubeMapTextures.SetLength(mGLMaxTextureUnits);
     mBound3DTextures.SetLength(mGLMaxTextureUnits);
     mBoundSamplers.SetLength(mGLMaxTextureUnits);
 
+    ////////////////
+
     if (MinCapabilityMode()) {
-        mGLMaxTextureSize = MINVALUE_GL_MAX_TEXTURE_SIZE;
-        mGLMaxCubeMapTextureSize = MINVALUE_GL_MAX_CUBE_MAP_TEXTURE_SIZE;
-        mGLMaxRenderbufferSize = MINVALUE_GL_MAX_RENDERBUFFER_SIZE;
+        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;
+
         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, &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_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_IMAGE_UNITS, &mGLMaxTextureImageUnits);
         gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &mGLMaxVertexTextureImageUnits);
 
         if (!gl->GetPotentialInteger(LOCAL_GL_MAX_SAMPLES, (GLint*)&mGLMaxSamples))
             mGLMaxSamples = 1;
     }
 
-    // Calculate log2 of mGLMaxTextureSize and mGLMaxCubeMapTextureSize
-    mGLMaxTextureSizeLog2 = 0;
-    int32_t tempSize = mGLMaxTextureSize;
-    while (tempSize >>= 1) {
-        ++mGLMaxTextureSizeLog2;
-    }
+    const auto fnFloor = [](uint32_t& val) {
+        if (val) {
+            val = FloorPOT(val);
+        }
+    };
 
-    mGLMaxCubeMapTextureSizeLog2 = 0;
-    tempSize = mGLMaxCubeMapTextureSize;
-    while (tempSize >>= 1) {
-        ++mGLMaxCubeMapTextureSizeLog2;
-    }
+    fnFloor(mImplMaxTextureSize);
+    fnFloor(mImplMaxCubeMapTextureSize);
+    fnFloor(mImplMaxRenderbufferSize);
 
-    mGLMaxTextureSize = FloorPOT(mGLMaxTextureSize);
-    mGLMaxRenderbufferSize = FloorPOT(mGLMaxRenderbufferSize);
+    fnFloor(mImplMax3DTextureSize);
+    fnFloor(mImplMaxArrayTextureLayers);
+
+    ////////////////
+
+    mGLMaxColorAttachments = 1;
+    mGLMaxDrawBuffers = 1;
 
     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);
@@ -1931,16 +1044,32 @@ 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/WebGLExtensionColorBufferFloat.cpp
+++ b/dom/canvas/WebGLExtensionColorBufferFloat.cpp
@@ -4,42 +4,42 @@
 
 #include "WebGLExtensions.h"
 
 #include "GLContext.h"
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "WebGLContext.h"
 #include "WebGLFormats.h"
 
-namespace mozilla {
+#ifdef FOO
+#error FOO is already defined! We use FOO() macros to keep things succinct in this file.
+#endif
 
-using mozilla::webgl::EffectiveFormat;
+namespace mozilla {
 
 WebGLExtensionColorBufferFloat::WebGLExtensionColorBufferFloat(WebGLContext* webgl)
     : WebGLExtensionBase(webgl)
 {
     MOZ_ASSERT(IsSupported(webgl), "Don't construct extension if unsupported.");
 
-    webgl::FormatUsageAuthority* authority = webgl->mFormatUsage.get();
+    auto& fua = webgl->mFormatUsage;
 
-    auto updateUsage = [authority](EffectiveFormat effectiveFormat) {
-        webgl::FormatUsageInfo* usage = authority->GetUsage(effectiveFormat);
-        MOZ_ASSERT(usage);
-        usage->asRenderbuffer = usage->isRenderable = true;
+    auto fnUpdateUsage = [&fua](GLenum sizedFormat, webgl::EffectiveFormat effFormat) {
+        auto usage = fua->EditUsage(effFormat);
+        usage->isRenderable = true;
+
+        fua->AddRBFormat(sizedFormat, usage);
     };
 
-    // Ensure require formats are initialized.
-    WebGLExtensionTextureFloat::InitWebGLFormats(authority);
+#define FOO(x) fnUpdateUsage(LOCAL_GL_ ## x, webgl::EffectiveFormat::x)
 
-    // Update usage to allow asRenderbuffer and isRenderable
-    updateUsage(EffectiveFormat::RGBA32F);
-    updateUsage(EffectiveFormat::RGB32F);
-    updateUsage(EffectiveFormat::Luminance32FAlpha32F);
-    updateUsage(EffectiveFormat::Luminance32F);
-    updateUsage(EffectiveFormat::Alpha32F);
+    FOO(RGBA32F);
+    FOO(RGB32F);
+
+#undef FOO
 }
 
 WebGLExtensionColorBufferFloat::~WebGLExtensionColorBufferFloat()
 {
 }
 
 bool
 WebGLExtensionColorBufferFloat::IsSupported(const WebGLContext* webgl)
--- a/dom/canvas/WebGLExtensionColorBufferHalfFloat.cpp
+++ b/dom/canvas/WebGLExtensionColorBufferHalfFloat.cpp
@@ -4,42 +4,42 @@
 
 #include "WebGLExtensions.h"
 
 #include "GLContext.h"
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "WebGLContext.h"
 #include "WebGLFormats.h"
 
-namespace mozilla {
+#ifdef FOO
+#error FOO is already defined! We use FOO() macros to keep things succinct in this file.
+#endif
 
-using mozilla::webgl::EffectiveFormat;
+namespace mozilla {
 
 WebGLExtensionColorBufferHalfFloat::WebGLExtensionColorBufferHalfFloat(WebGLContext* webgl)
     : WebGLExtensionBase(webgl)
 {
     MOZ_ASSERT(IsSupported(webgl), "Don't construct extension if unsupported.");
 
-    webgl::FormatUsageAuthority* authority = webgl->mFormatUsage.get();
+    auto& fua = webgl->mFormatUsage;
 
-    auto updateUsage = [authority](EffectiveFormat effectiveFormat) {
-        webgl::FormatUsageInfo* usage = authority->GetUsage(effectiveFormat);
-        MOZ_ASSERT(usage);
-        usage->asRenderbuffer = usage->isRenderable = true;
+    auto fnUpdateUsage = [&fua](GLenum sizedFormat, webgl::EffectiveFormat effFormat) {
+        auto usage = fua->EditUsage(effFormat);
+        usage->isRenderable = true;
+
+        fua->AddRBFormat(sizedFormat, usage);
     };
 
-    // Ensure require formats are initialized.
-    WebGLExtensionTextureHalfFloat::InitWebGLFormats(authority);
+#define FOO(x) fnUpdateUsage(LOCAL_GL_ ## x, webgl::EffectiveFormat::x)
 
-    // Update usage to allow asRenderbuffer and isRenderable
-    updateUsage(EffectiveFormat::RGBA16F);
-    updateUsage(EffectiveFormat::RGB16F);
-    updateUsage(EffectiveFormat::Luminance16FAlpha16F);
-    updateUsage(EffectiveFormat::Luminance16F);
-    updateUsage(EffectiveFormat::Alpha16F);
+    FOO(RGBA16F);
+    FOO(RGB16F);
+
+#undef FOO
 }
 
 WebGLExtensionColorBufferHalfFloat::~WebGLExtensionColorBufferHalfFloat()
 {
 }
 
 bool
 WebGLExtensionColorBufferHalfFloat::IsSupported(const WebGLContext* webgl)
--- a/dom/canvas/WebGLExtensionCompressedTextureATC.cpp
+++ b/dom/canvas/WebGLExtensionCompressedTextureATC.cpp
@@ -2,24 +2,41 @@
  * 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)
 {
-    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);
+    const auto fnAdd = [webgl](GLenum sizedFormat, webgl::EffectiveFormat effFormat) {
+        auto& fua = webgl->mFormatUsage;
+
+        auto usage = fua->EditUsage(effFormat);
+        fua->AddSizedTexFormat(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
 }
 
 WebGLExtensionCompressedTextureATC::~WebGLExtensionCompressedTextureATC()
 {
 }
 
 IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionCompressedTextureATC, WEBGL_compressed_texture_atc)
 
--- a/dom/canvas/WebGLExtensionCompressedTextureETC1.cpp
+++ b/dom/canvas/WebGLExtensionCompressedTextureETC1.cpp
@@ -2,22 +2,39 @@
  * 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)
 {
-    webgl->mCompressedTextureFormats.AppendElement(LOCAL_GL_ETC1_RGB8_OES);
+    const auto fnAdd = [webgl](GLenum sizedFormat, webgl::EffectiveFormat effFormat) {
+        auto& fua = webgl->mFormatUsage;
+
+        auto usage = fua->EditUsage(effFormat);
+        fua->AddSizedTexFormat(sizedFormat, usage);
+
+        webgl->mCompressedTextureFormats.AppendElement(sizedFormat);
+    };
+
+#define FOO(x) LOCAL_GL_ ## x, webgl::EffectiveFormat::x
+
+    fnAdd(FOO(ETC1_RGB8_OES));
+
+#undef FOO
 }
 
 WebGLExtensionCompressedTextureETC1::~WebGLExtensionCompressedTextureETC1()
 {
 }
 
 IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionCompressedTextureETC1, WEBGL_compressed_texture_etc1)
 
--- a/dom/canvas/WebGLExtensionCompressedTexturePVRTC.cpp
+++ b/dom/canvas/WebGLExtensionCompressedTexturePVRTC.cpp
@@ -2,25 +2,42 @@
  * 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)
 {
-    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);
+    const auto fnAdd = [webgl](GLenum sizedFormat, webgl::EffectiveFormat effFormat) {
+        auto& fua = webgl->mFormatUsage;
+
+        auto usage = fua->EditUsage(effFormat);
+        fua->AddSizedTexFormat(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
 }
 
 WebGLExtensionCompressedTexturePVRTC::~WebGLExtensionCompressedTexturePVRTC()
 {
 }
 
 IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionCompressedTexturePVRTC, WEBGL_compressed_texture_pvrtc)
 
--- a/dom/canvas/WebGLExtensionCompressedTextureS3TC.cpp
+++ b/dom/canvas/WebGLExtensionCompressedTextureS3TC.cpp
@@ -2,25 +2,42 @@
  * 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)
 {
-    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);
+    const auto fnAdd = [webgl](GLenum sizedFormat, webgl::EffectiveFormat effFormat) {
+        auto& fua = webgl->mFormatUsage;
+
+        auto usage = fua->EditUsage(effFormat);
+        fua->AddSizedTexFormat(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
 }
 
 WebGLExtensionCompressedTextureS3TC::~WebGLExtensionCompressedTextureS3TC()
 {
 }
 
 IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionCompressedTextureS3TC, WEBGL_compressed_texture_s3tc)
 
--- a/dom/canvas/WebGLExtensionDepthTexture.cpp
+++ b/dom/canvas/WebGLExtensionDepthTexture.cpp
@@ -8,16 +8,37 @@
 #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);
+        const webgl::PackingInfo pi = {unpackFormat, unpackType};
+        const webgl::DriverUnpackInfo dui = {unpackFormat, unpackFormat, unpackType};
+
+        fua->AddUnsizedTexFormat(pi, usage);
+        usage->AddUnpack(pi, dui);
+    };
+
+    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/WebGLExtensionSRGB.cpp
+++ b/dom/canvas/WebGLExtensionSRGB.cpp
@@ -7,46 +7,52 @@
 
 #include "GLContext.h"
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "WebGLContext.h"
 #include "WebGLFormats.h"
 
 namespace mozilla {
 
-using mozilla::webgl::EffectiveFormat;
-
-
 WebGLExtensionSRGB::WebGLExtensionSRGB(WebGLContext* webgl)
     : WebGLExtensionBase(webgl)
 {
     MOZ_ASSERT(IsSupported(webgl), "Don't construct extension if unsupported.");
 
     gl::GLContext* gl = webgl->GL();
     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);
     }
 
-    webgl::FormatUsageAuthority* authority = webgl->mFormatUsage.get();
+    auto& fua = webgl->mFormatUsage;
+
+    webgl::FormatUsageInfo* usage;
+    webgl::PackingInfo pi;
+    webgl::DriverUnpackInfo dui;
 
-    auto addFormatIfMissing = [authority](EffectiveFormat effectiveFormat,
-                                          GLenum unpackFormat, GLenum unpackType,
-                                          bool asRenderbuffer)
-        {
-            if (!authority->GetUsage(effectiveFormat)) {
-                authority->AddFormat(effectiveFormat, asRenderbuffer, asRenderbuffer, true, true);
-                authority->AddUnpackOption(unpackFormat, unpackType, effectiveFormat);
-            }
-        };
+    usage = fua->EditUsage(webgl::EffectiveFormat::SRGB8);
+    usage->isRenderable = false;
+    usage->isFilterable = true;
+    pi = {LOCAL_GL_SRGB, LOCAL_GL_UNSIGNED_BYTE};
+    dui = {LOCAL_GL_SRGB, LOCAL_GL_SRGB, LOCAL_GL_UNSIGNED_BYTE};
+    fua->AddUnsizedTexFormat(pi, usage);
+    usage->AddUnpack(pi, dui);
 
-    addFormatIfMissing(EffectiveFormat::SRGB8       , LOCAL_GL_SRGB      , LOCAL_GL_UNSIGNED_BYTE, false);
-    addFormatIfMissing(EffectiveFormat::SRGB8_ALPHA8, LOCAL_GL_SRGB_ALPHA, LOCAL_GL_UNSIGNED_BYTE, true);
+    usage = fua->EditUsage(webgl::EffectiveFormat::SRGB8_ALPHA8);
+    usage->isRenderable = true;
+    usage->isFilterable = true;
+    pi = {LOCAL_GL_SRGB_ALPHA, LOCAL_GL_UNSIGNED_BYTE};
+    dui = {LOCAL_GL_SRGB_ALPHA, LOCAL_GL_SRGB_ALPHA, LOCAL_GL_UNSIGNED_BYTE};
+    fua->AddUnsizedTexFormat(pi, usage);
+    usage->AddUnpack(pi, dui);
+
+    fua->AddRBFormat(LOCAL_GL_SRGB_ALPHA, usage);
 }
 
 WebGLExtensionSRGB::~WebGLExtensionSRGB()
 {
 }
 
 bool
 WebGLExtensionSRGB::IsSupported(const WebGLContext* webgl)
--- a/dom/canvas/WebGLExtensionTextureFloat.cpp
+++ b/dom/canvas/WebGLExtensionTextureFloat.cpp
@@ -1,65 +1,59 @@
 /* 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 "WebGLExtensions.h"
 
+#include "GLContext.h"
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "WebGLContext.h"
 #include "WebGLFormats.h"
 
 namespace mozilla {
 
-using mozilla::webgl::EffectiveFormat;
-
-void
-WebGLExtensionTextureFloat::InitWebGLFormats(webgl::FormatUsageAuthority* authority)
-{
-    MOZ_ASSERT(authority);
-
-    auto addFormatIfMissing = [authority](EffectiveFormat effectiveFormat)
-        {
-            if (!authority->GetUsage(effectiveFormat)) {
-                authority->AddFormat(effectiveFormat, false, false, false, false);
-            }
-        };
-
-    // Populate authority with any missing effective formats.
-    addFormatIfMissing(EffectiveFormat::RGBA32F);
-    addFormatIfMissing(EffectiveFormat::RGB32F);
-    addFormatIfMissing(EffectiveFormat::Luminance32FAlpha32F);
-    addFormatIfMissing(EffectiveFormat::Luminance32F);
-    addFormatIfMissing(EffectiveFormat::Alpha32F);
-}
-
 WebGLExtensionTextureFloat::WebGLExtensionTextureFloat(WebGLContext* webgl)
     : WebGLExtensionBase(webgl)
 {
-    webgl::FormatUsageAuthority* authority = webgl->mFormatUsage.get();
+    auto& fua = webgl->mFormatUsage;
+
+    webgl::PackingInfo pi;
+    webgl::DriverUnpackInfo dui;
+
+    const auto fnAdd = [&fua, &pi, &dui](webgl::EffectiveFormat effFormat) {
+        auto usage = fua->EditUsage(effFormat);
+        fua->AddUnsizedTexFormat(pi, usage);
+        usage->AddUnpack(pi, dui);
+    };
+
+    const bool isCore = webgl->GL()->IsCoreProfile();
+
+    pi = {LOCAL_GL_RGBA, LOCAL_GL_FLOAT};
+    dui = {LOCAL_GL_RGBA, LOCAL_GL_RGBA, LOCAL_GL_FLOAT};
+    fnAdd(webgl::EffectiveFormat::RGBA32F);
 
-    auto updateUsage = [authority](EffectiveFormat effectiveFormat,
-                                   GLenum unpackFormat, GLenum unpackType)
-        {
-            webgl::FormatUsageInfo* usage = authority->GetUsage(effectiveFormat);
-            MOZ_ASSERT(usage);
-            usage->asTexture = true;
-            authority->AddUnpackOption(unpackFormat, unpackType, effectiveFormat);
-        };
+    pi = {LOCAL_GL_RGB, LOCAL_GL_FLOAT};
+    dui = {LOCAL_GL_RGB, LOCAL_GL_RGB, LOCAL_GL_FLOAT};
+    fnAdd(webgl::EffectiveFormat::RGB32F);
+
+    pi = {LOCAL_GL_LUMINANCE, LOCAL_GL_FLOAT};
+    if (isCore) dui = {LOCAL_GL_R32F, LOCAL_GL_RED, LOCAL_GL_FLOAT};
+    else        dui = {LOCAL_GL_LUMINANCE, LOCAL_GL_LUMINANCE, LOCAL_GL_FLOAT};
+    fnAdd(webgl::EffectiveFormat::Luminance32F);
 
-    // Ensure require formats are initialized.
-    InitWebGLFormats(authority);
+    pi = {LOCAL_GL_ALPHA, LOCAL_GL_FLOAT};
+    if (isCore) dui = {LOCAL_GL_R32F, LOCAL_GL_RED, LOCAL_GL_FLOAT};
+    else        dui = {LOCAL_GL_ALPHA, LOCAL_GL_ALPHA, LOCAL_GL_FLOAT};
+    fnAdd(webgl::EffectiveFormat::Alpha32F);
 
-    // Update usage to allow asTexture and add unpack
-    updateUsage(EffectiveFormat::RGBA32F             , LOCAL_GL_RGBA           , LOCAL_GL_FLOAT);
-    updateUsage(EffectiveFormat::RGB32F              , LOCAL_GL_RGB            , LOCAL_GL_FLOAT);
-    updateUsage(EffectiveFormat::Luminance32FAlpha32F, LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_FLOAT);
-    updateUsage(EffectiveFormat::Luminance32F        , LOCAL_GL_LUMINANCE      , LOCAL_GL_FLOAT);
-    updateUsage(EffectiveFormat::Alpha32F            , LOCAL_GL_ALPHA          , LOCAL_GL_FLOAT);
+    pi = {LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_FLOAT};
+    if (isCore) dui = {LOCAL_GL_RG32F, LOCAL_GL_RG, LOCAL_GL_FLOAT};
+    else        dui = {LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_FLOAT};
+    fnAdd(webgl::EffectiveFormat::Luminance32FAlpha32F);
 }
 
 WebGLExtensionTextureFloat::~WebGLExtensionTextureFloat()
 {
 }
 
 IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionTextureFloat, OES_texture_float)
 
--- a/dom/canvas/WebGLExtensionTextureFloatLinear.cpp
+++ b/dom/canvas/WebGLExtensionTextureFloatLinear.cpp
@@ -5,42 +5,26 @@
 #include "WebGLExtensions.h"
 
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "WebGLContext.h"
 #include "WebGLFormats.h"
 
 namespace mozilla {
 
-using mozilla::webgl::EffectiveFormat;
-
 WebGLExtensionTextureFloatLinear::WebGLExtensionTextureFloatLinear(WebGLContext* webgl)
     : WebGLExtensionBase(webgl)
 {
-    // This update requires that the authority already be populated by
-    // WebGLExtensionTextureFloat.  Enabling extensions to control
-    // features is a mess in WebGL
-
-    webgl::FormatUsageAuthority* authority = webgl->mFormatUsage.get();
+    auto& fua = webgl->mFormatUsage;
 
-    auto updateUsage = [authority](EffectiveFormat effectiveFormat) {
-        webgl::FormatUsageInfo* usage = authority->GetUsage(effectiveFormat);
-        MOZ_ASSERT(usage);
-        usage->isFilterable = true;
-    };
-
-    // Ensure require formats are initialized.
-    WebGLExtensionTextureFloat::InitWebGLFormats(authority);
-
-    // Update usage to allow isFilterable
-    updateUsage(EffectiveFormat::RGBA32F);
-    updateUsage(EffectiveFormat::RGB32F);
-    updateUsage(EffectiveFormat::Luminance32FAlpha32F);
-    updateUsage(EffectiveFormat::Luminance32F);
-    updateUsage(EffectiveFormat::Alpha32F);
+    fua->EditUsage(webgl::EffectiveFormat::RGBA32F)->isFilterable = true;
+    fua->EditUsage(webgl::EffectiveFormat::RGB32F)->isFilterable = true;
+    fua->EditUsage(webgl::EffectiveFormat::Luminance32FAlpha32F)->isFilterable = true;
+    fua->EditUsage(webgl::EffectiveFormat::Luminance32F)->isFilterable = true;
+    fua->EditUsage(webgl::EffectiveFormat::Alpha32F)->isFilterable = true;
 }
 
 WebGLExtensionTextureFloatLinear::~WebGLExtensionTextureFloatLinear()
 {
 }
 
 IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionTextureFloatLinear, OES_texture_float_linear)
 
--- a/dom/canvas/WebGLExtensionTextureHalfFloat.cpp
+++ b/dom/canvas/WebGLExtensionTextureHalfFloat.cpp
@@ -1,64 +1,65 @@
 /* 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 "WebGLExtensions.h"
 
+#include "GLContext.h"
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "WebGLContext.h"
 #include "WebGLFormats.h"
 
 namespace mozilla {
 
-using mozilla::webgl::EffectiveFormat;
-
-void
-WebGLExtensionTextureHalfFloat::InitWebGLFormats(webgl::FormatUsageAuthority* authority)
-{
-    MOZ_ASSERT(authority);
-
-    auto addFormatIfMissing = [authority](EffectiveFormat effectiveFormat)
-        {
-            if (!authority->GetUsage(effectiveFormat)) {
-                authority->AddFormat(effectiveFormat, false, false, false, false);
-            }
-        };
-
-    // Populate authority with any missing effective formats.
-    addFormatIfMissing(EffectiveFormat::RGBA16F);
-    addFormatIfMissing(EffectiveFormat::RGB16F);
-    addFormatIfMissing(EffectiveFormat::Luminance16FAlpha16F);
-    addFormatIfMissing(EffectiveFormat::Luminance16F);
-    addFormatIfMissing(EffectiveFormat::Alpha16F);
-}
-
 WebGLExtensionTextureHalfFloat::WebGLExtensionTextureHalfFloat(WebGLContext* webgl)
     : WebGLExtensionBase(webgl)
 {
-    webgl::FormatUsageAuthority* authority = webgl->mFormatUsage.get();
+    auto& fua = webgl->mFormatUsage;
+
+    webgl::PackingInfo pi;
+    webgl::DriverUnpackInfo dui;
+
+    const auto fnAdd = [&fua, &pi, &dui](webgl::EffectiveFormat effFormat) {
+        auto usage = fua->EditUsage(effFormat);
+        fua->AddUnsizedTexFormat(pi, usage);
+        usage->AddUnpack(pi, dui);
+    };
+
+    GLenum driverUnpackType = LOCAL_GL_HALF_FLOAT;
+    if (!webgl->GL()->IsSupported(gl::GLFeature::texture_half_float)) {
+        MOZ_ASSERT(webgl->GL()->IsExtensionSupported(gl::GLContext::OES_texture_half_float));
+        driverUnpackType = LOCAL_GL_HALF_FLOAT_OES;
+    }
+
+    const bool isCore = webgl->GL()->IsCoreProfile();
 
-    auto updateUsage = [authority](EffectiveFormat effectiveFormat,
-                                   GLenum unpackFormat, GLenum unpackType)
-        {
-            webgl::FormatUsageInfo* usage = authority->GetUsage(effectiveFormat);
-            MOZ_ASSERT(usage);
-            usage->asTexture = true;
-            authority->AddUnpackOption(unpackFormat, unpackType, effectiveFormat);
-        };
+    pi = {LOCAL_GL_RGBA, LOCAL_GL_HALF_FLOAT_OES};
+    dui = {LOCAL_GL_RGBA, LOCAL_GL_RGBA, driverUnpackType};
+    fnAdd(webgl::EffectiveFormat::RGBA16F);
+
+    pi = {LOCAL_GL_RGB, LOCAL_GL_HALF_FLOAT_OES};
+    dui = {LOCAL_GL_RGB, LOCAL_GL_RGB, driverUnpackType};
+    fnAdd(webgl::EffectiveFormat::RGB16F);
 
-    InitWebGLFormats(authority);
+    pi = {LOCAL_GL_LUMINANCE, LOCAL_GL_HALF_FLOAT_OES};
+    if (isCore) dui = {LOCAL_GL_R16F, LOCAL_GL_RED, driverUnpackType};
+    else        dui = {LOCAL_GL_LUMINANCE, LOCAL_GL_LUMINANCE, driverUnpackType};
+    fnAdd(webgl::EffectiveFormat::Luminance16F);
 
-    // Update usage to allow asTexture and add unpack
-    updateUsage(EffectiveFormat::RGBA16F             , LOCAL_GL_RGBA           , LOCAL_GL_HALF_FLOAT_OES);
-    updateUsage(EffectiveFormat::RGB16F              , LOCAL_GL_RGB            , LOCAL_GL_HALF_FLOAT_OES);
-    updateUsage(EffectiveFormat::Luminance16FAlpha16F, LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_HALF_FLOAT_OES);
-    updateUsage(EffectiveFormat::Luminance16F        , LOCAL_GL_LUMINANCE      , LOCAL_GL_HALF_FLOAT_OES);
-    updateUsage(EffectiveFormat::Alpha16F            , LOCAL_GL_ALPHA          , LOCAL_GL_HALF_FLOAT_OES);
+    pi = {LOCAL_GL_ALPHA, LOCAL_GL_HALF_FLOAT_OES};
+    if (isCore) dui = {LOCAL_GL_R16F, LOCAL_GL_RED, driverUnpackType};
+    else        dui = {LOCAL_GL_ALPHA, LOCAL_GL_ALPHA, driverUnpackType};
+    fnAdd(webgl::EffectiveFormat::Alpha16F);
+
+    pi = {LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_HALF_FLOAT_OES};
+    if (isCore) dui = {LOCAL_GL_RG16F, LOCAL_GL_RG, driverUnpackType};
+    else        dui = {LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_LUMINANCE_ALPHA, driverUnpackType};
+    fnAdd(webgl::EffectiveFormat::Luminance16FAlpha16F);
 }
 
 WebGLExtensionTextureHalfFloat::~WebGLExtensionTextureHalfFloat()
 {
 }
 
 IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionTextureHalfFloat, OES_texture_half_float)
 
--- a/dom/canvas/WebGLExtensionTextureHalfFloatLinear.cpp
+++ b/dom/canvas/WebGLExtensionTextureHalfFloatLinear.cpp
@@ -5,42 +5,26 @@
 #include "WebGLExtensions.h"
 
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "WebGLContext.h"
 #include "WebGLFormats.h"
 
 namespace mozilla {
 
-using mozilla::webgl::EffectiveFormat;
-
 WebGLExtensionTextureHalfFloatLinear::WebGLExtensionTextureHalfFloatLinear(WebGLContext* webgl)
     : WebGLExtensionBase(webgl)
 {
-    // This update requires that the authority already be populated by
-    // WebGLExtensionTextureHalfFloat.  Enabling extensions to control
-    // features is a mess in WebGL
-
-    webgl::FormatUsageAuthority* authority = webgl->mFormatUsage.get();
+    auto& fua = webgl->mFormatUsage;
 
-    auto updateUsage = [authority](EffectiveFormat effectiveFormat) {
-        webgl::FormatUsageInfo* usage = authority->GetUsage(effectiveFormat);
-        MOZ_ASSERT(usage);
-        usage->isFilterable = true;
-    };
-
-    // Ensure require formats are initialized.
-    WebGLExtensionTextureHalfFloat::InitWebGLFormats(authority);
-
-    // Update usage to allow isFilterable
-    updateUsage(EffectiveFormat::RGBA16F);
-    updateUsage(EffectiveFormat::RGB16F);
-    updateUsage(EffectiveFormat::Luminance16FAlpha16F);
-    updateUsage(EffectiveFormat::Luminance16F);
-    updateUsage(EffectiveFormat::Alpha16F);
+    fua->EditUsage(webgl::EffectiveFormat::RGBA16F)->isFilterable = true;
+    fua->EditUsage(webgl::EffectiveFormat::RGB16F)->isFilterable = true;
+    fua->EditUsage(webgl::EffectiveFormat::Luminance16FAlpha16F)->isFilterable = true;
+    fua->EditUsage(webgl::EffectiveFormat::Luminance16F)->isFilterable = true;
+    fua->EditUsage(webgl::EffectiveFormat::Alpha16F)->isFilterable = true;
 }
 
 WebGLExtensionTextureHalfFloatLinear::~WebGLExtensionTextureHalfFloatLinear()
 {
 }
 
 IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionTextureHalfFloatLinear, OES_texture_half_float_linear)
 
--- a/dom/canvas/WebGLExtensions.h
+++ b/dom/canvas/WebGLExtensions.h
@@ -33,17 +33,17 @@ class WebGLVertexArray;
 class WebGLExtensionBase
     : public nsWrapperCache
     , public WebGLContextBoundObject
 {
 public:
     explicit WebGLExtensionBase(WebGLContext* webgl);
 
     WebGLContext* GetParentObject() const {
-        return Context();
+        return mContext;
     }
 
     void MarkLost();
 
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLExtensionBase)
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLExtensionBase)
 
 protected:
--- a/dom/canvas/WebGLFormats.cpp
+++ b/dom/canvas/WebGLFormats.cpp
@@ -3,132 +3,152 @@
  * 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 "WebGLFormats.h"
 
 #include "GLDefs.h"
 #include "mozilla/StaticMutex.h"
 
+#ifdef FOO
+#error FOO is already defined! We use FOO() macros to keep things succinct in this file.
+#endif
+
 namespace mozilla {
 namespace webgl {
 
+// Returns an iterator to the in-place pair.
+template<typename K, typename V, typename K2, typename V2>
+static inline auto
+AlwaysInsert(std::map<K,V>& dest, const K2& key, const V2& val)
+{
+    auto res = dest.insert({ key, val });
+    DebugOnly<bool> didInsert = res.second;
+    MOZ_ASSERT(didInsert);
+
+    return res.first;
+}
+
+template<typename K, typename V, typename K2>
+static inline V*
+FindOrNull(const std::map<K,V*>& dest, const K2& key)
+{
+    auto itr = dest.find(key);
+    if (itr == dest.end())
+        return nullptr;
+
+    return itr->second;
+}
+
+// Returns a pointer to the in-place value for `key`.
+template<typename K, typename V, typename K2>
+static inline V*
+FindPtrOrNull(const std::map<K,V>& dest, const K2& key)
+{
+    auto itr = dest.find(key);
+    if (itr == dest.end())
+        return nullptr;
+
+    return &(itr->second);
+}
+
 //////////////////////////////////////////////////////////////////////////////////////////
 
 std::map<EffectiveFormat, const CompressedFormatInfo> gCompressedFormatInfoMap;
 std::map<EffectiveFormat, const FormatInfo> gFormatInfoMap;
-std::map<UnpackTuple, const FormatInfo*> gUnpackTupleMap;
-std::map<GLenum, const FormatInfo*> gSizedFormatMap;
 
-static const CompressedFormatInfo*
+static inline const CompressedFormatInfo*
 GetCompressedFormatInfo(EffectiveFormat format)
 {
     MOZ_ASSERT(!gCompressedFormatInfoMap.empty());
-    auto itr = gCompressedFormatInfoMap.find(format);
-    if (itr == gCompressedFormatInfoMap.end())
-        return nullptr;
-
-    return &(itr->second);
+    return FindPtrOrNull(gCompressedFormatInfoMap, format);
 }
 
-static const FormatInfo*
+static inline const FormatInfo*
 GetFormatInfo_NoLock(EffectiveFormat format)
 {
     MOZ_ASSERT(!gFormatInfoMap.empty());
-    auto itr = gFormatInfoMap.find(format);
-    if (itr == gFormatInfoMap.end())
-        return nullptr;
-
-    return &(itr->second);
-}
-
-template<typename K, typename V, typename K2, typename V2>
-static void
-AlwaysInsert(std::map<K,V>& dest, const K2& key, const V2& val)
-{
-    auto res = dest.insert({ key, val });
-    bool didInsert = res.second;
-    MOZ_ALWAYS_TRUE(didInsert);
+    return FindPtrOrNull(gFormatInfoMap, format);
 }
 
 //////////////////////////////////////////////////////////////////////////////////////////
 
 static void
 AddCompressedFormatInfo(EffectiveFormat format, uint16_t bitsPerBlock, uint8_t blockWidth,
-                        uint8_t blockHeight, bool requirePOT,
-                        SubImageUpdateBehavior subImageUpdateBehavior)
+                        uint8_t blockHeight, CompressionFamily family)
 {
     MOZ_ASSERT(bitsPerBlock % 8 == 0);
     uint16_t bytesPerBlock = bitsPerBlock / 8; // The specs always state these in bits,
                                                // but it's only ever useful to us as
                                                // bytes.
     MOZ_ASSERT(bytesPerBlock <= 255);
 
     const CompressedFormatInfo info = { format, uint8_t(bytesPerBlock), blockWidth,
-                                        blockHeight, requirePOT, subImageUpdateBehavior };
+                                        blockHeight, family };
     AlwaysInsert(gCompressedFormatInfoMap, format, info);
 }
 
 static void
 InitCompressedFormatInfo()
 {
     // GLES 3.0.4, p147, table 3.19
     // GLES 3.0.4, p286+, $C.1 "ETC Compressed Texture Image Formats"
-    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGB8_ETC2                     ,  64, 4, 4, false, SubImageUpdateBehavior::BlockAligned);
-    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_SRGB8_ETC2                    ,  64, 4, 4, false, SubImageUpdateBehavior::BlockAligned);
-    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGBA8_ETC2_EAC                , 128, 4, 4, false, SubImageUpdateBehavior::BlockAligned);
-    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_SRGB8_ALPHA8_ETC2_EAC         , 128, 4, 4, false, SubImageUpdateBehavior::BlockAligned);
-    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_R11_EAC                       ,  64, 4, 4, false, SubImageUpdateBehavior::BlockAligned);
-    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RG11_EAC                      , 128, 4, 4, false, SubImageUpdateBehavior::BlockAligned);
-    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_SIGNED_R11_EAC                ,  64, 4, 4, false, SubImageUpdateBehavior::BlockAligned);
-    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_SIGNED_RG11_EAC               , 128, 4, 4, false, SubImageUpdateBehavior::BlockAligned);
-    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 ,  64, 4, 4, false, SubImageUpdateBehavior::BlockAligned);
-    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2,  64, 4, 4, false, SubImageUpdateBehavior::BlockAligned);
+    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGB8_ETC2                     ,  64, 4, 4, CompressionFamily::ES3);
+    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_SRGB8_ETC2                    ,  64, 4, 4, CompressionFamily::ES3);
+    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGBA8_ETC2_EAC                , 128, 4, 4, CompressionFamily::ES3);
+    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_SRGB8_ALPHA8_ETC2_EAC         , 128, 4, 4, CompressionFamily::ES3);
+    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_R11_EAC                       ,  64, 4, 4, CompressionFamily::ES3);
+    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RG11_EAC                      , 128, 4, 4, CompressionFamily::ES3);
+    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_SIGNED_R11_EAC                ,  64, 4, 4, CompressionFamily::ES3);
+    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_SIGNED_RG11_EAC               , 128, 4, 4, CompressionFamily::ES3);
+    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 ,  64, 4, 4, CompressionFamily::ES3);
+    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2,  64, 4, 4, CompressionFamily::ES3);
 
     // AMD_compressed_ATC_texture
-    AddCompressedFormatInfo(EffectiveFormat::ATC_RGB_AMD                    ,  64, 4, 4, false, SubImageUpdateBehavior::Forbidden);
-    AddCompressedFormatInfo(EffectiveFormat::ATC_RGBA_EXPLICIT_ALPHA_AMD    , 128, 4, 4, false, SubImageUpdateBehavior::Forbidden);
-    AddCompressedFormatInfo(EffectiveFormat::ATC_RGBA_INTERPOLATED_ALPHA_AMD, 128, 4, 4, false, SubImageUpdateBehavior::Forbidden);
+    AddCompressedFormatInfo(EffectiveFormat::ATC_RGB_AMD                    ,  64, 4, 4, CompressionFamily::ATC);
+    AddCompressedFormatInfo(EffectiveFormat::ATC_RGBA_EXPLICIT_ALPHA_AMD    , 128, 4, 4, CompressionFamily::ATC);
+    AddCompressedFormatInfo(EffectiveFormat::ATC_RGBA_INTERPOLATED_ALPHA_AMD, 128, 4, 4, CompressionFamily::ATC);
 
     // EXT_texture_compression_s3tc
-    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGB_S3TC_DXT1 ,  64, 4, 4, false, SubImageUpdateBehavior::BlockAligned);
-    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGBA_S3TC_DXT1,  64, 4, 4, false, SubImageUpdateBehavior::BlockAligned);
-    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGBA_S3TC_DXT3, 128, 4, 4, false, SubImageUpdateBehavior::BlockAligned);
-    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGBA_S3TC_DXT5, 128, 4, 4, false, SubImageUpdateBehavior::BlockAligned);
+    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGB_S3TC_DXT1_EXT ,  64, 4, 4, CompressionFamily::S3TC);
+    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGBA_S3TC_DXT1_EXT,  64, 4, 4, CompressionFamily::S3TC);
+    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGBA_S3TC_DXT3_EXT, 128, 4, 4, CompressionFamily::S3TC);
+    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGBA_S3TC_DXT5_EXT, 128, 4, 4, CompressionFamily::S3TC);
 
     // IMG_texture_compression_pvrtc
-    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGB_PVRTC_4BPPV1 , 256,  8, 8, true, SubImageUpdateBehavior::FullOnly);
-    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGBA_PVRTC_4BPPV1, 256,  8, 8, true, SubImageUpdateBehavior::FullOnly);
-    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGB_PVRTC_2BPPV1 , 256, 16, 8, true, SubImageUpdateBehavior::FullOnly);
-    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGBA_PVRTC_2BPPV1, 256, 16, 8, true, SubImageUpdateBehavior::FullOnly);
+    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGB_PVRTC_4BPPV1 , 256,  8, 8, CompressionFamily::PVRTC);
+    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGBA_PVRTC_4BPPV1, 256,  8, 8, CompressionFamily::PVRTC);
+    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGB_PVRTC_2BPPV1 , 256, 16, 8, CompressionFamily::PVRTC);
+    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGBA_PVRTC_2BPPV1, 256, 16, 8, CompressionFamily::PVRTC);
 
     // OES_compressed_ETC1_RGB8_texture
-    AddCompressedFormatInfo(EffectiveFormat::ETC1_RGB8, 64, 4, 4, false, SubImageUpdateBehavior::Forbidden);
+    AddCompressedFormatInfo(EffectiveFormat::ETC1_RGB8_OES, 64, 4, 4, CompressionFamily::ETC1);
 }
 
 //////////////////////////////////////////////////////////////////////////////////////////
 
 static void
-AddFormatInfo(EffectiveFormat format, const char* name, uint8_t bytesPerPixel,
-              UnsizedFormat unsizedFormat, ComponentType colorComponentType)
+AddFormatInfo(EffectiveFormat format, const char* name, GLenum sizedFormat,
+              uint8_t bytesPerPixel, UnsizedFormat unsizedFormat, bool isSRGB,
+              ComponentType componentType)
 {
     bool isColorFormat = false;
     bool hasAlpha = false;
     bool hasDepth = false;
     bool hasStencil = false;
 
     switch (unsizedFormat) {
     case UnsizedFormat::L:
     case UnsizedFormat::R:
     case UnsizedFormat::RG:
     case UnsizedFormat::RGB:
         isColorFormat = true;
         break;
 
-    case UnsizedFormat::A: // 'Color format' in the spec just means color-attachable.
+    case UnsizedFormat::A: // Alpha is a 'color format' since it's 'color-attachable'.
     case UnsizedFormat::LA:
     case UnsizedFormat::RGBA:
         isColorFormat = true;
         hasAlpha = true;
         break;
 
     case UnsizedFormat::D:
         hasDepth = true;
@@ -145,1015 +165,700 @@ AddFormatInfo(EffectiveFormat format, co
 
     default:
         MOZ_CRASH("Missing UnsizedFormat case.");
     }
 
     const CompressedFormatInfo* compressedFormatInfo = GetCompressedFormatInfo(format);
     MOZ_ASSERT(!bytesPerPixel == bool(compressedFormatInfo));
 
-    const FormatInfo info = { format, name, unsizedFormat, colorComponentType,
-                              bytesPerPixel, isColorFormat, hasAlpha, hasDepth,
+    const FormatInfo info = { format, name, sizedFormat, unsizedFormat, componentType,
+                              bytesPerPixel, isColorFormat, isSRGB, hasAlpha, hasDepth,
                               hasStencil, compressedFormatInfo };
-    AlwaysInsert(gFormatInfoMap, format, info);
+    const auto itr = AlwaysInsert(gFormatInfoMap, format, info);
 }
 
 static void
-InitFormatInfoMap()
+InitFormatInfo()
 {
-#ifdef FOO
-#error FOO is already defined!
-#endif
-
-#define FOO(x) EffectiveFormat::x, #x
+#define FOO(x) EffectiveFormat::x, #x, LOCAL_GL_ ## x
 
     // GLES 3.0.4, p130-132, table 3.13
-    AddFormatInfo(FOO(R8            ),  1, UnsizedFormat::R   , ComponentType::NormUInt    );
-    AddFormatInfo(FOO(R8_SNORM      ),  1, UnsizedFormat::R   , ComponentType::NormInt     );
-    AddFormatInfo(FOO(RG8           ),  2, UnsizedFormat::RG  , ComponentType::NormUInt    );
-    AddFormatInfo(FOO(RG8_SNORM     ),  2, UnsizedFormat::RG  , ComponentType::NormInt     );
-    AddFormatInfo(FOO(RGB8          ),  3, UnsizedFormat::RGB , ComponentType::NormUInt    );
-    AddFormatInfo(FOO(RGB8_SNORM    ),  3, UnsizedFormat::RGB , ComponentType::NormInt     );
-    AddFormatInfo(FOO(RGB565        ),  2, UnsizedFormat::RGB , ComponentType::NormUInt    );
-    AddFormatInfo(FOO(RGBA4         ),  2, UnsizedFormat::RGBA, ComponentType::NormUInt    );
-    AddFormatInfo(FOO(RGB5_A1       ),  2, UnsizedFormat::RGBA, ComponentType::NormUInt    );
-    AddFormatInfo(FOO(RGBA8         ),  4, UnsizedFormat::RGBA, ComponentType::NormUInt    );
-    AddFormatInfo(FOO(RGBA8_SNORM   ),  4, UnsizedFormat::RGBA, ComponentType::NormInt     );
-    AddFormatInfo(FOO(RGB10_A2      ),  4, UnsizedFormat::RGBA, ComponentType::NormUInt    );
-    AddFormatInfo(FOO(RGB10_A2UI    ),  4, UnsizedFormat::RGBA, ComponentType::UInt        );
+    AddFormatInfo(FOO(R8            ),  1, UnsizedFormat::R   , false, ComponentType::NormUInt);
+    AddFormatInfo(FOO(R8_SNORM      ),  1, UnsizedFormat::R   , false, ComponentType::NormInt );
+    AddFormatInfo(FOO(RG8           ),  2, UnsizedFormat::RG  , false, ComponentType::NormUInt);
+    AddFormatInfo(FOO(RG8_SNORM     ),  2, UnsizedFormat::RG  , false, ComponentType::NormInt );
+    AddFormatInfo(FOO(RGB8          ),  3, UnsizedFormat::RGB , false, ComponentType::NormUInt);
+    AddFormatInfo(FOO(RGB8_SNORM    ),  3, UnsizedFormat::RGB , false, ComponentType::NormInt );
+    AddFormatInfo(FOO(RGB565        ),  2, UnsizedFormat::RGB , false, ComponentType::NormUInt);
+    AddFormatInfo(FOO(RGBA4         ),  2, UnsizedFormat::RGBA, false, ComponentType::NormUInt);
+    AddFormatInfo(FOO(RGB5_A1       ),  2, UnsizedFormat::RGBA, false, ComponentType::NormUInt);
+    AddFormatInfo(FOO(RGBA8         ),  4, UnsizedFormat::RGBA, false, ComponentType::NormUInt);
+    AddFormatInfo(FOO(RGBA8_SNORM   ),  4, UnsizedFormat::RGBA, false, ComponentType::NormInt );
+    AddFormatInfo(FOO(RGB10_A2      ),  4, UnsizedFormat::RGBA, false, ComponentType::NormUInt);
+    AddFormatInfo(FOO(RGB10_A2UI    ),  4, UnsizedFormat::RGBA, false, ComponentType::UInt    );
 
-    AddFormatInfo(FOO(SRGB8         ),  3, UnsizedFormat::RGB , ComponentType::NormUIntSRGB);
-    AddFormatInfo(FOO(SRGB8_ALPHA8  ),  4, UnsizedFormat::RGBA, ComponentType::NormUIntSRGB);
+    AddFormatInfo(FOO(SRGB8         ),  3, UnsizedFormat::RGB , true , ComponentType::NormUInt);
+    AddFormatInfo(FOO(SRGB8_ALPHA8  ),  4, UnsizedFormat::RGBA, true , ComponentType::NormUInt);
 
-    AddFormatInfo(FOO(R16F          ),  2, UnsizedFormat::R   , ComponentType::Float       );
-    AddFormatInfo(FOO(RG16F         ),  4, UnsizedFormat::RG  , ComponentType::Float       );
-    AddFormatInfo(FOO(RGB16F        ),  6, UnsizedFormat::RGB , ComponentType::Float       );
-    AddFormatInfo(FOO(RGBA16F       ),  8, UnsizedFormat::RGBA, ComponentType::Float       );
-    AddFormatInfo(FOO(R32F          ),  4, UnsizedFormat::R   , ComponentType::Float       );
-    AddFormatInfo(FOO(RG32F         ),  8, UnsizedFormat::RG  , ComponentType::Float       );
-    AddFormatInfo(FOO(RGB32F        ), 12, UnsizedFormat::RGB , ComponentType::Float       );
-    AddFormatInfo(FOO(RGBA32F       ), 16, UnsizedFormat::RGBA, ComponentType::Float       );
+    AddFormatInfo(FOO(R16F          ),  2, UnsizedFormat::R   , false, ComponentType::Float   );
+    AddFormatInfo(FOO(RG16F         ),  4, UnsizedFormat::RG  , false, ComponentType::Float   );
+    AddFormatInfo(FOO(RGB16F        ),  6, UnsizedFormat::RGB , false, ComponentType::Float   );
+    AddFormatInfo(FOO(RGBA16F       ),  8, UnsizedFormat::RGBA, false, ComponentType::Float   );
+    AddFormatInfo(FOO(R32F          ),  4, UnsizedFormat::R   , false, ComponentType::Float   );
+    AddFormatInfo(FOO(RG32F         ),  8, UnsizedFormat::RG  , false, ComponentType::Float   );
+    AddFormatInfo(FOO(RGB32F        ), 12, UnsizedFormat::RGB , false, ComponentType::Float   );
+    AddFormatInfo(FOO(RGBA32F       ), 16, UnsizedFormat::RGBA, false, ComponentType::Float   );
 
-    AddFormatInfo(FOO(R11F_G11F_B10F),  4, UnsizedFormat::RGB , ComponentType::Float       );
-    AddFormatInfo(FOO(RGB9_E5       ),  4, UnsizedFormat::RGB , ComponentType::SharedExp   );
+    AddFormatInfo(FOO(R11F_G11F_B10F),  4, UnsizedFormat::RGB , false, ComponentType::Float   );
+    AddFormatInfo(FOO(RGB9_E5       ),  4, UnsizedFormat::RGB , false, ComponentType::Float   );
 
-    AddFormatInfo(FOO(R8I           ),  1, UnsizedFormat::R   , ComponentType::Int         );
-    AddFormatInfo(FOO(R8UI          ),  1, UnsizedFormat::R   , ComponentType::UInt        );
-    AddFormatInfo(FOO(R16I          ),  2, UnsizedFormat::R   , ComponentType::Int         );
-    AddFormatInfo(FOO(R16UI         ),  2, UnsizedFormat::R   , ComponentType::UInt        );
-    AddFormatInfo(FOO(R32I          ),  4, UnsizedFormat::R   , ComponentType::Int         );
-    AddFormatInfo(FOO(R32UI         ),  4, UnsizedFormat::R   , ComponentType::UInt        );
+    AddFormatInfo(FOO(R8I           ),  1, UnsizedFormat::R   , false, ComponentType::Int     );
+    AddFormatInfo(FOO(R8UI          ),  1, UnsizedFormat::R   , false, ComponentType::UInt    );
+    AddFormatInfo(FOO(R16I          ),  2, UnsizedFormat::R   , false, ComponentType::Int     );
+    AddFormatInfo(FOO(R16UI         ),  2, UnsizedFormat::R   , false, ComponentType::UInt    );
+    AddFormatInfo(FOO(R32I          ),  4, UnsizedFormat::R   , false, ComponentType::Int     );
+    AddFormatInfo(FOO(R32UI         ),  4, UnsizedFormat::R   , false, ComponentType::UInt    );
 
-    AddFormatInfo(FOO(RG8I          ),  2, UnsizedFormat::RG  , ComponentType::Int         );
-    AddFormatInfo(FOO(RG8UI         ),  2, UnsizedFormat::RG  , ComponentType::UInt        );
-    AddFormatInfo(FOO(RG16I         ),  4, UnsizedFormat::RG  , ComponentType::Int         );
-    AddFormatInfo(FOO(RG16UI        ),  4, UnsizedFormat::RG  , ComponentType::UInt        );
-    AddFormatInfo(FOO(RG32I         ),  8, UnsizedFormat::RG  , ComponentType::Int         );
-    AddFormatInfo(FOO(RG32UI        ),  8, UnsizedFormat::RG  , ComponentType::UInt        );
+    AddFormatInfo(FOO(RG8I          ),  2, UnsizedFormat::RG  , false, ComponentType::Int     );
+    AddFormatInfo(FOO(RG8UI         ),  2, UnsizedFormat::RG  , false, ComponentType::UInt    );
+    AddFormatInfo(FOO(RG16I         ),  4, UnsizedFormat::RG  , false, ComponentType::Int     );
+    AddFormatInfo(FOO(RG16UI        ),  4, UnsizedFormat::RG  , false, ComponentType::UInt    );
+    AddFormatInfo(FOO(RG32I         ),  8, UnsizedFormat::RG  , false, ComponentType::Int     );
+    AddFormatInfo(FOO(RG32UI        ),  8, UnsizedFormat::RG  , false, ComponentType::UInt    );
 
-    AddFormatInfo(FOO(RGB8I         ),  3, UnsizedFormat::RGB , ComponentType::Int         );
-    AddFormatInfo(FOO(RGB8UI        ),  3, UnsizedFormat::RGB , ComponentType::UInt        );
-    AddFormatInfo(FOO(RGB16I        ),  6, UnsizedFormat::RGB , ComponentType::Int         );
-    AddFormatInfo(FOO(RGB16UI       ),  6, UnsizedFormat::RGB , ComponentType::UInt        );
-    AddFormatInfo(FOO(RGB32I        ), 12, UnsizedFormat::RGB , ComponentType::Int         );
-    AddFormatInfo(FOO(RGB32UI       ), 12, UnsizedFormat::RGB , ComponentType::UInt        );
+    AddFormatInfo(FOO(RGB8I         ),  3, UnsizedFormat::RGB , false, ComponentType::Int     );
+    AddFormatInfo(FOO(RGB8UI        ),  3, UnsizedFormat::RGB , false, ComponentType::UInt    );
+    AddFormatInfo(FOO(RGB16I        ),  6, UnsizedFormat::RGB , false, ComponentType::Int     );
+    AddFormatInfo(FOO(RGB16UI       ),  6, UnsizedFormat::RGB , false, ComponentType::UInt    );
+    AddFormatInfo(FOO(RGB32I        ), 12, UnsizedFormat::RGB , false, ComponentType::Int     );
+    AddFormatInfo(FOO(RGB32UI       ), 12, UnsizedFormat::RGB , false, ComponentType::UInt    );
 
-    AddFormatInfo(FOO(RGBA8I        ),  4, UnsizedFormat::RGBA, ComponentType::Int         );
-    AddFormatInfo(FOO(RGBA8UI       ),  4, UnsizedFormat::RGBA, ComponentType::UInt        );
-    AddFormatInfo(FOO(RGBA16I       ),  8, UnsizedFormat::RGBA, ComponentType::Int         );
-    AddFormatInfo(FOO(RGBA16UI      ),  8, UnsizedFormat::RGBA, ComponentType::UInt        );
-    AddFormatInfo(FOO(RGBA32I       ), 16, UnsizedFormat::RGBA, ComponentType::Int         );
-    AddFormatInfo(FOO(RGBA32UI      ), 16, UnsizedFormat::RGBA, ComponentType::UInt        );
+    AddFormatInfo(FOO(RGBA8I        ),  4, UnsizedFormat::RGBA, false, ComponentType::Int     );
+    AddFormatInfo(FOO(RGBA8UI       ),  4, UnsizedFormat::RGBA, false, ComponentType::UInt    );
+    AddFormatInfo(FOO(RGBA16I       ),  8, UnsizedFormat::RGBA, false, ComponentType::Int     );
+    AddFormatInfo(FOO(RGBA16UI      ),  8, UnsizedFormat::RGBA, false, ComponentType::UInt    );
+    AddFormatInfo(FOO(RGBA32I       ), 16, UnsizedFormat::RGBA, false, ComponentType::Int     );
+    AddFormatInfo(FOO(RGBA32UI      ), 16, UnsizedFormat::RGBA, false, ComponentType::UInt    );
 
     // GLES 3.0.4, p133, table 3.14
-    AddFormatInfo(FOO(DEPTH_COMPONENT16 ), 2, UnsizedFormat::D , ComponentType::None);
-    AddFormatInfo(FOO(DEPTH_COMPONENT24 ), 3, UnsizedFormat::D , ComponentType::None);
-    AddFormatInfo(FOO(DEPTH_COMPONENT32F), 4, UnsizedFormat::D , ComponentType::None);
-    AddFormatInfo(FOO(DEPTH24_STENCIL8  ), 4, UnsizedFormat::DS, ComponentType::None);
-    AddFormatInfo(FOO(DEPTH32F_STENCIL8 ), 5, UnsizedFormat::DS, ComponentType::None);
+    AddFormatInfo(FOO(DEPTH_COMPONENT16 ), 2, UnsizedFormat::D , false, ComponentType::NormUInt);
+    AddFormatInfo(FOO(DEPTH_COMPONENT24 ), 3, UnsizedFormat::D , false, ComponentType::NormUInt);
+    AddFormatInfo(FOO(DEPTH_COMPONENT32F), 4, UnsizedFormat::D , false, ComponentType::Float);
+    AddFormatInfo(FOO(DEPTH24_STENCIL8  ), 4, UnsizedFormat::DS, false, ComponentType::None);
+    AddFormatInfo(FOO(DEPTH32F_STENCIL8 ), 5, UnsizedFormat::DS, false, ComponentType::None);
 
     // GLES 3.0.4, p205-206, "Required Renderbuffer Formats"
-    AddFormatInfo(FOO(STENCIL_INDEX8), 1, UnsizedFormat::S, ComponentType::None);
-
-    // GLES 3.0.4, p128, table 3.12.
-    AddFormatInfo(FOO(Luminance8Alpha8), 2, UnsizedFormat::LA, ComponentType::NormUInt);
-    AddFormatInfo(FOO(Luminance8      ), 1, UnsizedFormat::L , ComponentType::NormUInt);
-    AddFormatInfo(FOO(Alpha8          ), 1, UnsizedFormat::A , ComponentType::None    );
+    AddFormatInfo(FOO(STENCIL_INDEX8), 1, UnsizedFormat::S, false, ComponentType::UInt);
 
     // GLES 3.0.4, p147, table 3.19
     // GLES 3.0.4  p286+  $C.1 "ETC Compressed Texture Image Formats"
-    AddFormatInfo(FOO(COMPRESSED_RGB8_ETC2                     ), 0, UnsizedFormat::RGB , ComponentType::NormUInt    );
-    AddFormatInfo(FOO(COMPRESSED_SRGB8_ETC2                    ), 0, UnsizedFormat::RGB , ComponentType::NormUIntSRGB);
-    AddFormatInfo(FOO(COMPRESSED_RGBA8_ETC2_EAC                ), 0, UnsizedFormat::RGBA, ComponentType::NormUInt    );
-    AddFormatInfo(FOO(COMPRESSED_SRGB8_ALPHA8_ETC2_EAC         ), 0, UnsizedFormat::RGBA, ComponentType::NormUIntSRGB);
-    AddFormatInfo(FOO(COMPRESSED_R11_EAC                       ), 0, UnsizedFormat::R   , ComponentType::NormUInt    );
-    AddFormatInfo(FOO(COMPRESSED_RG11_EAC                      ), 0, UnsizedFormat::RG  , ComponentType::NormUInt    );
-    AddFormatInfo(FOO(COMPRESSED_SIGNED_R11_EAC                ), 0, UnsizedFormat::R   , ComponentType::NormInt     );
-    AddFormatInfo(FOO(COMPRESSED_SIGNED_RG11_EAC               ), 0, UnsizedFormat::RG  , ComponentType::NormInt     );
-    AddFormatInfo(FOO(COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 ), 0, UnsizedFormat::RGBA, ComponentType::NormUInt    );
-    AddFormatInfo(FOO(COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2), 0, UnsizedFormat::RGBA, ComponentType::NormUIntSRGB);
+    AddFormatInfo(FOO(COMPRESSED_RGB8_ETC2                     ), 0, UnsizedFormat::RGB , false, ComponentType::NormUInt);
+    AddFormatInfo(FOO(COMPRESSED_SRGB8_ETC2                    ), 0, UnsizedFormat::RGB , true , ComponentType::NormUInt);
+    AddFormatInfo(FOO(COMPRESSED_RGBA8_ETC2_EAC                ), 0, UnsizedFormat::RGBA, false, ComponentType::NormUInt);
+    AddFormatInfo(FOO(COMPRESSED_SRGB8_ALPHA8_ETC2_EAC         ), 0, UnsizedFormat::RGBA, true , ComponentType::NormUInt);
+    AddFormatInfo(FOO(COMPRESSED_R11_EAC                       ), 0, UnsizedFormat::R   , false, ComponentType::NormUInt);
+    AddFormatInfo(FOO(COMPRESSED_RG11_EAC                      ), 0, UnsizedFormat::RG  , false, ComponentType::NormUInt);
+    AddFormatInfo(FOO(COMPRESSED_SIGNED_R11_EAC                ), 0, UnsizedFormat::R   , false, ComponentType::NormInt );
+    AddFormatInfo(FOO(COMPRESSED_SIGNED_RG11_EAC               ), 0, UnsizedFormat::RG  , false, ComponentType::NormInt );
+    AddFormatInfo(FOO(COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 ), 0, UnsizedFormat::RGBA, false, ComponentType::NormUInt);
+    AddFormatInfo(FOO(COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2), 0, UnsizedFormat::RGBA, true , ComponentType::NormUInt);
 
     // AMD_compressed_ATC_texture
-    AddFormatInfo(FOO(ATC_RGB_AMD                    ), 0, UnsizedFormat::RGB , ComponentType::NormUInt);
-    AddFormatInfo(FOO(ATC_RGBA_EXPLICIT_ALPHA_AMD    ), 0, UnsizedFormat::RGBA, ComponentType::NormUInt);
-    AddFormatInfo(FOO(ATC_RGBA_INTERPOLATED_ALPHA_AMD), 0, UnsizedFormat::RGBA, ComponentType::NormUInt);
+    AddFormatInfo(FOO(ATC_RGB_AMD                    ), 0, UnsizedFormat::RGB , false, ComponentType::NormUInt);
+    AddFormatInfo(FOO(ATC_RGBA_EXPLICIT_ALPHA_AMD    ), 0, UnsizedFormat::RGBA, false, ComponentType::NormUInt);
+    AddFormatInfo(FOO(ATC_RGBA_INTERPOLATED_ALPHA_AMD), 0, UnsizedFormat::RGBA, false, ComponentType::NormUInt);
 
     // EXT_texture_compression_s3tc
-    AddFormatInfo(FOO(COMPRESSED_RGB_S3TC_DXT1 ), 0, UnsizedFormat::RGB , ComponentType::NormUInt);
-    AddFormatInfo(FOO(COMPRESSED_RGBA_S3TC_DXT1), 0, UnsizedFormat::RGBA, ComponentType::NormUInt);
-    AddFormatInfo(FOO(COMPRESSED_RGBA_S3TC_DXT3), 0, UnsizedFormat::RGBA, ComponentType::NormUInt);
-    AddFormatInfo(FOO(COMPRESSED_RGBA_S3TC_DXT5), 0, UnsizedFormat::RGBA, ComponentType::NormUInt);
+    AddFormatInfo(FOO(COMPRESSED_RGB_S3TC_DXT1_EXT ), 0, UnsizedFormat::RGB , false, ComponentType::NormUInt);
+    AddFormatInfo(FOO(COMPRESSED_RGBA_S3TC_DXT1_EXT), 0, UnsizedFormat::RGBA, false, ComponentType::NormUInt);
+    AddFormatInfo(FOO(COMPRESSED_RGBA_S3TC_DXT3_EXT), 0, UnsizedFormat::RGBA, false, ComponentType::NormUInt);
+    AddFormatInfo(FOO(COMPRESSED_RGBA_S3TC_DXT5_EXT), 0, UnsizedFormat::RGBA, false, ComponentType::NormUInt);
 
     // IMG_texture_compression_pvrtc
-    AddFormatInfo(FOO(COMPRESSED_RGB_PVRTC_4BPPV1 ), 0, UnsizedFormat::RGB , ComponentType::NormUInt);
-    AddFormatInfo(FOO(COMPRESSED_RGBA_PVRTC_4BPPV1), 0, UnsizedFormat::RGBA, ComponentType::NormUInt);
-    AddFormatInfo(FOO(COMPRESSED_RGB_PVRTC_2BPPV1 ), 0, UnsizedFormat::RGB , ComponentType::NormUInt);
-    AddFormatInfo(FOO(COMPRESSED_RGBA_PVRTC_2BPPV1), 0, UnsizedFormat::RGBA, ComponentType::NormUInt);
+    AddFormatInfo(FOO(COMPRESSED_RGB_PVRTC_4BPPV1 ), 0, UnsizedFormat::RGB , false, ComponentType::NormUInt);
+    AddFormatInfo(FOO(COMPRESSED_RGBA_PVRTC_4BPPV1), 0, UnsizedFormat::RGBA, false, ComponentType::NormUInt);
+    AddFormatInfo(FOO(COMPRESSED_RGB_PVRTC_2BPPV1 ), 0, UnsizedFormat::RGB , false, ComponentType::NormUInt);
+    AddFormatInfo(FOO(COMPRESSED_RGBA_PVRTC_2BPPV1), 0, UnsizedFormat::RGBA, false, ComponentType::NormUInt);
 
     // OES_compressed_ETC1_RGB8_texture
-    AddFormatInfo(FOO(ETC1_RGB8), 0, UnsizedFormat::RGB, ComponentType::NormUInt);
-
-    // OES_texture_float
-    AddFormatInfo(FOO(Luminance32FAlpha32F), 2, UnsizedFormat::LA, ComponentType::Float);
-    AddFormatInfo(FOO(Luminance32F        ), 1, UnsizedFormat::L , ComponentType::Float);
-    AddFormatInfo(FOO(Alpha32F            ), 1, UnsizedFormat::A , ComponentType::Float);
-
-    // OES_texture_half_float
-    AddFormatInfo(FOO(Luminance16FAlpha16F), 2, UnsizedFormat::LA, ComponentType::Float);
-    AddFormatInfo(FOO(Luminance16F        ), 1, UnsizedFormat::L , ComponentType::Float);
-    AddFormatInfo(FOO(Alpha16F            ), 1, UnsizedFormat::A , ComponentType::Float);
+    AddFormatInfo(FOO(ETC1_RGB8_OES), 0, UnsizedFormat::RGB, false, ComponentType::NormUInt);
 
 #undef FOO
-}
 
-//////////////////////////////////////////////////////////////////////////////////////////
-
-static void
-AddUnpackTuple(GLenum unpackFormat, GLenum unpackType, EffectiveFormat effectiveFormat)
-{
-    const UnpackTuple unpack = { unpackFormat, unpackType };
-    const FormatInfo* info = GetFormatInfo_NoLock(effectiveFormat);
-    MOZ_ASSERT(info);
-
-    AlwaysInsert(gUnpackTupleMap, unpack, info);
-}
-
-static void
-InitUnpackTupleMap()
-{
-    AddUnpackTuple(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE         , EffectiveFormat::RGBA8  );
-    AddUnpackTuple(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_SHORT_4_4_4_4, EffectiveFormat::RGBA4  );
-    AddUnpackTuple(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_SHORT_5_5_5_1, EffectiveFormat::RGB5_A1);
-    AddUnpackTuple(LOCAL_GL_RGB , LOCAL_GL_UNSIGNED_BYTE         , EffectiveFormat::RGB8   );
-    AddUnpackTuple(LOCAL_GL_RGB , LOCAL_GL_UNSIGNED_SHORT_5_6_5  , EffectiveFormat::RGB565 );
-
-    AddUnpackTuple(LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_UNSIGNED_BYTE, EffectiveFormat::Luminance8Alpha8);
-    AddUnpackTuple(LOCAL_GL_LUMINANCE      , LOCAL_GL_UNSIGNED_BYTE, EffectiveFormat::Luminance8      );
-    AddUnpackTuple(LOCAL_GL_ALPHA          , LOCAL_GL_UNSIGNED_BYTE, EffectiveFormat::Alpha8          );
-
-    AddUnpackTuple(LOCAL_GL_RGB            , LOCAL_GL_FLOAT, EffectiveFormat::RGB32F );
-    AddUnpackTuple(LOCAL_GL_RGBA           , LOCAL_GL_FLOAT, EffectiveFormat::RGBA32F);
-    AddUnpackTuple(LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_FLOAT, EffectiveFormat::Luminance32FAlpha32F);
-    AddUnpackTuple(LOCAL_GL_LUMINANCE      , LOCAL_GL_FLOAT, EffectiveFormat::Luminance32F);
-    AddUnpackTuple(LOCAL_GL_ALPHA          , LOCAL_GL_FLOAT, EffectiveFormat::Alpha32F);
+    // 'Virtual' effective formats have no sizedFormat.
+#define FOO(x) EffectiveFormat::x, #x, 0
 
-    AddUnpackTuple(LOCAL_GL_RGB            , LOCAL_GL_HALF_FLOAT, EffectiveFormat::RGB16F );
-    AddUnpackTuple(LOCAL_GL_RGBA           , LOCAL_GL_HALF_FLOAT, EffectiveFormat::RGBA16F);
-    AddUnpackTuple(LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_HALF_FLOAT, EffectiveFormat::Luminance16FAlpha16F);
-    AddUnpackTuple(LOCAL_GL_LUMINANCE      , LOCAL_GL_HALF_FLOAT, EffectiveFormat::Luminance16F);
-    AddUnpackTuple(LOCAL_GL_ALPHA          , LOCAL_GL_HALF_FLOAT, EffectiveFormat::Alpha16F);
-
-    // Everyone's favorite problem-child:
-    AddUnpackTuple(LOCAL_GL_RGB            , LOCAL_GL_HALF_FLOAT_OES, EffectiveFormat::RGB16F );
-    AddUnpackTuple(LOCAL_GL_RGBA           , LOCAL_GL_HALF_FLOAT_OES, EffectiveFormat::RGBA16F);
-    AddUnpackTuple(LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_HALF_FLOAT_OES, EffectiveFormat::Luminance16FAlpha16F);
-    AddUnpackTuple(LOCAL_GL_LUMINANCE      , LOCAL_GL_HALF_FLOAT_OES, EffectiveFormat::Luminance16F);
-    AddUnpackTuple(LOCAL_GL_ALPHA          , LOCAL_GL_HALF_FLOAT_OES, EffectiveFormat::Alpha16F);
-}
-
-//////////////////////////////////////////////////////////////////////////////////////////
-
-static void
-AddSizedFormat(GLenum sizedFormat, EffectiveFormat effectiveFormat)
-{
-    const FormatInfo* info = GetFormatInfo_NoLock(effectiveFormat);
-    MOZ_ASSERT(info);
-
-    AlwaysInsert(gSizedFormatMap, sizedFormat, info);
-}
-
-static void
-InitSizedFormatMap()
-{
-    // GLES 3.0.4, p128-129 "Required Texture Formats"
-
-    // "Texture and renderbuffer color formats"
-#ifdef FOO
-#error FOO is already defined!
-#endif
-
-#define FOO(x) AddSizedFormat(LOCAL_GL_ ## x, EffectiveFormat::x)
+    // GLES 3.0.4, p128, table 3.12.
+    AddFormatInfo(FOO(Luminance8Alpha8), 2, UnsizedFormat::LA, false, ComponentType::NormUInt);
+    AddFormatInfo(FOO(Luminance8      ), 1, UnsizedFormat::L , false, ComponentType::NormUInt);
+    AddFormatInfo(FOO(Alpha8          ), 1, UnsizedFormat::A , false, ComponentType::NormUInt);
 
-    FOO(RGBA32I);
-    FOO(RGBA32UI);
-    FOO(RGBA16I);
-    FOO(RGBA16UI);
-    FOO(RGBA8);
-    FOO(RGBA8I);
-    FOO(RGBA8UI);
-    FOO(SRGB8_ALPHA8);
-    FOO(RGB10_A2);
-    FOO(RGB10_A2UI);
-    FOO(RGBA4);
-    FOO(RGB5_A1);
-
-    FOO(RGB8);
-    FOO(RGB565);
-
-    FOO(RG32I);
-    FOO(RG32UI);
-    FOO(RG16I);
-    FOO(RG16UI);
-    FOO(RG8);
-    FOO(RG8I);
-    FOO(RG8UI);
-
-    FOO(R32I);
-    FOO(R32UI);
-    FOO(R16I);
-    FOO(R16UI);
-    FOO(R8);
-    FOO(R8I);
-    FOO(R8UI);
+    // OES_texture_float
+    AddFormatInfo(FOO(Luminance32FAlpha32F), 2, UnsizedFormat::LA, false, ComponentType::Float);
+    AddFormatInfo(FOO(Luminance32F        ), 1, UnsizedFormat::L , false, ComponentType::Float);
+    AddFormatInfo(FOO(Alpha32F            ), 1, UnsizedFormat::A , false, ComponentType::Float);
 
-    // "Texture-only color formats"
-    FOO(RGBA32F);
-    FOO(RGBA16F);
-    FOO(RGBA8_SNORM);
-
-    FOO(RGB32F);
-    FOO(RGB32I);
-    FOO(RGB32UI);
-
-    FOO(RGB16F);
-    FOO(RGB16I);
-    FOO(RGB16UI);
-
-    FOO(RGB8_SNORM);
-    FOO(RGB8I);
-    FOO(RGB8UI);
-    FOO(SRGB8);
-
-    FOO(R11F_G11F_B10F);
-    FOO(RGB9_E5);
-
-    FOO(RG32F);
-    FOO(RG16F);
-    FOO(RG8_SNORM);
-
-    FOO(R32F);
-    FOO(R16F);
-    FOO(R8_SNORM);
-
-    // "Depth formats"
-    FOO(DEPTH_COMPONENT32F);
-    FOO(DEPTH_COMPONENT24);
-    FOO(DEPTH_COMPONENT16);
-
-    // "Combined depth+stencil formats"
-    FOO(DEPTH32F_STENCIL8);
-    FOO(DEPTH24_STENCIL8);
-
-    // GLES 3.0.4, p205-206, "Required Renderbuffer Formats"
-    FOO(STENCIL_INDEX8);
+    // OES_texture_half_float
+    AddFormatInfo(FOO(Luminance16FAlpha16F), 2, UnsizedFormat::LA, false, ComponentType::Float);
+    AddFormatInfo(FOO(Luminance16F        ), 1, UnsizedFormat::L , false, ComponentType::Float);
+    AddFormatInfo(FOO(Alpha16F            ), 1, UnsizedFormat::A , false, ComponentType::Float);
 
 #undef FOO
 }
 
 //////////////////////////////////////////////////////////////////////////////////////////
 
 bool gAreFormatTablesInitialized = false;
 
 static void
-EnsureInitFormatTables()
+EnsureInitFormatTables(const StaticMutexAutoLock&) // Prove that you locked it!
 {
     if (MOZ_LIKELY(gAreFormatTablesInitialized))
         return;
 
     gAreFormatTablesInitialized = true;
 
     InitCompressedFormatInfo();
-    InitFormatInfoMap();
-    InitUnpackTupleMap();
-    InitSizedFormatMap();
+    InitFormatInfo();
 }
 
 //////////////////////////////////////////////////////////////////////////////////////////
 // Public funcs
 
 StaticMutex gFormatMapMutex;
 
 const FormatInfo*
-GetFormatInfo(EffectiveFormat format)
+GetFormat(EffectiveFormat format)
 {
     StaticMutexAutoLock lock(gFormatMapMutex);
-    EnsureInitFormatTables();
+    EnsureInitFormatTables(lock);
 
     return GetFormatInfo_NoLock(format);
 }
 
-const FormatInfo*
-GetInfoByUnpackTuple(GLenum unpackFormat, GLenum unpackType)
+//////////////////////////////////////////////////////////////////////////////////////////
+
+uint8_t
+BytesPerPixel(const PackingInfo& packing)
 {
-    StaticMutexAutoLock lock(gFormatMapMutex);
-    EnsureInitFormatTables();
+    uint8_t bytesPerChannel;
+    switch (packing.type) {
+    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:
+        return 2;
+
+    case LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV:
+    case LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV:
+    case LOCAL_GL_UNSIGNED_INT_24_8:
+    case LOCAL_GL_UNSIGNED_INT_5_9_9_9_REV:
+        return 4;
+
+    case LOCAL_GL_FLOAT_32_UNSIGNED_INT_24_8_REV:
+        return 8;
+
+    // Alright, that's all the fixed-size unpackTypes.
+
+    case LOCAL_GL_BYTE:
+    case LOCAL_GL_UNSIGNED_BYTE:
+        bytesPerChannel = 1;
+        break;
+
+    case LOCAL_GL_SHORT:
+    case LOCAL_GL_UNSIGNED_SHORT:
+    case LOCAL_GL_HALF_FLOAT:
+    case LOCAL_GL_HALF_FLOAT_OES:
+        bytesPerChannel = 2;
+        break;
 
-    const UnpackTuple unpack = { unpackFormat, unpackType };
+    case LOCAL_GL_INT:
+    case LOCAL_GL_UNSIGNED_INT:
+    case LOCAL_GL_FLOAT:
+        bytesPerChannel = 4;
+        break;
+
+    default:
+        MOZ_CRASH("invalid PackingInfo");
+    }
 
-    MOZ_ASSERT(!gUnpackTupleMap.empty());
-    auto itr = gUnpackTupleMap.find(unpack);
-    if (itr == gUnpackTupleMap.end())
-        return nullptr;
+    uint8_t channels;
+    switch (packing.format) {
+    case LOCAL_GL_RG:
+    case LOCAL_GL_RG_INTEGER:
+    case LOCAL_GL_LUMINANCE_ALPHA:
+        channels = 2;
+        break;
 
-    return itr->second;
+    case LOCAL_GL_RGB:
+    case LOCAL_GL_RGB_INTEGER:
+        channels = 3;
+        break;
+
+    case LOCAL_GL_RGBA:
+    case LOCAL_GL_RGBA_INTEGER:
+        channels = 4;
+        break;
+
+    default:
+        channels = 1;
+        break;
+    }
+
+    return bytesPerChannel * channels;
 }
 
-const FormatInfo*
-GetInfoBySizedFormat(GLenum sizedFormat)
-{
-    StaticMutexAutoLock lock(gFormatMapMutex);
-    EnsureInitFormatTables();
-
-    MOZ_ASSERT(!gSizedFormatMap.empty());
-    auto itr = gSizedFormatMap.find(sizedFormat);
-    if (itr == gSizedFormatMap.end())
-        return nullptr;
-
-    return itr->second;
-}
-
-//////////////////////////////////////////////////////////////////////////////////////////
-
-bool
-FormatUsageInfo::CanUnpackWith(GLenum unpackFormat, GLenum unpackType) const
-{
-    const UnpackTuple key = { unpackFormat, unpackType };
-    auto itr = validUnpacks.find(key);
-    return itr != validUnpacks.end();
-}
 
 //////////////////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////////////////
 // FormatUsageAuthority
 
+void
+FormatUsageInfo::AddUnpack(const PackingInfo& key, const DriverUnpackInfo& value)
+{
+    auto itr = AlwaysInsert(validUnpacks, key, value);
+
+    if (!idealUnpack) {
+        // First one!
+        idealUnpack = &(itr->second);
+    }
+}
+
+bool
+FormatUsageInfo::IsUnpackValid(const PackingInfo& key,
+                               const DriverUnpackInfo** const out_value) const
+{
+    auto itr = validUnpacks.find(key);
+    if (itr == validUnpacks.end())
+        return false;
+
+    *out_value = &(itr->second);
+    return true;
+}
+
+////////////////////////////////////////
+
+static inline void
+SetUsage(FormatUsageAuthority* fua, EffectiveFormat effFormat, bool isRenderable,
+         bool isFilterable)
+{
+    MOZ_ASSERT(!fua->GetUsage(effFormat));
+
+    auto usage = fua->EditUsage(effFormat);
+    usage->isRenderable = isRenderable;
+    usage->isFilterable = isFilterable;
+}
+
+static void
+AddLegacyFormats_LA8(FormatUsageAuthority* fua, gl::GLContext* gl)
+{
+    PackingInfo pi;
+    DriverUnpackInfo dui;
+
+    const auto fnAdd = [fua, &pi, &dui](EffectiveFormat effFormat) {
+        auto usage = fua->EditUsage(effFormat);
+        fua->AddUnsizedTexFormat(pi, usage);
+        usage->AddUnpack(pi, dui);
+    };
+
+    const bool isCore = gl->IsCoreProfile();
+
+    pi = {LOCAL_GL_LUMINANCE, LOCAL_GL_UNSIGNED_BYTE};
+    if (isCore) dui = {LOCAL_GL_R8, LOCAL_GL_RED, LOCAL_GL_UNSIGNED_BYTE};
+    else        dui = {LOCAL_GL_LUMINANCE, LOCAL_GL_LUMINANCE, LOCAL_GL_UNSIGNED_BYTE};
+    fnAdd(EffectiveFormat::Luminance8);
+
+    pi = {LOCAL_GL_ALPHA, LOCAL_GL_UNSIGNED_BYTE};
+    if (isCore) dui = {LOCAL_GL_R8, LOCAL_GL_RED, LOCAL_GL_UNSIGNED_BYTE};
+    else        dui = {LOCAL_GL_ALPHA, LOCAL_GL_ALPHA, LOCAL_GL_UNSIGNED_BYTE};
+    fnAdd(EffectiveFormat::Alpha8);
+
+    pi = {LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_UNSIGNED_BYTE};
+    if (isCore) dui = {LOCAL_GL_RG8, LOCAL_GL_RG, LOCAL_GL_UNSIGNED_BYTE};
+    else        dui = {LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_UNSIGNED_BYTE};
+    fnAdd(EffectiveFormat::Luminance8Alpha8);
+}
+
+static void
+AddBasicUnsizedFormats(FormatUsageAuthority* fua, gl::GLContext* gl)
+{
+    const auto fnAddSimpleUnsized = [fua](GLenum unpackFormat, GLenum unpackType,
+                                          EffectiveFormat effFormat)
+    {
+        auto usage = fua->EditUsage(effFormat);
+
+        const PackingInfo pi = {unpackFormat, unpackType};
+        fua->AddUnsizedTexFormat(pi, usage);
+
+        const DriverUnpackInfo dui = {unpackFormat, unpackFormat, unpackType};
+        usage->AddUnpack(pi, dui);
+    };
+
+    // GLES 2.0.25, p63, Table 3.4
+
+    fnAddSimpleUnsized(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE         , EffectiveFormat::RGBA8  );
+    fnAddSimpleUnsized(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_SHORT_4_4_4_4, EffectiveFormat::RGBA4  );
+    fnAddSimpleUnsized(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_SHORT_5_5_5_1, EffectiveFormat::RGB5_A1);
+    fnAddSimpleUnsized(LOCAL_GL_RGB , LOCAL_GL_UNSIGNED_BYTE         , EffectiveFormat::RGB8   );
+    fnAddSimpleUnsized(LOCAL_GL_RGB , LOCAL_GL_UNSIGNED_SHORT_5_6_5  , EffectiveFormat::RGB565 );
+
+    // L, A, LA
+    AddLegacyFormats_LA8(fua, gl);
+}
+
 UniquePtr<FormatUsageAuthority>
 FormatUsageAuthority::CreateForWebGL1(gl::GLContext* gl)
 {
     UniquePtr<FormatUsageAuthority> ret(new FormatUsageAuthority);
+    const auto ptr = ret.get();
 
     ////////////////////////////////////////////////////////////////////////////
-
-#ifdef FOO
-#error FOO is already defined!
-#endif
-
-#define FOO(effFormat,rbFormat,isRenderable,texInternalFormat,texUnpackFormat,texUnpackType,isFilterable) \
-    ret->AddFormat(EffectiveFormat::effFormat, LOCAL_GL_ ## rbFormat, isRenderable,                       \
-                   LOCAL_GL_ ## texInternalFormat, LOCAL_GL_ ## texUnpackFormat,                          \
-                   LOCAL_GL_ ## texUnpackType, isFilterable)
+    // Usages
 
     // GLES 2.0.25, p117, Table 4.5
     // RGBA8 is made renderable in WebGL 1.0, "Framebuffer Object Attachments"
 
-    //                   |                  |render|tex internal    |tex unpack      |                       |filter
-    //  effective format |RB format         |able  |format          |format          |tex unpack type        |able
+    //                                      render filter
+    //                                      able   able
+    SetUsage(ptr, EffectiveFormat::RGBA8  , true , true);
+    SetUsage(ptr, EffectiveFormat::RGBA4  , true , true);
+    SetUsage(ptr, EffectiveFormat::RGB5_A1, true , true);
+    SetUsage(ptr, EffectiveFormat::RGB8   , false, true);
+    SetUsage(ptr, EffectiveFormat::RGB565 , true , true);
 
-    FOO(RGBA8            , NONE             , true , RGBA           , RGBA           , UNSIGNED_BYTE         , true );
-    FOO(RGBA4            , RGBA4            , true , RGBA           , RGBA           , UNSIGNED_SHORT_4_4_4_4, true );
-    FOO(RGB5_A1          , RGB5_A1          , true , RGBA           , RGBA           , UNSIGNED_SHORT_5_5_5_1, true );
-    FOO(RGB8             , NONE             , false, RGB            , RGB            , UNSIGNED_BYTE         , true );
-    FOO(RGB565           , RGB565           , true , RGB            , RGB            , UNSIGNED_SHORT_5_6_5  , true );
+    SetUsage(ptr, EffectiveFormat::Luminance8Alpha8, false, true);
+    SetUsage(ptr, EffectiveFormat::Luminance8      , false, true);
+    SetUsage(ptr, EffectiveFormat::Alpha8          , false, true);
 
-    FOO(Luminance8Alpha8 , NONE             , false, LUMINANCE_ALPHA, LUMINANCE_ALPHA, UNSIGNED_BYTE         , true );
-    FOO(Luminance8       , NONE             , false, LUMINANCE      , LUMINANCE      , UNSIGNED_BYTE         , true );
-    FOO(Alpha8           , NONE             , false, ALPHA          , ALPHA          , UNSIGNED_BYTE         , true );
-
-    FOO(DEPTH_COMPONENT16, DEPTH_COMPONENT16, true , NONE           , NONE           , NONE                  , false);
-    FOO(STENCIL_INDEX8   , STENCIL_INDEX8   , true , NONE           , NONE           , NONE                  , false);
+    SetUsage(ptr, EffectiveFormat::DEPTH_COMPONENT16, true, false);
+    SetUsage(ptr, EffectiveFormat::STENCIL_INDEX8   , true, false);
 
     // Added in WebGL 1.0 spec:
-    FOO(DEPTH24_STENCIL8 , DEPTH24_STENCIL8 , true , NONE           , NONE           , NONE                  , false);
+    SetUsage(ptr, EffectiveFormat::DEPTH24_STENCIL8, true, false);
+
+    ////////////////////////////////////
+    // RB formats
+
+#define FOO(x) ptr->AddRBFormat(LOCAL_GL_ ## x, ptr->GetUsage(EffectiveFormat::x))
+
+    FOO(RGBA4            );
+    FOO(RGB5_A1          );
+    FOO(RGB565           );
+    FOO(DEPTH_COMPONENT16);
+    FOO(STENCIL_INDEX8   );
+    //FOO(DEPTH24_STENCIL8 );
 
 #undef FOO
 
+    ptr->AddRBFormat(LOCAL_GL_DEPTH_STENCIL,
+                     ptr->GetUsage(EffectiveFormat::DEPTH24_STENCIL8));
+
     ////////////////////////////////////////////////////////////////////////////
 
-    // GLES 2.0.25, p63, Table 3.4
-
-    ret->AddUnpackOption(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE         , EffectiveFormat::RGBA8  );
-    ret->AddUnpackOption(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_SHORT_4_4_4_4, EffectiveFormat::RGBA4  );
-    ret->AddUnpackOption(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_SHORT_5_5_5_1, EffectiveFormat::RGB5_A1);
-    ret->AddUnpackOption(LOCAL_GL_RGB , LOCAL_GL_UNSIGNED_BYTE         , EffectiveFormat::RGB8   );
-    ret->AddUnpackOption(LOCAL_GL_RGB , LOCAL_GL_UNSIGNED_SHORT_5_6_5  , EffectiveFormat::RGB565 );
-
-    ret->AddUnpackOption(LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_UNSIGNED_BYTE, EffectiveFormat::Luminance8Alpha8);
-    ret->AddUnpackOption(LOCAL_GL_LUMINANCE      , LOCAL_GL_UNSIGNED_BYTE, EffectiveFormat::Luminance8      );
-    ret->AddUnpackOption(LOCAL_GL_ALPHA          , LOCAL_GL_UNSIGNED_BYTE, EffectiveFormat::Alpha8          );
-
+    AddBasicUnsizedFormats(ptr, gl);
 
     return Move(ret);
 }
 
-static void
-AddES3TexFormat(FormatUsageAuthority* that, EffectiveFormat format, GLenum sizedFormat,
-                bool isRenderable, GLenum unpackFormat, GLenum unpackType,
-                bool isFilterable)
-{
-    GLenum rbFormat = isRenderable ? sizedFormat : 0;
-
-    that->AddFormat(format, rbFormat, isRenderable, sizedFormat, unpackFormat, unpackType,
-                    isFilterable);
-}
-
 UniquePtr<FormatUsageAuthority>
-FormatUsageAuthority::CreateForWebGL2()
+FormatUsageAuthority::CreateForWebGL2(gl::GLContext* gl)
 {
     UniquePtr<FormatUsageAuthority> ret(new FormatUsageAuthority);
     FormatUsageAuthority* const ptr = ret.get();
 
-    ////////////////////////////////////////////////////////////////////////////
+    const auto fnAddES3TexFormat = [ptr](GLenum sizedFormat, EffectiveFormat effFormat,
+                                         bool isRenderable, bool isFilterable)
+    {
+        SetUsage(ptr, effFormat, isRenderable, isFilterable);
+        auto usage = ptr->GetUsage(effFormat);
+        ptr->AddSizedTexFormat(sizedFormat, usage);
 
-#ifdef FOO
-#error FOO is already defined!
-#endif
+        if (isRenderable) {
+            ptr->AddRBFormat(sizedFormat, usage);
+        }
+    };
 
-#define FOO(effFormat,sizedFormat,isRenderable,unpackFormat,unpackType,isFilterable) \
-    AddES3TexFormat(ptr, EffectiveFormat::effFormat, LOCAL_GL_ ## sizedFormat,       \
-                    isRenderable, LOCAL_GL_ ## unpackFormat, LOCAL_GL_ ## unpackType,\
-                    isFilterable)
+    ////////////////////////////////////////////////////////////////////////////
 
     // For renderable, see GLES 3.0.4, p212 "Framebuffer Completeness"
     // For filterable, see GLES 3.0.4, p161 "...a texture is complete unless..."
 
     // GLES 3.0.4, p128-129 "Required Texture Formats"
     // GLES 3.0.4, p130-132, table 3.13
 
-    //                   |                  |render|tex internal    |tex unpack      |                       |filter
-    //  effective format |RB format         |able  |format          |format          |tex unpack type        |able
-
-    FOO(R8               , NONE             , true , RGBA           , RGBA           , UNSIGNED_BYTE         , true );
+#define FOO(x) LOCAL_GL_ ## x, EffectiveFormat::x
 
-    //                                                render filter
-    //                                                 able   able
-    AddES3TexFormat(ptr, EffectiveFormat::R8         , true , true );
-    AddES3TexFormat(ptr, EffectiveFormat::R8_SNORM   , false, true );
-    AddES3TexFormat(ptr, EffectiveFormat::RG8        , true , true );
-    AddES3TexFormat(ptr, EffectiveFormat::RG8_SNORM  , false, true );
-    AddES3TexFormat(ptr, EffectiveFormat::RGB8       , true , true );
-    AddES3TexFormat(ptr, EffectiveFormat::RGB8_SNORM , false, true );
-    AddES3TexFormat(ptr, EffectiveFormat::RGB565     , true , true );
-    AddES3TexFormat(ptr, EffectiveFormat::RGBA4      , true , true );
-    AddES3TexFormat(ptr, EffectiveFormat::RGB5_A1    , true , true );
-    AddES3TexFormat(ptr, EffectiveFormat::RGBA8      , true , true );
-    AddES3TexFormat(ptr, EffectiveFormat::RGBA8_SNORM, false, true );
-    AddES3TexFormat(ptr, EffectiveFormat::RGB10_A2   , true , true );
-    AddES3TexFormat(ptr, EffectiveFormat::RGB10_A2UI , true , false);
+    //                                 render filter
+    //                                  able   able
+    fnAddES3TexFormat(FOO(R8         ), true , true );
+    fnAddES3TexFormat(FOO(R8_SNORM   ), false, true );
+    fnAddES3TexFormat(FOO(RG8        ), true , true );
+    fnAddES3TexFormat(FOO(RG8_SNORM  ), false, true );
+    fnAddES3TexFormat(FOO(RGB8       ), true , true );
+    fnAddES3TexFormat(FOO(RGB8_SNORM ), false, true );
+    fnAddES3TexFormat(FOO(RGB565     ), true , true );
+    fnAddES3TexFormat(FOO(RGBA4      ), true , true );
+    fnAddES3TexFormat(FOO(RGB5_A1    ), true , true );
+    fnAddES3TexFormat(FOO(RGBA8      ), true , true );
+    fnAddES3TexFormat(FOO(RGBA8_SNORM), false, true );
+    fnAddES3TexFormat(FOO(RGB10_A2   ), true , true );
+    fnAddES3TexFormat(FOO(RGB10_A2UI ), true , false);
 
-    AddES3TexFormat(ptr, EffectiveFormat::SRGB8       , false, true);
-    AddES3TexFormat(ptr, EffectiveFormat::SRGB8_ALPHA8, true , true);
+    fnAddES3TexFormat(FOO(SRGB8       ), false, true);
+    fnAddES3TexFormat(FOO(SRGB8_ALPHA8), true , true);
 
-    AddES3TexFormat(ptr, EffectiveFormat::R16F   , false, true);
-    AddES3TexFormat(ptr, EffectiveFormat::RG16F  , false, true);
-    AddES3TexFormat(ptr, EffectiveFormat::RGB16F , false, true);
-    AddES3TexFormat(ptr, EffectiveFormat::RGBA16F, false, true);
+    fnAddES3TexFormat(FOO(R16F   ), false, true);
+    fnAddES3TexFormat(FOO(RG16F  ), false, true);
+    fnAddES3TexFormat(FOO(RGB16F ), false, true);
+    fnAddES3TexFormat(FOO(RGBA16F), false, true);
+
+    fnAddES3TexFormat(FOO(R32F   ), false, false);
+    fnAddES3TexFormat(FOO(RG32F  ), false, false);
+    fnAddES3TexFormat(FOO(RGB32F ), false, false);
+    fnAddES3TexFormat(FOO(RGBA32F), false, false);
 
-    AddES3TexFormat(ptr, EffectiveFormat::R32F   , false, false);
-    AddES3TexFormat(ptr, EffectiveFormat::RG32F  , false, false);
-    AddES3TexFormat(ptr, EffectiveFormat::RGB32F , false, false);
-    AddES3TexFormat(ptr, EffectiveFormat::RGBA32F, false, false);
+    fnAddES3TexFormat(FOO(R11F_G11F_B10F), false, true);
+    fnAddES3TexFormat(FOO(RGB9_E5       ), false, true);
 
-    AddES3TexFormat(ptr, EffectiveFormat::R11F_G11F_B10F, false, true);
-    AddES3TexFormat(ptr, EffectiveFormat::RGB9_E5       , false, true);
-
-    AddES3TexFormat(ptr, EffectiveFormat::R8I  , true, false);
-    AddES3TexFormat(ptr, EffectiveFormat::R8UI , true, false);
-    AddES3TexFormat(ptr, EffectiveFormat::R16I , true, false);
-    AddES3TexFormat(ptr, EffectiveFormat::R16UI, true, false);
-    AddES3TexFormat(ptr, EffectiveFormat::R32I , true, false);
-    AddES3TexFormat(ptr, EffectiveFormat::R32UI, true, false);
+    fnAddES3TexFormat(FOO(R8I  ), true, false);
+    fnAddES3TexFormat(FOO(R8UI ), true, false);
+    fnAddES3TexFormat(FOO(R16I ), true, false);
+    fnAddES3TexFormat(FOO(R16UI), true, false);
+    fnAddES3TexFormat(FOO(R32I ), true, false);
+    fnAddES3TexFormat(FOO(R32UI), true, false);
 
-    AddES3TexFormat(ptr, EffectiveFormat::RG8I  , true, false);
-    AddES3TexFormat(ptr, EffectiveFormat::RG8UI , true, false);
-    AddES3TexFormat(ptr, EffectiveFormat::RG16I , true, false);
-    AddES3TexFormat(ptr, EffectiveFormat::RG16UI, true, false);
-    AddES3TexFormat(ptr, EffectiveFormat::RG32I , true, false);
-    AddES3TexFormat(ptr, EffectiveFormat::RG32UI, true, false);
+    fnAddES3TexFormat(FOO(RG8I  ), true, false);
+    fnAddES3TexFormat(FOO(RG8UI ), true, false);
+    fnAddES3TexFormat(FOO(RG16I ), true, false);
+    fnAddES3TexFormat(FOO(RG16UI), true, false);
+    fnAddES3TexFormat(FOO(RG32I ), true, false);
+    fnAddES3TexFormat(FOO(RG32UI), true, false);
 
-    AddES3TexFormat(ptr, EffectiveFormat::RGB8I  , false, false);
-    AddES3TexFormat(ptr, EffectiveFormat::RGB8UI , false, false);
-    AddES3TexFormat(ptr, EffectiveFormat::RGB16I , false, false);
-    AddES3TexFormat(ptr, EffectiveFormat::RGB16UI, false, false);
-    AddES3TexFormat(ptr, EffectiveFormat::RGB32I , false, false);
-    AddES3TexFormat(ptr, EffectiveFormat::RGB32UI, false, false);
+    fnAddES3TexFormat(FOO(RGB8I  ), false, false);
+    fnAddES3TexFormat(FOO(RGB8UI ), false, false);
+    fnAddES3TexFormat(FOO(RGB16I ), false, false);
+    fnAddES3TexFormat(FOO(RGB16UI), false, false);
+    fnAddES3TexFormat(FOO(RGB32I ), false, false);
+    fnAddES3TexFormat(FOO(RGB32UI), false, false);
 
-    AddES3TexFormat(ptr, EffectiveFormat::RGBA8I  , true, false);
-    AddES3TexFormat(ptr, EffectiveFormat::RGBA8UI , true, false);
-    AddES3TexFormat(ptr, EffectiveFormat::RGBA16I , true, false);
-    AddES3TexFormat(ptr, EffectiveFormat::RGBA16UI, true, false);
-    AddES3TexFormat(ptr, EffectiveFormat::RGBA32I , true, false);
-    AddES3TexFormat(ptr, EffectiveFormat::RGBA32UI, true, false);
+    fnAddES3TexFormat(FOO(RGBA8I  ), true, false);
+    fnAddES3TexFormat(FOO(RGBA8UI ), true, false);
+    fnAddES3TexFormat(FOO(RGBA16I ), true, false);
+    fnAddES3TexFormat(FOO(RGBA16UI), true, false);
+    fnAddES3TexFormat(FOO(RGBA32I ), true, false);
+    fnAddES3TexFormat(FOO(RGBA32UI), true, false);
 
     // GLES 3.0.4, p133, table 3.14
     // GLES 3.0.4, p161 "...a texture is complete unless..."
-    AddES3TexFormat(ptr, EffectiveFormat::DEPTH_COMPONENT16 , true, false);
-    AddES3TexFormat(ptr, EffectiveFormat::DEPTH_COMPONENT24 , true, false);
-    AddES3TexFormat(ptr, EffectiveFormat::DEPTH_COMPONENT32F, true, false);
-    AddES3TexFormat(ptr, EffectiveFormat::DEPTH24_STENCIL8  , true, false);
-    AddES3TexFormat(ptr, EffectiveFormat::DEPTH32F_STENCIL8 , true, false);
+    fnAddES3TexFormat(FOO(DEPTH_COMPONENT16 ), true, false);
+    fnAddES3TexFormat(FOO(DEPTH_COMPONENT24 ), true, false);
+    fnAddES3TexFormat(FOO(DEPTH_COMPONENT32F), true, false);
+    fnAddES3TexFormat(FOO(DEPTH24_STENCIL8  ), true, false);
+    fnAddES3TexFormat(FOO(DEPTH32F_STENCIL8 ), true, false);
 
     // GLES 3.0.4, p205-206, "Required Renderbuffer Formats"
-    AddES3TexFormat(ptr, EffectiveFormat::STENCIL_INDEX8, true, false);
-
+    fnAddES3TexFormat(FOO(STENCIL_INDEX8), true, false);
+/*
     // GLES 3.0.4, p128, table 3.12.
     // Unsized RGBA/RGB formats are renderable, other unsized are not.
-    AddES3TexFormat(ptr, EffectiveFormat::Luminance8Alpha8, false, true);
-    AddES3TexFormat(ptr, EffectiveFormat::Luminance8      , false, true);
-    AddES3TexFormat(ptr, EffectiveFormat::Alpha8          , false, true);
-
+    fnAddES3TexFormat(FOO(Luminance8Alpha8, false, true);
+    fnAddES3TexFormat(FOO(Luminance8      , false, true);
+    fnAddES3TexFormat(FOO(Alpha8          , false, true);
+*/
     // GLES 3.0.4, p147, table 3.19
     // GLES 3.0.4, p286+, $C.1 "ETC Compressed Texture Image Formats"
     // (jgilbert) I can't find where these are established as filterable.
-    AddES3TexFormat(ptr, EffectiveFormat::COMPRESSED_RGB8_ETC2                     , false, true);
-    AddES3TexFormat(ptr, EffectiveFormat::COMPRESSED_SRGB8_ETC2                    , false, true);
-    AddES3TexFormat(ptr, EffectiveFormat::COMPRESSED_RGBA8_ETC2_EAC                , false, true);
-    AddES3TexFormat(ptr, EffectiveFormat::COMPRESSED_SRGB8_ALPHA8_ETC2_EAC         , false, true);
-    AddES3TexFormat(ptr, EffectiveFormat::COMPRESSED_R11_EAC                       , false, true);
-    AddES3TexFormat(ptr, EffectiveFormat::COMPRESSED_RG11_EAC                      , false, true);
-    AddES3TexFormat(ptr, EffectiveFormat::COMPRESSED_SIGNED_R11_EAC                , false, true);
-    AddES3TexFormat(ptr, EffectiveFormat::COMPRESSED_SIGNED_RG11_EAC               , false, true);
-    AddES3TexFormat(ptr, EffectiveFormat::COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 , false, true);
-    AddES3TexFormat(ptr, EffectiveFormat::COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2, false, true);
+    fnAddES3TexFormat(FOO(COMPRESSED_RGB8_ETC2                     ), false, true);
+    fnAddES3TexFormat(FOO(COMPRESSED_SRGB8_ETC2                    ), false, true);
+    fnAddES3TexFormat(FOO(COMPRESSED_RGBA8_ETC2_EAC                ), false, true);
+    fnAddES3TexFormat(FOO(COMPRESSED_SRGB8_ALPHA8_ETC2_EAC         ), false, true);
+    fnAddES3TexFormat(FOO(COMPRESSED_R11_EAC                       ), false, true);
+    fnAddES3TexFormat(FOO(COMPRESSED_RG11_EAC                      ), false, true);
+    fnAddES3TexFormat(FOO(COMPRESSED_SIGNED_R11_EAC                ), false, true);
+    fnAddES3TexFormat(FOO(COMPRESSED_SIGNED_RG11_EAC               ), false, true);
+    fnAddES3TexFormat(FOO(COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 ), false, true);
+    fnAddES3TexFormat(FOO(COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2), false, true);
+
+#undef FOO
+
+    const auto fnAddSizedUnpack = [ptr](EffectiveFormat effFormat, GLenum internalFormat,
+                                        GLenum unpackFormat, GLenum unpackType)
+    {
+        auto usage = ptr->EditUsage(effFormat);
+
+        const PackingInfo pi = {unpackFormat, unpackType};
+        const DriverUnpackInfo dui = {internalFormat, unpackFormat, unpackType};
+        usage->AddUnpack(pi, dui);
+    };
+
+#define FOO(x) EffectiveFormat::x, LOCAL_GL_ ## x
 
     ////////////////////////////////////////////////////////////////////////////
     // GLES 3.0.4 p111-113
     // RGBA
-    ret->AddUnpackOption(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE              , EffectiveFormat::RGBA8       );
-    ret->AddUnpackOption(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE              , EffectiveFormat::RGB5_A1     );
-    ret->AddUnpackOption(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE              , EffectiveFormat::RGBA4       );
-    ret->AddUnpackOption(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE              , EffectiveFormat::SRGB8_ALPHA8);
-    ret->AddUnpackOption(LOCAL_GL_RGBA, LOCAL_GL_BYTE                       , EffectiveFormat::RGBA8_SNORM );
-    ret->AddUnpackOption(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_SHORT_4_4_4_4     , EffectiveFormat::RGBA4       );
-    ret->AddUnpackOption(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_SHORT_5_5_5_1     , EffectiveFormat::RGB5_A1     );
-    ret->AddUnpackOption(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV, EffectiveFormat::RGB10_A2    );
-    ret->AddUnpackOption(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV, EffectiveFormat::RGB5_A1     );
-    ret->AddUnpackOption(LOCAL_GL_RGBA, LOCAL_GL_HALF_FLOAT                 , EffectiveFormat::RGBA16F     );
-    ret->AddUnpackOption(LOCAL_GL_RGBA, LOCAL_GL_FLOAT                      , EffectiveFormat::RGBA32F     );
-    ret->AddUnpackOption(LOCAL_GL_RGBA, LOCAL_GL_FLOAT                      , EffectiveFormat::RGBA16F     );
+    fnAddSizedUnpack(FOO(RGBA8       ), LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE              );
+    fnAddSizedUnpack(FOO(RGBA4       ), LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_SHORT_4_4_4_4     );
+    fnAddSizedUnpack(FOO(RGBA4       ), LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE              );
+    fnAddSizedUnpack(FOO(RGB5_A1     ), LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_SHORT_5_5_5_1     );
+    fnAddSizedUnpack(FOO(RGB5_A1     ), LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE              );
+    fnAddSizedUnpack(FOO(RGB5_A1     ), LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV);
+    fnAddSizedUnpack(FOO(SRGB8_ALPHA8), LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE              );
+    fnAddSizedUnpack(FOO(RGBA8_SNORM ), LOCAL_GL_RGBA, LOCAL_GL_BYTE                       );
+    fnAddSizedUnpack(FOO(RGB10_A2    ), LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV);
+    fnAddSizedUnpack(FOO(RGBA16F     ), LOCAL_GL_RGBA, LOCAL_GL_HALF_FLOAT                 );
+    fnAddSizedUnpack(FOO(RGBA16F     ), LOCAL_GL_RGBA, LOCAL_GL_FLOAT                      );
+    fnAddSizedUnpack(FOO(RGBA32F     ), LOCAL_GL_RGBA, LOCAL_GL_FLOAT                      );
+
     // RGBA_INTEGER
-    ret->AddUnpackOption(LOCAL_GL_RGBA_INTEGER, LOCAL_GL_UNSIGNED_BYTE              , EffectiveFormat::RGBA8UI   );
-    ret->AddUnpackOption(LOCAL_GL_RGBA_INTEGER, LOCAL_GL_BYTE                       , EffectiveFormat::RGBA8I    );
-    ret->AddUnpackOption(LOCAL_GL_RGBA_INTEGER, LOCAL_GL_UNSIGNED_SHORT             , EffectiveFormat::RGBA16UI  );
-    ret->AddUnpackOption(LOCAL_GL_RGBA_INTEGER, LOCAL_GL_SHORT                      , EffectiveFormat::RGBA16I   );
-    ret->AddUnpackOption(LOCAL_GL_RGBA_INTEGER, LOCAL_GL_UNSIGNED_INT               , EffectiveFormat::RGBA32UI  );
-    ret->AddUnpackOption(LOCAL_GL_RGBA_INTEGER, LOCAL_GL_INT                        , EffectiveFormat::RGBA32I   );
-    ret->AddUnpackOption(LOCAL_GL_RGBA_INTEGER, LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV, EffectiveFormat::RGB10_A2UI);
+    fnAddSizedUnpack(FOO(RGBA8UI   ), LOCAL_GL_RGBA_INTEGER, LOCAL_GL_UNSIGNED_BYTE              );
+    fnAddSizedUnpack(FOO(RGBA8I    ), LOCAL_GL_RGBA_INTEGER, LOCAL_GL_BYTE                       );
+    fnAddSizedUnpack(FOO(RGBA16UI  ), LOCAL_GL_RGBA_INTEGER, LOCAL_GL_UNSIGNED_SHORT             );
+    fnAddSizedUnpack(FOO(RGBA16I   ), LOCAL_GL_RGBA_INTEGER, LOCAL_GL_SHORT                      );
+    fnAddSizedUnpack(FOO(RGBA32UI  ), LOCAL_GL_RGBA_INTEGER, LOCAL_GL_UNSIGNED_INT               );
+    fnAddSizedUnpack(FOO(RGBA32I   ), LOCAL_GL_RGBA_INTEGER, LOCAL_GL_INT                        );
+    fnAddSizedUnpack(FOO(RGB10_A2UI), LOCAL_GL_RGBA_INTEGER, LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV);
 
     // RGB
-    ret->AddUnpackOption(LOCAL_GL_RGB, LOCAL_GL_UNSIGNED_BYTE               , EffectiveFormat::RGB8          );
-    ret->AddUnpackOption(LOCAL_GL_RGB, LOCAL_GL_UNSIGNED_BYTE               , EffectiveFormat::RGB565        );
-    ret->AddUnpackOption(LOCAL_GL_RGB, LOCAL_GL_UNSIGNED_BYTE               , EffectiveFormat::SRGB8         );
-    ret->AddUnpackOption(LOCAL_GL_RGB, LOCAL_GL_BYTE                        , EffectiveFormat::RGB8_SNORM    );
-    ret->AddUnpackOption(LOCAL_GL_RGB, LOCAL_GL_UNSIGNED_SHORT_5_6_5        , EffectiveFormat::RGB565        );
-    ret->AddUnpackOption(LOCAL_GL_RGB, LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV, EffectiveFormat::R11F_G11F_B10F);
-    ret->AddUnpackOption(LOCAL_GL_RGB, LOCAL_GL_UNSIGNED_INT_5_9_9_9_REV    , EffectiveFormat::RGB9_E5       );
-    ret->AddUnpackOption(LOCAL_GL_RGB, LOCAL_GL_HALF_FLOAT                  , EffectiveFormat::RGB16F        );
-    ret->AddUnpackOption(LOCAL_GL_RGB, LOCAL_GL_HALF_FLOAT                  , EffectiveFormat::R11F_G11F_B10F);
-    ret->AddUnpackOption(LOCAL_GL_RGB, LOCAL_GL_HALF_FLOAT                  , EffectiveFormat::RGB9_E5       );
-    ret->AddUnpackOption(LOCAL_GL_RGB, LOCAL_GL_FLOAT                       , EffectiveFormat::RGB32F        );
-    ret->AddUnpackOption(LOCAL_GL_RGB, LOCAL_GL_FLOAT                       , EffectiveFormat::RGB16F        );
-    ret->AddUnpackOption(LOCAL_GL_RGB, LOCAL_GL_FLOAT                       , EffectiveFormat::R11F_G11F_B10F);
-    ret->AddUnpackOption(LOCAL_GL_RGB, LOCAL_GL_FLOAT                       , EffectiveFormat::RGB9_E5       );
+    fnAddSizedUnpack(FOO(RGB8          ), LOCAL_GL_RGB, LOCAL_GL_UNSIGNED_BYTE               );
+    fnAddSizedUnpack(FOO(SRGB8         ), LOCAL_GL_RGB, LOCAL_GL_UNSIGNED_BYTE               );
+    fnAddSizedUnpack(FOO(RGB565        ), LOCAL_GL_RGB, LOCAL_GL_UNSIGNED_SHORT_5_6_5        );
+    fnAddSizedUnpack(FOO(RGB565        ), LOCAL_GL_RGB, LOCAL_GL_UNSIGNED_BYTE               );
+    fnAddSizedUnpack(FOO(RGB8_SNORM    ), LOCAL_GL_RGB, LOCAL_GL_BYTE                        );
+    fnAddSizedUnpack(FOO(R11F_G11F_B10F), LOCAL_GL_RGB, LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV);
+    fnAddSizedUnpack(FOO(R11F_G11F_B10F), LOCAL_GL_RGB, LOCAL_GL_HALF_FLOAT                  );
+    fnAddSizedUnpack(FOO(R11F_G11F_B10F), LOCAL_GL_RGB, LOCAL_GL_FLOAT                       );
+    fnAddSizedUnpack(FOO(RGB16F        ), LOCAL_GL_RGB, LOCAL_GL_HALF_FLOAT                  );
+    fnAddSizedUnpack(FOO(RGB16F        ), LOCAL_GL_RGB, LOCAL_GL_FLOAT                       );
+    fnAddSizedUnpack(FOO(RGB9_E5       ), LOCAL_GL_RGB, LOCAL_GL_UNSIGNED_INT_5_9_9_9_REV    );
+    fnAddSizedUnpack(FOO(RGB9_E5       ), LOCAL_GL_RGB, LOCAL_GL_HALF_FLOAT                  );
+    fnAddSizedUnpack(FOO(RGB9_E5       ), LOCAL_GL_RGB, LOCAL_GL_FLOAT                       );
+    fnAddSizedUnpack(FOO(RGB32F        ), LOCAL_GL_RGB, LOCAL_GL_FLOAT                       );
 
     // RGB_INTEGER
-    ret->AddUnpackOption(LOCAL_GL_RGB_INTEGER, LOCAL_GL_UNSIGNED_BYTE , EffectiveFormat::RGB8UI );
-    ret->AddUnpackOption(LOCAL_GL_RGB_INTEGER, LOCAL_GL_BYTE          , EffectiveFormat::RGB8I  );
-    ret->AddUnpackOption(LOCAL_GL_RGB_INTEGER, LOCAL_GL_UNSIGNED_SHORT, EffectiveFormat::RGB16UI);
-    ret->AddUnpackOption(LOCAL_GL_RGB_INTEGER, LOCAL_GL_SHORT         , EffectiveFormat::RGB16I );
-    ret->AddUnpackOption(LOCAL_GL_RGB_INTEGER, LOCAL_GL_UNSIGNED_INT  , EffectiveFormat::RGB32UI);
-    ret->AddUnpackOption(LOCAL_GL_RGB_INTEGER, LOCAL_GL_INT           , EffectiveFormat::RGB32I );
-
+    fnAddSizedUnpack(FOO(RGB8UI ), LOCAL_GL_RGB_INTEGER, LOCAL_GL_UNSIGNED_BYTE );
+    fnAddSizedUnpack(FOO(RGB8I  ), LOCAL_GL_RGB_INTEGER, LOCAL_GL_BYTE          );
+    fnAddSizedUnpack(FOO(RGB16UI), LOCAL_GL_RGB_INTEGER, LOCAL_GL_UNSIGNED_SHORT);
+    fnAddSizedUnpack(FOO(RGB16I ), LOCAL_GL_RGB_INTEGER, LOCAL_GL_SHORT         );
+    fnAddSizedUnpack(FOO(RGB32UI), LOCAL_GL_RGB_INTEGER, LOCAL_GL_UNSIGNED_INT  );
+    fnAddSizedUnpack(FOO(RGB32I ), LOCAL_GL_RGB_INTEGER, LOCAL_GL_INT           );
 
     // RG
-    ret->AddUnpackOption(LOCAL_GL_RG, LOCAL_GL_UNSIGNED_BYTE, EffectiveFormat::RG8      );
-    ret->AddUnpackOption(LOCAL_GL_RG, LOCAL_GL_BYTE         , EffectiveFormat::RG8_SNORM);
-    ret->AddUnpackOption(LOCAL_GL_RG, LOCAL_GL_HALF_FLOAT   , EffectiveFormat::RG16F    );
-    ret->AddUnpackOption(LOCAL_GL_RG, LOCAL_GL_FLOAT        , EffectiveFormat::RG32F    );
-    ret->AddUnpackOption(LOCAL_GL_RG, LOCAL_GL_FLOAT        , EffectiveFormat::RG16F    );
+    fnAddSizedUnpack(FOO(RG8      ), LOCAL_GL_RG, LOCAL_GL_UNSIGNED_BYTE);
+    fnAddSizedUnpack(FOO(RG8_SNORM), LOCAL_GL_RG, LOCAL_GL_BYTE         );
+    fnAddSizedUnpack(FOO(RG16F    ), LOCAL_GL_RG, LOCAL_GL_HALF_FLOAT   );
+    fnAddSizedUnpack(FOO(RG16F    ), LOCAL_GL_RG, LOCAL_GL_FLOAT        );
+    fnAddSizedUnpack(FOO(RG32F    ), LOCAL_GL_RG, LOCAL_GL_FLOAT        );
 
     // RG_INTEGER
-    ret->AddUnpackOption(LOCAL_GL_RG_INTEGER, LOCAL_GL_UNSIGNED_BYTE , EffectiveFormat::RG8UI );
-    ret->AddUnpackOption(LOCAL_GL_RG_INTEGER, LOCAL_GL_BYTE          , EffectiveFormat::RG8I  );
-    ret->AddUnpackOption(LOCAL_GL_RG_INTEGER, LOCAL_GL_UNSIGNED_SHORT, EffectiveFormat::RG16UI);
-    ret->AddUnpackOption(LOCAL_GL_RG_INTEGER, LOCAL_GL_SHORT         , EffectiveFormat::RG16I );
-    ret->AddUnpackOption(LOCAL_GL_RG_INTEGER, LOCAL_GL_UNSIGNED_INT  , EffectiveFormat::RG32UI);
-    ret->AddUnpackOption(LOCAL_GL_RG_INTEGER, LOCAL_GL_INT           , EffectiveFormat::RG32I );
+    fnAddSizedUnpack(FOO(RG8UI ), LOCAL_GL_RG_INTEGER, LOCAL_GL_UNSIGNED_BYTE );
+    fnAddSizedUnpack(FOO(RG8I  ), LOCAL_GL_RG_INTEGER, LOCAL_GL_BYTE          );
+    fnAddSizedUnpack(FOO(RG16UI), LOCAL_GL_RG_INTEGER, LOCAL_GL_UNSIGNED_SHORT);
+    fnAddSizedUnpack(FOO(RG16I ), LOCAL_GL_RG_INTEGER, LOCAL_GL_SHORT         );
+    fnAddSizedUnpack(FOO(RG32UI), LOCAL_GL_RG_INTEGER, LOCAL_GL_UNSIGNED_INT  );
+    fnAddSizedUnpack(FOO(RG32I ), LOCAL_GL_RG_INTEGER, LOCAL_GL_INT           );
 
     // RED
-    ret->AddUnpackOption(LOCAL_GL_RED, LOCAL_GL_UNSIGNED_BYTE, EffectiveFormat::R8      );
-    ret->AddUnpackOption(LOCAL_GL_RED, LOCAL_GL_BYTE         , EffectiveFormat::R8_SNORM);
-    ret->AddUnpackOption(LOCAL_GL_RED, LOCAL_GL_HALF_FLOAT   , EffectiveFormat::R16F    );
-    ret->AddUnpackOption(LOCAL_GL_RED, LOCAL_GL_FLOAT        , EffectiveFormat::R32F    );
-    ret->AddUnpackOption(LOCAL_GL_RED, LOCAL_GL_FLOAT        , EffectiveFormat::R16F    );
+    fnAddSizedUnpack(FOO(R8      ), LOCAL_GL_RED, LOCAL_GL_UNSIGNED_BYTE);
+    fnAddSizedUnpack(FOO(R8_SNORM), LOCAL_GL_RED, LOCAL_GL_BYTE         );
+    fnAddSizedUnpack(FOO(R16F    ), LOCAL_GL_RED, LOCAL_GL_HALF_FLOAT   );
+    fnAddSizedUnpack(FOO(R16F    ), LOCAL_GL_RED, LOCAL_GL_FLOAT        );
+    fnAddSizedUnpack(FOO(R32F    ), LOCAL_GL_RED, LOCAL_GL_FLOAT        );
 
     // RED_INTEGER
-    ret->AddUnpackOption(LOCAL_GL_RED_INTEGER, LOCAL_GL_UNSIGNED_BYTE , EffectiveFormat::R8UI );
-    ret->AddUnpackOption(LOCAL_GL_RED_INTEGER, LOCAL_GL_BYTE          , EffectiveFormat::R8I  );
-    ret->AddUnpackOption(LOCAL_GL_RED_INTEGER, LOCAL_GL_UNSIGNED_SHORT, EffectiveFormat::R16UI);
-    ret->AddUnpackOption(LOCAL_GL_RED_INTEGER, LOCAL_GL_SHORT         , EffectiveFormat::R16I );
-    ret->AddUnpackOption(LOCAL_GL_RED_INTEGER, LOCAL_GL_UNSIGNED_INT  , EffectiveFormat::R32UI);
-    ret->AddUnpackOption(LOCAL_GL_RED_INTEGER, LOCAL_GL_INT           , EffectiveFormat::R32I );
+    fnAddSizedUnpack(FOO(R8UI ), LOCAL_GL_RED_INTEGER, LOCAL_GL_UNSIGNED_BYTE );
+    fnAddSizedUnpack(FOO(R8I  ), LOCAL_GL_RED_INTEGER, LOCAL_GL_BYTE          );
+    fnAddSizedUnpack(FOO(R16UI), LOCAL_GL_RED_INTEGER, LOCAL_GL_UNSIGNED_SHORT);
+    fnAddSizedUnpack(FOO(R16I ), LOCAL_GL_RED_INTEGER, LOCAL_GL_SHORT         );
+    fnAddSizedUnpack(FOO(R32UI), LOCAL_GL_RED_INTEGER, LOCAL_GL_UNSIGNED_INT  );
+    fnAddSizedUnpack(FOO(R32I ), LOCAL_GL_RED_INTEGER, LOCAL_GL_INT           );
 
     // DEPTH_COMPONENT
-    ret->AddUnpackOption(LOCAL_GL_DEPTH_COMPONENT, LOCAL_GL_UNSIGNED_SHORT, EffectiveFormat::DEPTH_COMPONENT16 );
-    ret->AddUnpackOption(LOCAL_GL_DEPTH_COMPONENT, LOCAL_GL_UNSIGNED_INT  , EffectiveFormat::DEPTH_COMPONENT24 );
-    ret->AddUnpackOption(LOCAL_GL_DEPTH_COMPONENT, LOCAL_GL_UNSIGNED_INT  , EffectiveFormat::DEPTH_COMPONENT16 );
-    ret->AddUnpackOption(LOCAL_GL_DEPTH_COMPONENT, LOCAL_GL_FLOAT         , EffectiveFormat::DEPTH_COMPONENT32F);
+    fnAddSizedUnpack(FOO(DEPTH_COMPONENT16 ), LOCAL_GL_DEPTH_COMPONENT, LOCAL_GL_UNSIGNED_SHORT);
+    fnAddSizedUnpack(FOO(DEPTH_COMPONENT16 ), LOCAL_GL_DEPTH_COMPONENT, LOCAL_GL_UNSIGNED_INT  );
+    fnAddSizedUnpack(FOO(DEPTH_COMPONENT24 ), LOCAL_GL_DEPTH_COMPONENT, LOCAL_GL_UNSIGNED_INT  );
+    fnAddSizedUnpack(FOO(DEPTH_COMPONENT32F), LOCAL_GL_DEPTH_COMPONENT, LOCAL_GL_FLOAT         );
 
     // DEPTH_STENCIL
-    ret->AddUnpackOption(LOCAL_GL_DEPTH_STENCIL, LOCAL_GL_UNSIGNED_INT_24_8             , EffectiveFormat::DEPTH24_STENCIL8 );
-    ret->AddUnpackOption(LOCAL_GL_DEPTH_STENCIL, LOCAL_GL_FLOAT_32_UNSIGNED_INT_24_8_REV, EffectiveFormat::DEPTH32F_STENCIL8);
+    fnAddSizedUnpack(FOO(DEPTH24_STENCIL8 ), LOCAL_GL_DEPTH_STENCIL, LOCAL_GL_UNSIGNED_INT_24_8             );
+    fnAddSizedUnpack(FOO(DEPTH32F_STENCIL8), LOCAL_GL_DEPTH_STENCIL, LOCAL_GL_FLOAT_32_UNSIGNED_INT_24_8_REV);
 
-    // Unsized formats
-    ret->AddUnpackOption(LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_UNSIGNED_BYTE, EffectiveFormat::Luminance8Alpha8);
-    ret->AddUnpackOption(LOCAL_GL_LUMINANCE      , LOCAL_GL_UNSIGNED_BYTE, EffectiveFormat::Luminance8      );
-    ret->AddUnpackOption(LOCAL_GL_ALPHA          , LOCAL_GL_UNSIGNED_BYTE, EffectiveFormat::Alpha8          );
+#undef FOO
+
+    AddBasicUnsizedFormats(ptr, gl);
 
     return Move(ret);
 }
 
 //////////////////////////////////////////////////////////////////////////////////////////
 
-FormatUsageInfo*
-FormatUsageAuthority::GetUsage(EffectiveFormat format)
+void
+FormatUsageAuthority::AddRBFormat(GLenum sizedFormat, const FormatUsageInfo* usage)
+{
+    AlwaysInsert(mRBFormatMap, sizedFormat, usage);
+}
+
+void
+FormatUsageAuthority::AddSizedTexFormat(GLenum sizedFormat, const FormatUsageInfo* usage)
+{
+    AlwaysInsert(mSizedTexFormatMap, sizedFormat, usage);
+}
+
+void
+FormatUsageAuthority::AddUnsizedTexFormat(const PackingInfo& pi,
+                                          const FormatUsageInfo* usage)
+{
+    AlwaysInsert(mUnsizedTexFormatMap, pi, usage);
+}
+
+const FormatUsageInfo*
+FormatUsageAuthority::GetRBUsage(GLenum sizedFormat) const
+{
+    return FindOrNull(mRBFormatMap, sizedFormat);
+}
+
+const FormatUsageInfo*
+FormatUsageAuthority::GetSizedTexUsage(GLenum sizedFormat) const
 {
-    auto itr = mInfoMap.find(format);
+    return FindOrNull(mSizedTexFormatMap, sizedFormat);
+}
+
+const FormatUsageInfo*
+FormatUsageAuthority::GetUnsizedTexUsage(const PackingInfo& pi) const
+{
+    return FindOrNull(mUnsizedTexFormatMap, pi);
+}
+
+FormatUsageInfo*
+FormatUsageAuthority::EditUsage(EffectiveFormat format)
+{
+    auto itr = mUsageMap.find(format);
 
-    if (itr == mInfoMap.end())
+    if (itr == mUsageMap.end()) {
+        const FormatInfo* formatInfo = GetFormat(format);
+        MOZ_RELEASE_ASSERT(formatInfo);
+
+        FormatUsageInfo usage(formatInfo);
+        itr = AlwaysInsert(mUsageMap, format, usage);
+    }
+
+    return &(itr->second);
+}
+
+const FormatUsageInfo*
+FormatUsageAuthority::GetUsage(EffectiveFormat format) const
+{
+    auto itr = mUsageMap.find(format);
+    if (itr == mUsageMap.end())
         return nullptr;
 
     return &(itr->second);
 }
 
-void
-FormatUsageAuthority::AddFormat(EffectiveFormat format, GLenum rbInternalFormat,
-                                bool isRenderable, GLenum texInternalFormat,
-                                bool isFilterable)
-{
-    MOZ_ASSERT_IF(rbInternalFormat, isRenderable);
-    MOZ_ASSERT_IF(isFilterable, texInternalFormat);
-
-    const FormatInfo* formatInfo = GetFormatInfo(format);
-    MOZ_RELEASE_ASSERT(formatInfo);
-
-    FormatUsageInfo usage = { formatInfo, rbInternalFormat, isRenderable,
-                              texInternalFormat, isFilterable, std::set<UnpackTuple>() };
-    AlwaysInsert(mInfoMap, format, usage);
-}
-
-void
-FormatUsageAuthority::AddUnpackOption(GLenum unpackFormat, GLenum unpackType,
-                                      EffectiveFormat effectiveFormat)
-{
-    const UnpackTuple unpack = { unpackFormat, unpackType };
-    FormatUsageInfo* usage = GetUsage(effectiveFormat);
-    MOZ_RELEASE_ASSERT(usage);
-    if (!usage)
-        return;
-
-    MOZ_RELEASE_ASSERT(usage->asTexture);
-
-    auto res = usage->validUnpacks.insert(unpack);
-    bool didInsert = res.second;
-    MOZ_ALWAYS_TRUE(didInsert);
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 
-struct ComponentSizes
-{
-    GLubyte redSize;
-    GLubyte greenSize;
-    GLubyte blueSize;
-    GLubyte alphaSize;
-    GLubyte depthSize;
-    GLubyte stencilSize;
-};
-
-static ComponentSizes kComponentSizes[] = {
-    // GLES 3.0.4, p128-129, "Required Texture Formats"
-    // "Texture and renderbuffer color formats"
-    { 32, 32, 32, 32,  0,  0 }, // RGBA32I,
-    { 32, 32, 32, 32,  0,  0 }, // RGBA32UI,
-    { 16, 16, 16, 16,  0,  0 }, // RGBA16I,
-    { 16, 16, 16, 16,  0,  0 }, // RGBA16UI,
-    {  8,  8,  8,  8,  0,  0 }, // RGBA8,
-    {  8,  8,  8,  8,  0,  0 }, // RGBA8I,
-    {  8,  8,  8,  8,  0,  0 }, // RGBA8UI,
-    {  8,  8,  8,  8,  0,  0 }, // SRGB8_ALPHA8,
-    { 10, 10, 10,  2,  0,  0 }, // RGB10_A2,
-    { 10, 10, 10,  2,  0,  0 }, // RGB10_A2UI,
-    {  4,  4,  4,  4,  0,  0 }, // RGBA4,
-    {  5,  5,  5,  1,  0,  0 }, // RGB5_A1,
-
-    {  8,  8,  8,  0,  0,  0 }, // RGB8,
-    {  8,  8,  8,  0,  0,  0 }, // RGB565,
-
-    { 32, 32,  0,  0,  0,  0 }, // RG32I,
-    { 32, 32,  0,  0,  0,  0 }, // RG32UI,
-    { 16, 16,  0,  0,  0,  0 }, // RG16I,
-    { 16, 16,  0,  0,  0,  0 }, // RG16UI,
-    {  8,  8,  0,  0,  0,  0 }, // RG8,
-    {  8,  8,  0,  0,  0,  0 }, // RG8I,
-    {  8,  8,  0,  0,  0,  0 }, // RG8UI,
-
-    { 32,  0,  0,  0,  0,  0 }, // R32I,
-    { 32,  0,  0,  0,  0,  0 }, // R32UI,
-    { 16,  0,  0,  0,  0,  0 }, // R16I,
-    { 16,  0,  0,  0,  0,  0 }, // R16UI,
-    {  8,  0,  0,  0,  0,  0 }, // R8,
-    {  8,  0,  0,  0,  0,  0 }, // R8I,
-    {  8,  0,  0,  0,  0,  0 }, // R8UI,
-
-    // "Texture-only color formats"
-    { 32, 32, 32, 32,  0,  0 }, // RGBA32F,
-    { 16, 16, 16, 16,  0,  0 }, // RGBA16F,
-    {  8,  8,  8,  8,  0,  0 }, // RGBA8_SNORM,
-
-    { 32, 32, 32,  0,  0,  0 }, // RGB32F,
-    { 32, 32, 32,  0,  0,  0 }, // RGB32I,
-    { 32, 32, 32,  0,  0,  0 }, // RGB32UI,
-
-    { 16, 16, 16,  0,  0,  0 }, // RGB16F,
-    { 16, 16, 16,  0,  0,  0 }, // RGB16I,
-    { 16, 16, 16,  0,  0,  0 }, // RGB16UI,
-
-    {  8,  8,  8,  0,  0,  0 }, // RGB8_SNORM,
-    {  8,  8,  8,  0,  0,  0 }, // RGB8I,
-    {  8,  8,  8,  0,  0,  0 }, // RGB8UI,
-    {  8,  8,  8,  0,  0,  0 }, // SRGB8,
-
-    { 11, 11, 11,  0,  0,  0 }, // R11F_G11F_B10F,
-    {  9,  9,  9,  0,  0,  0 }, // RGB9_E5,
-
-    { 32, 32,  0,  0,  0,  0 }, // RG32F,
-    { 16, 16,  0,  0,  0,  0 }, // RG16F,
-    {  8,  8,  0,  0,  0,  0 }, // RG8_SNORM,
-
-    { 32,  0,  0,  0,  0,  0 }, // R32F,
-    { 16,  0,  0,  0,  0,  0 }, // R16F,
-    {  8,  0,  0,  0,  0,  0 }, // R8_SNORM,
-
-    // "Depth formats"
-    {  0,  0,  0,  0, 32,  0 }, // DEPTH_COMPONENT32F,
-    {  0,  0,  0,  0, 24,  0 }, // DEPTH_COMPONENT24,
-    {  0,  0,  0,  0, 16,  0 }, // DEPTH_COMPONENT16,
-
-    // "Combined depth+stencil formats"
-    {  0,  0,  0,  0, 32,  8 }, // DEPTH32F_STENCIL0,
-    {  0,  0,  0,  0, 24,  8 }, // DEPTH24_STENCIL8,
-
-    // GLES 3.0.4, p205-206, "Required Renderbuffer Formats"
-    {  0,  0,  0,  0,  0,  8 }, // STENCIL_INDEX8,
-
-    // GLES 3.0.4, p128, table 3.12.
-    {  8,  8,  8,  8,  0,  0 }, // Luminance8Alpha8,
-    {  8,  8,  8,  0,  0,  0 }, // Luminance8,
-    {  0,  0,  0,  8,  0,  0 }, // Alpha8,
-
-    // GLES 3.0.4, p147, table 3.19
-    // GLES 3.0.4, p286+, $C.1 "ETC Compressed Texture Image Formats"
-    {  8,  8,  8,  8,  0,  0 }, // COMPRESSED_R11_EAC,
-    {  8,  8,  8,  8,  0,  0 }, // COMPRESSED_SIGNED_R11_EAC,
-    {  8,  8,  8,  8,  0,  0 }, // COMPRESSED_RG11_EAC,
-    {  8,  8,  8,  8,  0,  0 }, // COMPRESSED_SIGNED_RG11_EAC,
-    {  8,  8,  8,  8,  0,  0 }, // COMPRESSED_RGB8_ETC2,
-    {  8,  8,  8,  8,  0,  0 }, // COMPRESSED_SRGB8_ETC2,
-    {  8,  8,  8,  8,  0,  0 }, // COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2,
-    {  8,  8,  8,  8,  0,  0 }, // COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2,
-    {  8,  8,  8,  8,  0,  0 }, // COMPRESSED_RGBA8_ETC2_EAC,
-    {  8,  8,  8,  8,  0,  0 }, // COMPRESSED_SRGB8_ALPHA8_ETC2_EAC,
-
-    // AMD_compressed_ATC_texture
-    {  8,  8,  8,  0,  0,  0 }, // ATC_RGB_AMD,
-    {  8,  8,  8,  8,  0,  0 }, // ATC_RGBA_EXPLICIT_ALPHA_AMD,
-    {  8,  8,  8,  8,  0,  0 }, // ATC_RGBA_INTERPOLATED_ALPHA_AMD,
-
-    // EXT_texture_compression_s3tc
-    {  8,  8,  8,  0,  0,  0 }, // COMPRESSED_RGB_S3TC_DXT1,
-    {  8,  8,  8,  8,  0,  0 }, // COMPRESSED_RGBA_S3TC_DXT1,
-    {  8,  8,  8,  8,  0,  0 }, // COMPRESSED_RGBA_S3TC_DXT3,
-    {  8,  8,  8,  8,  0,  0 }, // COMPRESSED_RGBA_S3TC_DXT5,
-
-    // IMG_texture_compression_pvrtc
-    {  8,  8,  8,  0,  0,  0 }, // COMPRESSED_RGB_PVRTC_4BPPV1,
-    {  8,  8,  8,  8,  0,  0 }, // COMPRESSED_RGBA_PVRTC_4BPPV1,
-    {  8,  8,  8,  0,  0,  0 }, // COMPRESSED_RGB_PVRTC_2BPPV1,
-    {  8,  8,  8,  8,  0,  0 }, // COMPRESSED_RGBA_PVRTC_2BPPV1,
-
-    // OES_compressed_ETC1_RGB8_texture
-    {  8,  8,  8,  0,  0,  0 }, // ETC1_RGB8,
-
-    // OES_texture_float
-    { 32, 32, 32, 32,  0,  0 }, // Luminance32FAlpha32F,
-    { 32, 32, 32,  0,  0,  0 }, // Luminance32F,
-    {  0,  0,  0, 32,  0,  0 }, // Alpha32F,
-
-    // OES_texture_half_float
-    { 16, 16, 16, 16, 0, 0 }, // Luminance16FAlpha16F,
-    { 16, 16, 16,  0, 0, 0 }, // Luminance16F,
-    {  0,  0,  0, 16, 0, 0 }, // Alpha16F,
-
-    {  0, } // MAX
-};
-
-GLint
-GetComponentSize(EffectiveFormat format, GLenum component)
-{
-    ComponentSizes compSize = kComponentSizes[(int) format];
-    switch (component) {
-    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE:
-    case LOCAL_GL_RENDERBUFFER_RED_SIZE:
-    case LOCAL_GL_RED_BITS:
-        return compSize.redSize;
-    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE:
-    case LOCAL_GL_RENDERBUFFER_GREEN_SIZE:
-    case LOCAL_GL_GREEN_BITS:
-        return compSize.greenSize;
-    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE:
-    case LOCAL_GL_RENDERBUFFER_BLUE_SIZE:
-    case LOCAL_GL_BLUE_BITS:
-        return compSize.blueSize;
-    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE:
-    case LOCAL_GL_RENDERBUFFER_ALPHA_SIZE:
-    case LOCAL_GL_ALPHA_BITS:
-        return compSize.alphaSize;
-    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE:
-    case LOCAL_GL_RENDERBUFFER_DEPTH_SIZE:
-    case LOCAL_GL_DEPTH_BITS:
-        return compSize.depthSize;
-    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE:
-    case LOCAL_GL_RENDERBUFFER_STENCIL_SIZE:
-    case LOCAL_GL_STENCIL_BITS:
-        return compSize.stencilSize;
-    }
-
-    return 0;
-}
-
-static GLenum kComponentTypes[] = {
-    // GLES 3.0.4, p128-129, "Required Texture Formats"
-    // "Texture and renderbuffer color formats"
-    LOCAL_GL_INT,                       // RGBA32I,
-    LOCAL_GL_UNSIGNED_INT,              // RGBA32UI,
-    LOCAL_GL_INT,                       // RGBA16I,
-    LOCAL_GL_UNSIGNED_INT,              // RGBA16UI,
-    LOCAL_GL_UNSIGNED_NORMALIZED,       // RGBA8,
-    LOCAL_GL_INT,                       // RGBA8I,
-    LOCAL_GL_UNSIGNED_INT,              // RGBA8UI,
-    LOCAL_GL_UNSIGNED_NORMALIZED,       // SRGB8_ALPHA8,
-    LOCAL_GL_UNSIGNED_NORMALIZED,       // RGB10_A2,
-    LOCAL_GL_UNSIGNED_INT,              // RGB10_A2UI,
-    LOCAL_GL_UNSIGNED_NORMALIZED,       // RGBA4,
-    LOCAL_GL_UNSIGNED_NORMALIZED,       // RGB5_A1,
-
-    LOCAL_GL_UNSIGNED_NORMALIZED,       // RGB8,
-    LOCAL_GL_UNSIGNED_NORMALIZED,       // RGB565,
-
-    LOCAL_GL_INT,                       // RG32I,
-    LOCAL_GL_UNSIGNED_INT,              // RG32UI,
-    LOCAL_GL_INT,                       // RG16I,
-    LOCAL_GL_UNSIGNED_INT,              // RG16UI,
-    LOCAL_GL_UNSIGNED_NORMALIZED,       // RG8,
-    LOCAL_GL_INT,                       // RG8I,
-    LOCAL_GL_UNSIGNED_INT,              // RG8UI,
-
-    LOCAL_GL_INT,                       // R32I,
-    LOCAL_GL_UNSIGNED_INT,              // R32UI,
-    LOCAL_GL_INT,                       // R16I,
-    LOCAL_GL_UNSIGNED_INT,              // R16UI,
-    LOCAL_GL_UNSIGNED_NORMALIZED,       // R8,
-    LOCAL_GL_INT,                       // R8I,
-    LOCAL_GL_UNSIGNED_INT,              // R8UI,
-
-    // "Texture-only color formats"
-    LOCAL_GL_FLOAT,                     // RGBA32F,
-    LOCAL_GL_FLOAT,                     // RGBA16F,
-    LOCAL_GL_SIGNED_NORMALIZED,         // RGBA8_SNORM,
-
-    LOCAL_GL_FLOAT,                     // RGB32F,
-    LOCAL_GL_INT,                       // RGB32I,
-    LOCAL_GL_UNSIGNED_INT,              // RGB32UI,
-
-    LOCAL_GL_FLOAT,                     // RGB16F,
-    LOCAL_GL_INT,                       // RGB16I,
-    LOCAL_GL_UNSIGNED_INT,              // RGB16UI,
-
-    LOCAL_GL_SIGNED_NORMALIZED,         // RGB8_SNORM,
-    LOCAL_GL_INT,                       // RGB8I,
-    LOCAL_GL_UNSIGNED_INT,              // RGB8UI,
-    LOCAL_GL_UNSIGNED_NORMALIZED,       // SRGB8,
-
-    LOCAL_GL_FLOAT,                     // R11F_G11F_B10F,
-    LOCAL_GL_FLOAT,                     // RGB9_E5,
-
-    LOCAL_GL_FLOAT,                     // RG32F,
-    LOCAL_GL_FLOAT,                     // RG16F,
-    LOCAL_GL_SIGNED_NORMALIZED,         // RG8_SNORM,
-
-    LOCAL_GL_FLOAT,                     // R32F,
-    LOCAL_GL_FLOAT,                     // R16F,
-    LOCAL_GL_SIGNED_NORMALIZED,         // R8_SNORM,
-
-    // "Depth formats"
-    LOCAL_GL_FLOAT,                     // DEPTH_COMPONENT32F,
-    LOCAL_GL_UNSIGNED_NORMALIZED,       // DEPTH_COMPONENT24,
-    LOCAL_GL_UNSIGNED_NORMALIZED,       // DEPTH_COMPONENT16,
-
-    // "Combined depth+stencil formats"
-    LOCAL_GL_FLOAT,                     // DEPTH32F_STENCIL8,
-    LOCAL_GL_UNSIGNED_NORMALIZED,       // DEPTH24_STENCIL8,
-
-    // GLES 3.0.4, p205-206, "Required Renderbuffer Formats"
-    LOCAL_GL_UNSIGNED_NORMALIZED,       // STENCIL_INDEX8,
-
-    // GLES 3.0.4, p128, table 3.12.
-    LOCAL_GL_UNSIGNED_NORMALIZED,       // Luminance8Alpha8,
-    LOCAL_GL_UNSIGNED_NORMALIZED,       // Luminance8,
-    LOCAL_GL_UNSIGNED_NORMALIZED,       // Alpha8,
-
-    // GLES 3.0.4, p147, table 3.19
-    // GLES 3.0.4, p286+, $C.1 "ETC Compressed Texture Image Formats"
-    LOCAL_GL_UNSIGNED_NORMALIZED,       // COMPRESSED_R11_EAC,
-    LOCAL_GL_UNSIGNED_NORMALIZED,       // COMPRESSED_SIGNED_R11_EAC,
-    LOCAL_GL_UNSIGNED_NORMALIZED,       // COMPRESSED_RG11_EAC,
-    LOCAL_GL_UNSIGNED_NORMALIZED,       // COMPRESSED_SIGNED_RG11_EAC,
-    LOCAL_GL_UNSIGNED_NORMALIZED,       // COMPRESSED_RGB8_ETC2,
-    LOCAL_GL_UNSIGNED_NORMALIZED,       // COMPRESSED_SRGB8_ETC2,
-    LOCAL_GL_UNSIGNED_NORMALIZED,       // COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2,
-    LOCAL_GL_UNSIGNED_NORMALIZED,       // COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2,
-    LOCAL_GL_UNSIGNED_NORMALIZED,       // COMPRESSED_RGBA8_ETC2_EAC,
-    LOCAL_GL_UNSIGNED_NORMALIZED,       // COMPRESSED_SRGB8_ALPHA8_ETC2_EAC,
-
-    // AMD_compressed_ATC_texture
-    LOCAL_GL_UNSIGNED_NORMALIZED,       // ATC_RGB_AMD,
-    LOCAL_GL_UNSIGNED_NORMALIZED,       // ATC_RGBA_EXPLICIT_ALPHA_AMD,
-    LOCAL_GL_UNSIGNED_NORMALIZED,       // ATC_RGBA_INTERPOLATED_ALPHA_AMD,
-
-    // EXT_texture_compression_s3tc
-    LOCAL_GL_UNSIGNED_NORMALIZED,       // COMPRESSED_RGB_S3TC_DXT1,
-    LOCAL_GL_UNSIGNED_NORMALIZED,       // COMPRESSED_RGBA_S3TC_DXT1,
-    LOCAL_GL_UNSIGNED_NORMALIZED,       // COMPRESSED_RGBA_S3TC_DXT3,
-    LOCAL_GL_UNSIGNED_NORMALIZED,       // COMPRESSED_RGBA_S3TC_DXT5,
-
-    // IMG_texture_compression_pvrtc
-    LOCAL_GL_UNSIGNED_NORMALIZED,       // COMPRESSED_RGB_PVRTC_4BPPV1,
-    LOCAL_GL_UNSIGNED_NORMALIZED,       // COMPRESSED_RGBA_PVRTC_4BPPV1,
-    LOCAL_GL_UNSIGNED_NORMALIZED,       // COMPRESSED_RGB_PVRTC_2BPPV1,
-    LOCAL_GL_UNSIGNED_NORMALIZED,       // COMPRESSED_RGBA_PVRTC_2BPPV1,
-
-    // OES_compressed_ETC1_RGB8_texture
-    LOCAL_GL_UNSIGNED_NORMALIZED,       // ETC1_RGB8,
-
-    // OES_texture_float
-    LOCAL_GL_FLOAT,                     // Luminance32FAlpha32F,
-    LOCAL_GL_FLOAT,                     // Luminance32F,
-    LOCAL_GL_FLOAT,                     // Alpha32F,
-
-    // OES_texture_half_float
-    LOCAL_GL_FLOAT,                     // Luminance16FAlpha16F,
-    LOCAL_GL_FLOAT,                     // Luminance16F,
-    LOCAL_GL_FLOAT,                     // Alpha16F,
-
-    LOCAL_GL_NONE // MAX
-};
-
-GLenum
-GetComponentType(EffectiveFormat format)
-{
-    return kComponentTypes[(int) format];
-}
-
-GLenum
-GetColorEncoding(EffectiveFormat format)
-{
-    const bool isSRGB = (GetFormatInfo(format)->colorComponentType ==
-                         ComponentType::NormUIntSRGB);
-    return (isSRGB) ? LOCAL_GL_SRGB : LOCAL_GL_LINEAR;
-}
-
 } // namespace webgl
 } // namespace mozilla
--- a/dom/canvas/WebGLFormats.h
+++ b/dom/canvas/WebGLFormats.h
@@ -88,20 +88,17 @@ enum class EffectiveFormat : EffectiveFo
 
     // "Combined depth+stencil formats"
     DEPTH32F_STENCIL8,
     DEPTH24_STENCIL8,
 
     // GLES 3.0.4, p205-206, "Required Renderbuffer Formats"
     STENCIL_INDEX8,
 
-    // GLES 3.0.4, p128, table 3.12.
-    Luminance8Alpha8,
-    Luminance8,
-    Alpha8,
+    ////////////////////////////////////
 
     // GLES 3.0.4, p147, table 3.19
     // GLES 3.0.4, p286+, $C.1 "ETC Compressed Texture Image Formats"
     COMPRESSED_R11_EAC,
     COMPRESSED_SIGNED_R11_EAC,
     COMPRESSED_RG11_EAC,
     COMPRESSED_SIGNED_RG11_EAC,
     COMPRESSED_RGB8_ETC2,
@@ -112,29 +109,36 @@ enum class EffectiveFormat : EffectiveFo
     COMPRESSED_SRGB8_ALPHA8_ETC2_EAC,
 
     // AMD_compressed_ATC_texture
     ATC_RGB_AMD,
     ATC_RGBA_EXPLICIT_ALPHA_AMD,
     ATC_RGBA_INTERPOLATED_ALPHA_AMD,
 
     // EXT_texture_compression_s3tc
-    COMPRESSED_RGB_S3TC_DXT1,
-    COMPRESSED_RGBA_S3TC_DXT1,
-    COMPRESSED_RGBA_S3TC_DXT3,
-    COMPRESSED_RGBA_S3TC_DXT5,
+    COMPRESSED_RGB_S3TC_DXT1_EXT,
+    COMPRESSED_RGBA_S3TC_DXT1_EXT,
+    COMPRESSED_RGBA_S3TC_DXT3_EXT,
+    COMPRESSED_RGBA_S3TC_DXT5_EXT,
 
     // IMG_texture_compression_pvrtc
     COMPRESSED_RGB_PVRTC_4BPPV1,
     COMPRESSED_RGBA_PVRTC_4BPPV1,
     COMPRESSED_RGB_PVRTC_2BPPV1,
     COMPRESSED_RGBA_PVRTC_2BPPV1,
 
     // OES_compressed_ETC1_RGB8_texture
-    ETC1_RGB8,
+    ETC1_RGB8_OES,
+
+    ////////////////////////////////////
+
+    // GLES 3.0.4, p128, table 3.12.
+    Luminance8Alpha8,
+    Luminance8,
+    Alpha8,
 
     // OES_texture_float
     Luminance32FAlpha32F,
     Luminance32F,
     Alpha32F,
 
     // OES_texture_half_float
     Luminance16FAlpha16F,
@@ -152,127 +156,147 @@ enum class UnsizedFormat : uint8_t {
     LA,
     L,
     A,
     D,
     S,
     DS,
 };
 
-// GLES 3.0.4 p114 Table 3.4
+// GLES 3.0.4 p114 Table 3.4, p240
 enum class ComponentType : uint8_t {
-    None,         // DEPTH_COMPONENT32F
+    None,         // DEPTH24_STENCIL8
     Int,          // RGBA32I
-    UInt,         // RGBA32UI
+    UInt,         // RGBA32UI, STENCIL_INDEX8
     NormInt,      // RGBA8_SNORM
-    NormUInt,     // RGBA8
-    NormUIntSRGB, // SRGB8_ALPHA8
+    NormUInt,     // RGBA8, DEPTH_COMPONENT16
     Float,        // RGBA32F
-    SharedExp,    // RGB9_E5
 };
 
-enum class SubImageUpdateBehavior : uint8_t {
-    Forbidden,
-    FullOnly,
-    BlockAligned,
+enum class CompressionFamily : uint8_t {
+    ETC1,
+    ES3, // ETC2 or EAC
+    ATC,
+    S3TC,
+    PVRTC,
 };
 
 ////////////////////////////////////////////////////////////////////////////////
 
-struct CompressedFormatInfo {
+struct CompressedFormatInfo
+{
     const EffectiveFormat effectiveFormat;
     const uint8_t bytesPerBlock;
     const uint8_t blockWidth;
     const uint8_t blockHeight;
-    const bool requirePOT;
-    const SubImageUpdateBehavior subImageUpdateBehavior;
+    const CompressionFamily family;
 };
 
-struct FormatInfo {
+struct FormatInfo
+{
     const EffectiveFormat effectiveFormat;
     const char* const name;
+    const GLenum sizedFormat;
     const UnsizedFormat unsizedFormat;
-    const ComponentType colorComponentType;
-    const uint8_t bytesPerPixel; // 0 iff `!!compression`.
-    const bool isColorFormat; // This *does* include alpha-only formats.
-    const bool hasAlpha;
+    const ComponentType componentType;
+    const uint8_t estimatedBytesPerPixel; // 0 iff `!!compression`. Only use this for
+    const bool isColorFormat;             // memory usage estimation. Use
+    const bool isSRGB;                    // BytesPerPixel(packingFormat, packingType) for
+    const bool hasAlpha;                  // calculating pack/unpack byte count.
     const bool hasDepth;
     const bool hasStencil;
 
     const CompressedFormatInfo* const compression;
 };
 
-//////////////////////////////////////////////////////////////////////////////////////////
-
-const FormatInfo* GetFormatInfo(EffectiveFormat format);
-const FormatInfo* GetInfoByUnpackTuple(GLenum unpackFormat, GLenum unpackType);
-const FormatInfo* GetInfoBySizedFormat(GLenum sizedFormat);
-
-////////////////////////////////////////
+struct PackingInfo
+{
+    GLenum format;
+    GLenum type;
 
-struct UnpackTuple {
-    const GLenum format;
-    const GLenum type;
-
-    bool operator <(const UnpackTuple& x) const
+    bool operator <(const PackingInfo& x) const
     {
-        if (format == x.format) {
-            return type < x.type;
-        }
+        if (format != x.format)
+            return format < x.format;
 
-        return format < x.format;
+        return type < x.type;
     }
 };
 
-struct FormatUsageInfo {
-    const FormatInfo* const formatInfo;
+struct DriverUnpackInfo
+{
+    GLenum internalFormat;
+    GLenum unpackFormat;
+    GLenum unpackType;
+};
 
-    GLenum rbInternalFormat;
-    bool isRenderable;
+//////////////////////////////////////////////////////////////////////////////////////////
+
+const FormatInfo* GetFormat(EffectiveFormat format);
+uint8_t BytesPerPixel(const PackingInfo& packing);
+/*
+GLint ComponentSize(const FormatInfo* format, GLenum component);
+GLenum ComponentType(const FormatInfo* format);
+*/
+////////////////////////////////////////
 
-    GLenum texInternalFormat;
-    GLenum nativeUnpackFormat;
-    GLenum nativeUnpackType;
+struct FormatUsageInfo
+{
+    const FormatInfo* const format;
+    bool isRenderable;
     bool isFilterable;
+    std::map<PackingInfo, DriverUnpackInfo> validUnpacks;
+    const DriverUnpackInfo* idealUnpack;
+    //const GLint* textureSwizzleRGBA;
 
-    std::set<UnpackTuple> validUnpacks;
+    FormatUsageInfo(const FormatInfo* _format)
+        : format(_format)
+        , isRenderable(false)
+        , isFilterable(false)
+        , idealUnpack(nullptr)
+    { }
 
-    bool CanUnpackWith(GLenum unpackFormat, GLenum unpackType) const;
+    void AddUnpack(const PackingInfo& key, const DriverUnpackInfo& value);
+    bool IsUnpackValid(const PackingInfo& key,
+                       const DriverUnpackInfo** const out_value) const;
 };
 
 class FormatUsageAuthority
 {
-    std::map<EffectiveFormat, FormatUsageInfo> mInfoMap;
+    std::map<EffectiveFormat, FormatUsageInfo> mUsageMap;
+
+    std::map<GLenum, const FormatUsageInfo*> mRBFormatMap;
+    std::map<GLenum, const FormatUsageInfo*> mSizedTexFormatMap;
+    std::map<PackingInfo, const FormatUsageInfo*> mUnsizedTexFormatMap;
 
 public:
     static UniquePtr<FormatUsageAuthority> CreateForWebGL1(gl::GLContext* gl);
     static UniquePtr<FormatUsageAuthority> CreateForWebGL2(gl::GLContext* gl);
 
 private:
     FormatUsageAuthority() { }
 
 public:
-    void AddFormat(EffectiveFormat format, GLenum rbInternalFormat, bool isRenderable,
-                   GLenum texInternalFormat, bool isFilterable);
+    void AddRBFormat(GLenum sizedFormat, const FormatUsageInfo* usage);
+    void AddSizedTexFormat(GLenum sizedFormat, const FormatUsageInfo* usage);
+    void AddUnsizedTexFormat(const PackingInfo& pi, const FormatUsageInfo* usage);
 
-    void AddUnpackOption(GLenum unpackFormat, GLenum unpackType,
-                         EffectiveFormat effectiveFormat);
+    const FormatUsageInfo* GetRBUsage(GLenum sizedFormat) const;
+    const FormatUsageInfo* GetSizedTexUsage(GLenum sizedFormat) const;
+    const FormatUsageInfo* GetUnsizedTexUsage(const PackingInfo& pi) const;
 
-    FormatUsageInfo* GetUsage(EffectiveFormat format);
-    FormatUsageInfo* GetUsage(const FormatInfo* format)
+    FormatUsageInfo* EditUsage(EffectiveFormat format);
+
+    const FormatUsageInfo* GetUsage(EffectiveFormat format) const;
+
+    const FormatUsageInfo* GetUsage(const FormatInfo* format) const
     {
         if (!format)
             return nullptr;
 
         return GetUsage(format->effectiveFormat);
     }
 };
 
-////////////////////////////////////////////////////////////////////////////////
-
-GLint GetComponentSize(EffectiveFormat format, GLenum component);
-GLenum GetComponentType(EffectiveFormat format);
-GLenum GetColorEncoding(EffectiveFormat format);
-
 } // namespace webgl
 } // namespace mozilla
 
 #endif // WEBGL_FORMATS_H_
--- a/dom/canvas/WebGLFramebuffer.cpp
+++ b/dom/canvas/WebGLFramebuffer.cpp
@@ -45,55 +45,56 @@ WebGLFBAttachPoint::IsDeleteRequested() 
 bool
 WebGLFBAttachPoint::IsDefined() const
 {
     return (Renderbuffer() && Renderbuffer()->IsDefined()) ||
            (Texture() && Texture()->ImageInfoAt(mTexImageTarget,
                                                 mTexImageLevel).IsDefined());
 }
 
-TexInternalFormat
+const webgl::FormatUsageInfo*
 WebGLFBAttachPoint::Format() const
 {
     MOZ_ASSERT(IsDefined());
 
     if (Texture())
         return Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel).mFormat;
 
     if (Renderbuffer())
-        return Renderbuffer()->InternalFormat();
+        return Renderbuffer()->Format();
 
-    return LOCAL_GL_NONE;
+    return nullptr;
 }
 
 bool
 WebGLFBAttachPoint::HasAlpha() const
 {
-    return FormatHasAlpha(Format());
+    return Format()->format->hasAlpha;
 }
 
-GLenum
+const webgl::FormatUsageInfo*
 WebGLFramebuffer::GetFormatForAttachment(const WebGLFBAttachPoint& attachment) const
 {
     MOZ_ASSERT(attachment.IsDefined());
     MOZ_ASSERT(attachment.Texture() || attachment.Renderbuffer());
 
-    return attachment.Format().get();
+    return attachment.Format();
 }
 
 bool
 WebGLFBAttachPoint::IsReadableFloat() const
 {
-    TexInternalFormat internalformat = Format();
-    MOZ_ASSERT(internalformat != LOCAL_GL_NONE);
+    auto formatUsage = Format();
+    MOZ_ASSERT(formatUsage);
 
-    TexType type = TypeFromInternalFormat(internalformat);
-    return type == LOCAL_GL_FLOAT ||
-           type == LOCAL_GL_HALF_FLOAT_OES ||
-           type == LOCAL_GL_HALF_FLOAT;
+    auto format = formatUsage->format;
+    if (!format->isColorFormat)
+        return false;
+
+    return format->componentType == webgl::ComponentType::Float;
 }
 
 void
 WebGLFBAttachPoint::Clear()
 {
     if (mRenderbufferPtr) {
         MOZ_ASSERT(!mTexturePtr);
         mRenderbufferPtr->UnmarkAttachment(*this);
@@ -160,17 +161,17 @@ WebGLFBAttachPoint::HasUninitializedImag
 
 void
 WebGLFBAttachPoint::SetImageDataStatus(WebGLImageDataStatus newStatus)
 {
     if (!HasImage())
         return;
 
     if (mRenderbufferPtr) {
-        mRenderbufferPtr->SetImageDataStatus(newStatus);
+        mRenderbufferPtr->mImageDataStatus = newStatus;
         return;
     }
 
     MOZ_ASSERT(mTexturePtr);
 
     auto& imageInfo = mTexturePtr->ImageInfoAt(mTexImageTarget, mTexImageLevel);
     MOZ_ASSERT(imageInfo.IsDefined());
 
@@ -179,17 +180,17 @@ WebGLFBAttachPoint::SetImageDataStatus(W
 }
 
 bool
 WebGLFBAttachPoint::HasImage() const
 {
     if (Texture() && Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel).IsDefined())
         return true;
 
-    if (Renderbuffer())
+    if (Renderbuffer() && Renderbuffer()->IsDefined())
         return true;
 
     return false;
 }
 
 void
 WebGLFBAttachPoint::Size(uint32_t* const out_width, uint32_t* const out_height) const
 {
@@ -210,165 +211,56 @@ WebGLFBAttachPoint::Size(uint32_t* const
 }
 
 void
 WebGLFBAttachPoint::OnBackingStoreRespecified() const
 {
     mFB->InvalidateFramebufferStatus();
 }
 
-/*
-const WebGLRectangleObject&
-WebGLFBAttachPoint::RectangleObject() const
-{
-    MOZ_ASSERT(HasImage(),
-               "Make sure it has an image before requesting the rectangle.");
-
-    if (Texture()) {
-        MOZ_ASSERT(Texture()->HasImageInfoAt(mTexImageTarget, mTexImageLevel));
-        return Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel);
-    }
-
-    if (Renderbuffer())
-        return *Renderbuffer();
-
-    MOZ_CRASH("Should not get here.");
-}
-*/
-// The following IsValidFBOTextureXXX functions check the internal format that
-// is used by GL or GL ES texture formats.  This corresponds to the state that
-// is stored in WebGLTexture::ImageInfo::InternalFormat()
-
-static inline bool
-IsValidFBOTextureDepthFormat(GLenum internalformat)
-{
-    return IsGLDepthFormat(internalformat);
-}
-
-static inline bool
-IsValidFBOTextureDepthStencilFormat(GLenum internalformat)
-{
-    return IsGLDepthStencilFormat(internalformat);
-}
-
-// The following IsValidFBORenderbufferXXX functions check the internal format
-// that is stored by WebGLRenderbuffer::InternalFormat(). Valid values can be
-// found in WebGLContext::RenderbufferStorage.
-
-static inline bool
-IsValidFBORenderbufferDepthFormat(GLenum internalFormat)
-{
-    return internalFormat == LOCAL_GL_DEPTH_COMPONENT16;
-}
-
-static inline bool
-IsValidFBORenderbufferDepthStencilFormat(GLenum internalFormat)
-{
-    return internalFormat == LOCAL_GL_DEPTH_STENCIL;
-}
-
-static inline bool
-IsValidFBORenderbufferStencilFormat(GLenum internalFormat)
-{
-    return internalFormat == LOCAL_GL_STENCIL_INDEX8;
-}
-
-bool
-WebGLContext::IsFormatValidForFB(TexInternalFormat format) const
-{
-    switch (format.get()) {
-    case LOCAL_GL_RGB8:
-    case LOCAL_GL_RGBA8:
-    case LOCAL_GL_RGB565:
-    case LOCAL_GL_RGB5_A1:
-    case LOCAL_GL_RGBA4:
-        return true;
-
-    case LOCAL_GL_SRGB8:
-    case LOCAL_GL_SRGB8_ALPHA8_EXT:
-        return IsExtensionEnabled(WebGLExtensionID::EXT_sRGB);
-
-    case LOCAL_GL_RGB32F:
-    case LOCAL_GL_RGBA32F:
-        return IsExtensionEnabled(WebGLExtensionID::WEBGL_color_buffer_float);
-
-    case LOCAL_GL_RGB16F:
-    case LOCAL_GL_RGBA16F:
-        return IsExtensionEnabled(WebGLExtensionID::EXT_color_buffer_half_float);
-    }
-
-    return false;
-}
-
 bool
 WebGLFBAttachPoint::IsComplete() const
 {
     if (!HasImage())
         return false;
 
     uint32_t width;
     uint32_t height;
     Size(&width, &height);
     if (!width || !height)
         return false;
 
-    if (Texture()) {
-        const auto& imageInfo = Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel);
-        MOZ_ASSERT(imageInfo.IsDefined());
-
-        const auto format = imageInfo.mFormat;
-
-        if (mAttachmentPoint == LOCAL_GL_DEPTH_ATTACHMENT)
-            return IsGLDepthFormat(format);
-
-        if (mAttachmentPoint == LOCAL_GL_STENCIL_ATTACHMENT) {
-            // Textures can't have the correct format for stencil buffers.
-            return false;
-        }
-
-        if (mAttachmentPoint == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT)
-            return IsGLDepthStencilFormat(format);
+    auto formatUsage = Format();
+    if (!formatUsage->isRenderable)
+        return false;
 
-        if (mAttachmentPoint >= LOCAL_GL_COLOR_ATTACHMENT0 &&
-            mAttachmentPoint <= FBAttachment(LOCAL_GL_COLOR_ATTACHMENT0 - 1 +
-                                             WebGLContext::kMaxColorAttachments))
-        {
-            WebGLContext* webgl = Texture()->Context();
-            return webgl->IsFormatValidForFB(format);
-        }
-        MOZ_ASSERT(false, "Invalid WebGL attachment point?");
-        return false;
-    }
-
-    MOZ_ASSERT(Renderbuffer());
-    GLenum internalFormat = Renderbuffer()->InternalFormat();
-
-    if (mAttachmentPoint == LOCAL_GL_DEPTH_ATTACHMENT)
-        return IsValidFBORenderbufferDepthFormat(internalFormat);
-
-    if (mAttachmentPoint == LOCAL_GL_STENCIL_ATTACHMENT)
-        return IsValidFBORenderbufferStencilFormat(internalFormat);
-
-    if (mAttachmentPoint == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT)
-        return IsValidFBORenderbufferDepthStencilFormat(internalFormat);
+    auto format = formatUsage->format;
 
     if (mAttachmentPoint >= LOCAL_GL_COLOR_ATTACHMENT0 &&
         mAttachmentPoint <= FBAttachment(LOCAL_GL_COLOR_ATTACHMENT0 - 1 +
                                          WebGLContext::kMaxColorAttachments))
     {
-        WebGLContext* webgl = Renderbuffer()->Context();
-        return webgl->IsFormatValidForFB(internalFormat);
+        return format->isColorFormat;
     }
-    MOZ_ASSERT(false, "Invalid WebGL attachment point?");
-    return false;
+
+    if (mAttachmentPoint == LOCAL_GL_DEPTH_ATTACHMENT)
+        return format->hasDepth && !format->hasStencil;
+
+    if (mAttachmentPoint == LOCAL_GL_STENCIL_ATTACHMENT)
+        return !format->hasDepth && format->hasStencil;
+
+    if (mAttachmentPoint == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT)
+        return format->hasDepth && format->hasStencil;
+
+    MOZ_CRASH("Invalid WebGL attachment point?");
 }
 
 void
 WebGLFBAttachPoint::FinalizeAttachment(gl::GLContext* gl,
-                                                 FBAttachment attachmentLoc) const
+                                       FBAttachment attachmentLoc) const
 {
     if (!HasImage()) {
         switch (attachmentLoc.get()) {
         case LOCAL_GL_DEPTH_ATTACHMENT:
         case LOCAL_GL_STENCIL_ATTACHMENT:
         case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT:
             break;
 
@@ -378,17 +270,17 @@ WebGLFBAttachPoint::FinalizeAttachment(g
             break;
         }
 
         return;
     }
     MOZ_ASSERT(HasImage());
 
     if (Texture()) {
-        MOZ_ASSERT(gl == Texture()->Context()->GL());
+        MOZ_ASSERT(gl == Texture()->mContext->GL());
 
         const GLenum imageTarget = ImageTarget().get();
         const GLint mipLevel = MipLevel();
         const GLint layer = Layer();
         const GLuint glName = Texture()->mGLName;
 
         switch (imageTarget) {
         case LOCAL_GL_TEXTURE_2D:
@@ -431,40 +323,40 @@ WebGLFBAttachPoint::FinalizeAttachment(g
         Renderbuffer()->FramebufferRenderbuffer(attachmentLoc);
         return;
     }
 
     MOZ_CRASH();
 }
 
 JS::Value
-WebGLFBAttachPoint::GetParameter(WebGLContext* context, GLenum pname)
+WebGLFBAttachPoint::GetParameter(WebGLContext* context, GLenum target, GLenum attachment,
+                                 GLenum pname)
 {
-    // TODO: WebGLTexture and WebGLRenderbuffer should store FormatInfo instead of doing
-    // this dance every time.
-    const GLenum internalFormat = EffectiveInternalFormat().get();
-    const webgl::FormatInfo* info = webgl::GetInfoBySizedFormat(internalFormat);
-    MOZ_ASSERT(info);
-
     WebGLTexture* tex = Texture();
 
+    bool isPNameValid = false;
     switch (pname) {
     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(info->effectiveFormat, pname));
-
     case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE:
-        return JS::Int32Value(webgl::GetComponentType(info->effectiveFormat));
+        isPNameValid = true;
+        break;
 
     case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING:
-        return JS::Int32Value(webgl::GetColorEncoding(info->effectiveFormat));
+        if (tex->mContext->IsWebGL2() ||
+            tex->mContext->IsExtensionEnabled(WebGLExtensionID::EXT_sRGB))
+        {
+            isPNameValid = true;
+        }
+        break;
 
     case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL:
         if (tex) {
             return JS::Int32Value(MipLevel());
         }
         break;
 
     case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE:
@@ -485,19 +377,28 @@ WebGLFBAttachPoint::GetParameter(WebGLCo
             {
                 layer = Layer();
             }
             return JS::Int32Value(layer);
         }
         break;
     }
 
-    context->ErrorInvalidEnum("getFramebufferParameter: Invalid combination of "
-                              "attachment and pname.");
-    return JS::NullValue();
+    if (!isPNameValid) {
+        context->ErrorInvalidEnum("getFramebufferParameter: Invalid combination of "
+                                  "attachment and pname.");
+        return JS::NullValue();
+    }
+
+    gl::GLContext* gl = tex->mContext->GL();
+    gl->MakeCurrent();
+
+    GLint ret = 0;
+    gl->fGetFramebufferAttachmentParameteriv(target, attachment, pname, &ret);
+    return JS::Int32Value(ret);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // WebGLFramebuffer
 
 WebGLFramebuffer::WebGLFramebuffer(WebGLContext* webgl, GLuint fbo)
     : WebGLContextBoundObject(webgl)
     , mGLName(fbo)
@@ -741,19 +642,19 @@ WebGLFramebuffer::AllImageRectsMatch() c
 {
     MOZ_ASSERT(HasDefinedAttachments());
     MOZ_ASSERT(!HasIncompleteAttachments());
 
     uint32_t width = 0;
     uint32_t height = 0;
     bool imageRectsMatch = true;
 
-    imageRectsMatch &= MatchOrReplaceSize(mColorAttachment0, &width, &height);
-    imageRectsMatch &= MatchOrReplaceSize(mDepthAttachment, &width, &height);
-    imageRectsMatch &= MatchOrReplaceSize(mStencilAttachment, &width, &height);
+    imageRectsMatch &= MatchOrReplaceSize(mColorAttachment0,       &width, &height);
+    imageRectsMatch &= MatchOrReplaceSize(mDepthAttachment,        &width, &height);
+    imageRectsMatch &= MatchOrReplaceSize(mStencilAttachment,      &width, &height);
     imageRectsMatch &= MatchOrReplaceSize(mDepthStencilAttachment, &width, &height);
 
     const size_t moreColorAttachmentCount = mMoreColorAttachments.Length();
     for (size_t i = 0; i < moreColorAttachmentCount; i++) {
         imageRectsMatch &= MatchOrReplaceSize(mMoreColorAttachments[i], &width, &height);
     }
 
     return imageRectsMatch;
@@ -984,41 +885,37 @@ WebGLFramebuffer::FinalizeAttachments() 
         GLenum attachPoint = LOCAL_GL_COLOR_ATTACHMENT0 + 1 + i;
         mMoreColorAttachments[i].FinalizeAttachment(gl, attachPoint);
     }
 
     FinalizeDrawAndReadBuffers(gl, mColorAttachment0.IsDefined());
 }
 
 bool
-WebGLFramebuffer::ValidateForRead(const char* info, TexInternalFormat* const out_format,
+WebGLFramebuffer::ValidateForRead(const char* funcName,
+                                  const webgl::FormatUsageInfo** const out_format,
                                   uint32_t* const out_width, uint32_t* const out_height)
 {
+    if (!CheckAndInitializeAttachments()) {
+        mContext->ErrorInvalidFramebufferOperation("%s: Incomplete framebuffer.",
+                                                   funcName);
+        return false;
+    }
+
     if (mReadBufferMode == LOCAL_GL_NONE) {
         mContext->ErrorInvalidOperation("%s: Read buffer mode must not be"
-                                        " NONE.", info);
+                                        " NONE.", funcName);
         return false;
     }
 
     const auto& attachPoint = GetAttachPoint(mReadBufferMode);
 
-    if (!CheckAndInitializeAttachments()) {
-        mContext->ErrorInvalidFramebufferOperation("readPixels: incomplete framebuffer");
-        return false;
-    }
-
-    GLenum readPlaneBits = LOCAL_GL_COLOR_BUFFER_BIT;
-    if (!HasCompletePlanes(readPlaneBits)) {
-        mContext->ErrorInvalidOperation("readPixels: Read source attachment doesn't have the"
-                                        " correct color/depth/stencil type.");
-        return false;
-    }
-
     if (!attachPoint.IsDefined()) {
-        mContext->ErrorInvalidOperation("readPixels: ");
+        mContext->ErrorInvalidOperation("%s: THe attachment specified for reading is"
+                                        " null.", funcName);
         return false;
     }
 
     *out_format = attachPoint.Format();
     attachPoint.Size(out_width, out_height);
     return true;
 }
 
@@ -1032,20 +929,18 @@ AttachmentsDontMatch(const WebGLFBAttach
     if (a.Renderbuffer()) {
         return (a.Renderbuffer() != b.Renderbuffer());
     }
 
     return false;
 }
 
 JS::Value
-WebGLFramebuffer::GetAttachmentParameter(JSContext* cx,
-                                         GLenum attachment,
-                                         GLenum pname,
-                                         ErrorResult& rv)
+WebGLFramebuffer::GetAttachmentParameter(JSContext* cx, GLenum target, GLenum attachment,
+                                         GLenum pname, ErrorResult* const out_error)
 {
     // "If a framebuffer object is bound to target, then attachment must be one of the
     // attachment points of the framebuffer listed in table 4.6."
     switch (attachment) {
     case LOCAL_GL_DEPTH_ATTACHMENT:
     case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT:
         break;
 
@@ -1100,35 +995,35 @@ WebGLFramebuffer::GetAttachmentParameter
 
     case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME:
         if (objectType == LOCAL_GL_NONE) {
             return JS::NullValue();
         }
 
         if (objectType == LOCAL_GL_RENDERBUFFER) {
             const WebGLRenderbuffer* rb = fba.Renderbuffer();
-            return mContext->WebGLObjectAsJSValue(cx, rb, rv);
+            return mContext->WebGLObjectAsJSValue(cx, rb, *out_error);
         }
 
         /* If the value of FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is TEXTURE, then */
         if (objectType == LOCAL_GL_TEXTURE) {
             const WebGLTexture* tex = fba.Texture();
-            return mContext->WebGLObjectAsJSValue(cx, tex, rv);
+            return mContext->WebGLObjectAsJSValue(cx, tex, *out_error);
         }
         break;
     }
 
     if (objectType == LOCAL_GL_NONE) {
         mContext->ErrorInvalidOperation("getFramebufferAttachmentParameter: No "
                                         "attachment at %s",
                                         mContext->EnumName(attachment));
         return JS::NullValue();
     }
 
-    return fba.GetParameter(mContext, pname);
+    return fba.GetParameter(mContext, target, attachment, pname);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // Goop.
 
 JSObject*
 WebGLFramebuffer::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto)
 {
--- a/dom/canvas/WebGLFramebuffer.h
+++ b/dom/canvas/WebGLFramebuffer.h
@@ -41,17 +41,17 @@ public:
     WebGLFBAttachPoint(WebGLFramebuffer* fb, FBAttachment attachmentPoint);
     ~WebGLFBAttachPoint();
 
     void Unlink();
 
     bool IsDefined() const;
     bool IsDeleteRequested() const;
 
-    TexInternalFormat Format() const;
+    const webgl::FormatUsageInfo* Format() const;
 
     bool HasAlpha() const;
     bool IsReadableFloat() const;
 
     void Clear();
 
     void SetTexImage(WebGLTexture* tex, TexImageTarget target, GLint level);
     void SetTexImageLayer(WebGLTexture* tex, TexImageTarget target, GLint level,
@@ -87,17 +87,18 @@ public:
     //const WebGLRectangleObject& RectangleObject() const;
 
     bool HasImage() const;
     bool IsComplete() const;
 
     void FinalizeAttachment(gl::GLContext* gl,
                             FBAttachment attachmentLoc) const;
 
-    JS::Value GetParameter(WebGLContext* context, GLenum pname);
+    JS::Value GetParameter(WebGLContext* context, GLenum target, GLenum attachment,
+                           GLenum pname);
 
     void OnBackingStoreRespecified() const;
 };
 
 class WebGLFramebuffer final
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLFramebuffer>
     , public LinkedListElement<WebGLFramebuffer>
@@ -156,17 +157,17 @@ public:
                                  GLint layer);
 
     bool HasDefinedAttachments() const;
     bool HasIncompleteAttachments() const;
     bool AllImageRectsMatch() const;
     FBStatus PrecheckFramebufferStatus() const;
     FBStatus CheckFramebufferStatus() const;
 
-    GLenum
+    const webgl::FormatUsageInfo*
     GetFormatForAttachment(const WebGLFBAttachPoint& attachment) const;
 
     bool HasDepthStencilConflict() const {
         return int(mDepthAttachment.IsDefined()) +
                int(mStencilAttachment.IsDefined()) +
                int(mDepthStencilAttachment.IsDefined()) >= 2;
     }
 
@@ -193,17 +194,17 @@ public:
 
     WebGLFBAttachPoint& GetAttachPoint(FBAttachment attachPointEnum);
 
     void DetachTexture(const WebGLTexture* tex);
 
     void DetachRenderbuffer(const WebGLRenderbuffer* rb);
 
     WebGLContext* GetParentObject() const {
-        return Context();
+        return mContext;
     }
 
     void FinalizeAttachments() const;
 
     virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
 
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLFramebuffer)
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLFramebuffer)
@@ -217,18 +218,19 @@ public:
                                     const char* funcName) const;
 
     void EnsureColorAttachPoints(size_t colorAttachmentId);
 
     void InvalidateFramebufferStatus() const {
         mStatus = 0;
     }
 
-    bool ValidateForRead(const char* info, TexInternalFormat* const out_format,
+    bool ValidateForRead(const char* info,
+                         const webgl::FormatUsageInfo** const out_format,
                          uint32_t* const out_width, uint32_t* const out_height);
 
-    JS::Value GetAttachmentParameter(JSContext* cx, GLenum attachment, GLenum pname,
-                                     ErrorResult& rv);
+    JS::Value GetAttachmentParameter(JSContext* cx, GLenum target, GLenum attachment,
+                                     GLenum pname, ErrorResult* const out_error);
 };
 
 } // namespace mozilla
 
 #endif // WEBGL_FRAMEBUFFER_H_
--- a/dom/canvas/WebGLObjectModel.h
+++ b/dom/canvas/WebGLObjectModel.h
@@ -264,20 +264,18 @@ protected:
 // as well as comparison with the current context.
 class WebGLContextBoundObject
 {
 public:
     explicit WebGLContextBoundObject(WebGLContext* webgl);
 
     bool IsCompatibleWithContext(WebGLContext* other);
 
-    WebGLContext* Context() const { return mContext; }
-
+    WebGLContext* const mContext;
 protected:
-    WebGLContext* const mContext;
     const uint32_t mContextGeneration;
 };
 
 // this class is a mixin for GL objects that have dimensions
 // that we need to track.
 class WebGLRectangleObject
 {
 public:
--- a/dom/canvas/WebGLProgram.cpp
+++ b/dom/canvas/WebGLProgram.cpp
@@ -162,17 +162,17 @@ QueryProgramInfo(WebGLProgram* prog, gl:
 
 #ifdef DUMP_SHADERVAR_MAPPINGS
         printf_stderr("[attrib %i] %s/%s\n", i, mappedName.BeginReading(),
                       userName.BeginReading());
         printf_stderr("    lengthWithoutNull: %d\n", lengthWithoutNull);
 #endif
 
         const bool isArray = false;
-        AddActiveInfo(prog->Context(), elemCount, elemType, isArray, userName, mappedName,
+        AddActiveInfo(prog->mContext, elemCount, elemType, isArray, userName, mappedName,
                       &info->activeAttribs, &info->attribMap);
 
         // Collect active locations:
         GLint loc = gl->fGetAttribLocation(prog->mGLName, mappedName.BeginReading());
         if (loc == -1)
             MOZ_CRASH("Active attrib has no location.");
 
         info->activeAttribLocs.insert(loc);
@@ -227,17 +227,17 @@ QueryProgramInfo(WebGLProgram* prog, gl:
 #ifdef DUMP_SHADERVAR_MAPPINGS
         printf_stderr("[uniform %i] %s/%i/%s/%s\n", i, mappedName.BeginReading(),
                       (int)isArray, baseMappedName.BeginReading(),
                       baseUserName.BeginReading());
         printf_stderr("    lengthWithoutNull: %d\n", lengthWithoutNull);
         printf_stderr("    isArray: %d\n", (int)isArray);
 #endif
 
-        AddActiveInfo(prog->Context(), elemCount, elemType, isArray, baseUserName,
+        AddActiveInfo(prog->mContext, elemCount, elemType, isArray, baseUserName,
                       baseMappedName, &info->activeUniforms, &info->uniformMap);
     }
 
     // Uniform Blocks
 
     if (gl->IsSupported(gl::GLFeature::uniform_buffer_object)) {
         GLuint numActiveUniformBlocks = 0;
         gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_UNIFORM_BLOCKS,
--- a/dom/canvas/WebGLProgram.h
+++ b/dom/canvas/WebGLProgram.h
@@ -191,17 +191,17 @@ public:
 
     bool IsLinked() const { return mMostRecentLinkInfo; }
 
     const webgl::LinkedProgramInfo* LinkInfo() const {
         return mMostRecentLinkInfo.get();
     }
 
     WebGLContext* GetParentObject() const {
-        return Context();
+        return mContext;
     }
 
     virtual JSObject* WrapObject(JSContext* js, JS::Handle<JSObject*> givenProto) override;
 
 private:
     ~WebGLProgram();
 
     bool LinkAndUpdate();
--- a/dom/canvas/WebGLQuery.h
+++ b/dom/canvas/WebGLQuery.h
@@ -28,17 +28,17 @@ public:
         return mType != 0;
     }
 
     // WebGLRefCountedObject
     void Delete();
 
     // nsWrapperCache
     WebGLContext* GetParentObject() const {
-        return Context();
+        return mContext;
     }
 
     // NS
     virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
 
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLQuery)
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLQuery)
 
--- a/dom/canvas/WebGLRenderbuffer.cpp
+++ b/dom/canvas/WebGLRenderbuffer.cpp
@@ -46,20 +46,20 @@ WebGLRenderbuffer::WrapObject(JSContext*
 {
     return dom::WebGLRenderbufferBinding::Wrap(cx, this, givenProto);
 }
 
 WebGLRenderbuffer::WebGLRenderbuffer(WebGLContext* webgl)
     : WebGLContextBoundObject(webgl)
     , mPrimaryRB(0)
     , mSecondaryRB(0)
-    , mInternalFormat(0)
-    , mInternalFormatForGL(0)
+    , mFormat(nullptr)
     , mImageDataStatus(WebGLImageDataStatus::NoImageData)
     , mSamples(1)
+    , mIsUsingSecondary(false)
 #ifdef ANDROID
     , mIsRB(false)
 #endif
 {
     mContext->MakeContextCurrent();
 
     mContext->gl->fGenRenderbuffers(1, &mPrimaryRB);
     if (!SupportsDepthStencil(mContext->gl))
@@ -81,72 +81,31 @@ WebGLRenderbuffer::Delete()
 #ifdef ANDROID
     mIsRB = false;
 #endif
 }
 
 int64_t
 WebGLRenderbuffer::MemoryUsage() const
 {
-    int64_t pixels = int64_t(Width()) * int64_t(Height());
-
-    GLenum primaryFormat = InternalFormatForGL();
     // If there is no defined format, we're not taking up any memory
-    if (!primaryFormat)
+    if (!mFormat)
         return 0;
 
-    int64_t secondarySize = 0;
-    if (mSecondaryRB) {
-        if (NeedsDepthStencilEmu(mContext->gl, primaryFormat)) {
-            primaryFormat = DepthStencilDepthFormat(mContext->gl);
-            secondarySize = 1*pixels; // STENCIL_INDEX8
-        } else {
-            secondarySize = 1*1*2; // 1x1xRGBA4
-        }
+    auto bytesPerPixel = mFormat->format->estimatedBytesPerPixel;
+    uint64_t pixels = uint64_t(mWidth) * uint64_t(mHeight);
+
+    uint64_t totalSize = pixels * bytesPerPixel;
+
+    // If we have the same bytesPerPixel whether or not we have a secondary RB.
+    if (mSecondaryRB && !mIsUsingSecondary) {
+        totalSize += 2; // 1x1xRGBA4
     }
 
-    int64_t primarySize = 0;
-    switch (primaryFormat) {
-        case LOCAL_GL_STENCIL_INDEX8:
-            primarySize = 1*pixels;
-            break;
-        case LOCAL_GL_RGBA4:
-        case LOCAL_GL_RGB5_A1:
-        case LOCAL_GL_RGB565:
-        case LOCAL_GL_DEPTH_COMPONENT16:
-            primarySize = 2*pixels;
-            break;
-        case LOCAL_GL_RGB8:
-        case LOCAL_GL_DEPTH_COMPONENT24:
-            primarySize = 3*pixels;
-            break;
-        case LOCAL_GL_RGBA8:
-        case LOCAL_GL_SRGB8_ALPHA8_EXT:
-        case LOCAL_GL_DEPTH24_STENCIL8:
-        case LOCAL_GL_DEPTH_COMPONENT32:
-            primarySize = 4*pixels;
-            break;
-        case LOCAL_GL_RGB16F:
-            primarySize = 2*3*pixels;
-            break;
-        case LOCAL_GL_RGBA16F:
-            primarySize = 2*4*pixels;
-            break;
-        case LOCAL_GL_RGB32F:
-            primarySize = 4*3*pixels;
-            break;
-        case LOCAL_GL_RGBA32F:
-            primarySize = 4*4*pixels;
-            break;
-        default:
-            MOZ_ASSERT(false, "Unknown `primaryFormat`.");
-            break;
-    }
-
-    return primarySize + secondarySize;
+    return int64_t(totalSize);
 }
 
 void
 WebGLRenderbuffer::BindRenderbuffer() const
 {
     /* Do this explicitly here, since the meaning changes for depth-stencil emu.
      * Under normal circumstances, there's only one RB: `mPrimaryRB`.
      * `mSecondaryRB` is used when we have to pretend that the renderbuffer is
@@ -167,76 +126,116 @@ static void
 RenderbufferStorageMaybeMultisample(gl::GLContext* gl, GLsizei samples,
                                     GLenum internalFormat, GLsizei width,
                                     GLsizei height)
 {
     MOZ_ASSERT_IF(samples >= 1, gl->IsSupported(gl::GLFeature::framebuffer_multisample));
     MOZ_ASSERT(samples >= 0);
     MOZ_ASSERT(samples <= gl->MaxSamples());
 
+    // 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;
+    }
+
     if (samples > 0) {
         gl->fRenderbufferStorageMultisample(LOCAL_GL_RENDERBUFFER, samples,
-                                            internalFormat, width, height);
+                                            internalFormatForGL, width, height);
     } else {
-        gl->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER, internalFormat, width,
+        gl->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER, internalFormatForGL, width,
                                  height);
     }
 }
 
 void
-WebGLRenderbuffer::RenderbufferStorage(GLsizei samples, GLenum internalFormat,
-                                       GLsizei width, GLsizei height) const
+WebGLRenderbuffer::RenderbufferStorage(GLsizei samples,
+                                       const webgl::FormatUsageInfo* format,
+                                       GLsizei width, GLsizei height)
 {
     MOZ_ASSERT(mContext->mBoundRenderbuffer == this);
 
-    InvalidateStatusOfAttachedFBs();
 
     gl::GLContext* gl = mContext->gl;
     MOZ_ASSERT(samples >= 0 && samples <= 256); // Sanity check.
 
-    GLenum primaryFormat = internalFormat;
+    GLenum primaryFormat = format->format->sizedFormat;
     GLenum secondaryFormat = 0;
 
     if (NeedsDepthStencilEmu(mContext->gl, primaryFormat)) {
         primaryFormat = DepthStencilDepthFormat(gl);
         secondaryFormat = LOCAL_GL_STENCIL_INDEX8;
     }
 
     RenderbufferStorageMaybeMultisample(gl, samples, primaryFormat, width,
                                         height);
 
-    if (!mSecondaryRB) {
-        MOZ_ASSERT(!secondaryFormat);
-        return;
+    if (mSecondaryRB) {
+        // We can't leave the secondary RB unspecified either, since we should
+        // handle the case where we attach a non-depth-stencil RB to a
+        // depth-stencil attachment point, or attach this depth-stencil RB to a
+        // non-depth-stencil attachment point.
+        gl::ScopedBindRenderbuffer autoRB(gl, mSecondaryRB);
+        if (secondaryFormat) {
+            RenderbufferStorageMaybeMultisample(gl, samples, secondaryFormat, width,
+                                                height);
+        } else {
+            RenderbufferStorageMaybeMultisample(gl, samples, LOCAL_GL_RGBA4, 1, 1);
+        }
     }
-    // We can't leave the secondary RB unspecified either, since we should
-    // handle the case where we attach a non-depth-stencil RB to a
-    // depth-stencil attachment point, or attach this depth-stencil RB to a
-    // non-depth-stencil attachment point.
-    gl::ScopedBindRenderbuffer autoRB(gl, mSecondaryRB);
-    if (secondaryFormat) {
-        RenderbufferStorageMaybeMultisample(gl, samples, secondaryFormat, width,
-                                            height);
-    } else {
-        RenderbufferStorageMaybeMultisample(gl, samples, LOCAL_GL_RGBA4, 1, 1);
-    }
+
+    mSamples = samples;
+    mFormat = format;
+    mWidth = width;
+    mHeight = height;
+    mImageDataStatus = WebGLImageDataStatus::UninitializedImageData;
+    mIsUsingSecondary = bool(secondaryFormat);
+
+    InvalidateStatusOfAttachedFBs();
 }
 
 void
 WebGLRenderbuffer::FramebufferRenderbuffer(FBAttachment attachment) const
 {
     gl::GLContext* gl = mContext->gl;
     if (attachment != LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
         gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, attachment.get(),
                                      LOCAL_GL_RENDERBUFFER, mPrimaryRB);
         return;
     }
 
     GLuint stencilRB = mPrimaryRB;
-    if (NeedsDepthStencilEmu(mContext->gl, InternalFormatForGL())) {
+    if (mIsUsingSecondary) {
         MOZ_ASSERT(mSecondaryRB);
         stencilRB = mSecondaryRB;
     }
     gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER,
                                  LOCAL_GL_DEPTH_ATTACHMENT,
                                  LOCAL_GL_RENDERBUFFER, mPrimaryRB);
     gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER,
                                  LOCAL_GL_STENCIL_ATTACHMENT,
@@ -246,30 +245,23 @@ WebGLRenderbuffer::FramebufferRenderbuff
 GLint
 WebGLRenderbuffer::GetRenderbufferParameter(RBTarget target,
                                             RBParam pname) const
 {
     gl::GLContext* gl = mContext->gl;
 
     switch (pname.get()) {
     case LOCAL_GL_RENDERBUFFER_STENCIL_SIZE:
-        if (NeedsDepthStencilEmu(mContext->gl, InternalFormatForGL())) {
-            if (gl->WorkAroundDriverBugs() &&
-                gl->Renderer() == gl::GLRenderer::Tegra)
-            {
-                return 8;
-            }
+        if (!mFormat)
+            return 0;
 
-            gl::ScopedBindRenderbuffer autoRB(gl, mSecondaryRB);
+        if (!mFormat->format->hasStencil)
+            return 0;
 
-            GLint i = 0;
-            gl->fGetRenderbufferParameteriv(target.get(), pname.get(), &i);
-            return i;
-        }
-        // Fall through otherwise.
+        return 8;
 
     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:
@@ -280,14 +272,23 @@ WebGLRenderbuffer::GetRenderbufferParame
         }
     }
 
     MOZ_ASSERT(false,
                "This function should only be called with valid `pname`.");
     return 0;
 }
 
+GLenum
+WebGLRenderbuffer::GetInternalFormat() const
+{
+    if (!mFormat)
+        return 0;
+
+    return mFormat->format->sizedFormat;
+}
+
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLRenderbuffer)
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLRenderbuffer, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLRenderbuffer, Release)
 
 } // namespace mozilla
--- a/dom/canvas/WebGLRenderbuffer.h
+++ b/dom/canvas/WebGLRenderbuffer.h
@@ -9,16 +9,19 @@
 #include "mozilla/LinkedList.h"
 #include "nsWrapperCache.h"
 
 #include "WebGLFramebufferAttachable.h"
 #include "WebGLObjectModel.h"
 #include "WebGLStrongTypes.h"
 
 namespace mozilla {
+namespace webgl {
+struct FormatUsageInfo;
+}
 
 class WebGLRenderbuffer final
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLRenderbuffer>
     , public LinkedListElement<WebGLRenderbuffer>
     , public WebGLRectangleObject
     , public WebGLContextBoundObject
     , public WebGLFramebufferAttachable
@@ -28,83 +31,70 @@ public:
 
     void Delete();
 
     bool HasUninitializedImageData() const {
         MOZ_ASSERT(mImageDataStatus != WebGLImageDataStatus::NoImageData);
         return mImageDataStatus == WebGLImageDataStatus::UninitializedImageData;
     }
 
-    void SetImageDataStatus(WebGLImageDataStatus x) {
-        // there is no way to go from having image data to not having any
-        MOZ_ASSERT(x != WebGLImageDataStatus::NoImageData ||
-                   mImageDataStatus == WebGLImageDataStatus::NoImageData);
-        mImageDataStatus = x;
-    }
-
     bool IsDefined() const {
-        if (mInternalFormat == LOCAL_GL_NONE) {
+        if (!mFormat) {
             MOZ_ASSERT(!mWidth && !mHeight);
             return false;
         }
         return true;
     }
 
     GLsizei Samples() const { return mSamples; }
-    void SetSamples(GLsizei samples) { mSamples = samples; }
 
     GLuint PrimaryGLName() const { return mPrimaryRB; }
 
-    GLenum InternalFormat() const { return mInternalFormat; }
-    void SetInternalFormat(GLenum internalFormat) {
-        mInternalFormat = internalFormat;
-    }
+    const webgl::FormatUsageInfo* Format() const { return mFormat; }
 
-    GLenum InternalFormatForGL() const { return mInternalFormatForGL; }
-    void SetInternalFormatForGL(GLenum internalFormatForGL) {
-        mInternalFormatForGL = internalFormatForGL;
-    }
+    GLenum GetInternalFormat() const;
 
     int64_t MemoryUsage() const;
 
     WebGLContext* GetParentObject() const {
-        return Context();
+        return mContext;
     }
 
     void BindRenderbuffer() const;
-    void RenderbufferStorage(GLsizei samples, GLenum internalFormat,
-                             GLsizei width, GLsizei height) const;
+    void RenderbufferStorage(GLsizei samples, const webgl::FormatUsageInfo* format,
+                             GLsizei width, GLsizei height);
     void FramebufferRenderbuffer(FBAttachment attachment) const;
     // Only handles a subset of `pname`s.
     GLint GetRenderbufferParameter(RBTarget target, RBParam pname) const;
 
     virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
 
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLRenderbuffer)
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLRenderbuffer)
 
 protected:
     ~WebGLRenderbuffer() {
         DeleteOnce();
     }
 
     GLuint mPrimaryRB;
     GLuint mSecondaryRB;
-    GLenum mInternalFormat;
-    GLenum mInternalFormatForGL;
+    const webgl::FormatUsageInfo* mFormat;
     WebGLImageDataStatus mImageDataStatus;
     GLsizei mSamples;
+    bool mIsUsingSecondary;
 #ifdef ANDROID
     // Bug 1140459: Some drivers (including our test slaves!) don't
     // give reasonable answers for IsRenderbuffer, maybe others.
     // This shows up on Android 2.3 emulator.
     //
     // So we track the `is a Renderbuffer` state ourselves.
     bool mIsRB;
 #endif
 
     friend class WebGLContext;
     friend class WebGLFramebuffer;
+    friend class WebGLFBAttachPoint;
 };
 
 } // namespace mozilla
 
 #endif // WEBGL_RENDERBUFFER_H_
--- a/dom/canvas/WebGLSampler.cpp
+++ b/dom/canvas/WebGLSampler.cpp
@@ -30,17 +30,17 @@ WebGLSampler::Delete()
     mContext->gl->fDeleteSamplers(1, &mGLName);
 
     removeFrom(mContext->mSamplers);
 }
 
 WebGLContext*
 WebGLSampler::GetParentObject() const
 {
-    return Context();
+    return mContext;
 }
 
 JSObject*
 WebGLSampler::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto)
 {
     return dom::WebGLSamplerBinding::Wrap(cx, this, givenProto);
 }
 
--- a/dom/canvas/WebGLShader.cpp
+++ b/dom/canvas/WebGLShader.cpp
@@ -6,16 +6,17 @@
 #include "WebGLShader.h"
 
 #include "angle/ShaderLang.h"
 #include "GLContext.h"
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "mozilla/MemoryReporting.h"
 #include "nsPrintfCString.h"
 #include "nsString.h"
+#include "prenv.h"
 #include "WebGLContext.h"
 #include "WebGLObjectModel.h"
 #include "WebGLShaderValidator.h"
 #include "WebGLValidateStrings.h"
 
 namespace mozilla {
 
 // On success, writes to out_validator and out_translatedSource.
@@ -162,45 +163,46 @@ WebGLShader::ShaderSource(const nsAStrin
     StripComments stripComments(source);
     const nsAString& cleanSource = Substring(stripComments.result().Elements(),
                                              stripComments.length());
     if (!ValidateGLSLString(cleanSource, mContext, "shaderSource"))
         return;
 
     // We checked that the source stripped of comments is in the
     // 7-bit ASCII range, so we can skip the NS_IsAscii() check.
-    NS_LossyConvertUTF16toASCII sourceCString(cleanSource);
+    const NS_LossyConvertUTF16toASCII sourceCString(cleanSource);
 
     if (mContext->gl->WorkAroundDriverBugs()) {
         const size_t maxSourceLength = 0x3ffff;
         if (sourceCString.Length() > maxSourceLength) {
             mContext->ErrorInvalidValue("shaderSource: Source has more than %d"
                                         " characters. (Driver workaround)",
                                         maxSourceLength);
             return;
         }
     }
 
-    // HACK - dump shader source
-    {
-/*
-        printf_stderr("//-*- glsl -*-\n");
-        // Wow - Roll Your Own For Each Lines because printf_stderr has a hard-coded internal size, so long strings are truncated.
-        const nsString& src = shader->Source();
+    if (PR_GetEnv("MOZ_WEBGL_DUMP_SHADERS")) {
+        printf_stderr("////////////////////////////////////////\n");
+        printf_stderr("// MOZ_WEBGL_DUMP_SHADERS:\n");
+
+        // Wow - Roll Your Own Foreach-Lines because printf_stderr has a hard-coded
+        // internal size, so long strings are truncated.
+
         int32_t start = 0;
-        int32_t end = src.Find("\n", false, start, -1);
+        int32_t end = sourceCString.Find("\n", false, start, -1);
         while (end > -1) {
-            printf_stderr("%s\n", NS_ConvertUTF16toUTF8(nsDependentSubstring(src, start, end - start)).get());
+            const nsCString line(sourceCString.BeginReading() + start, end - start);
+            printf_stderr("%s\n", line.BeginReading());
             start = end + 1;
-            end = src.Find("\n", false, start, -1);
+            end = sourceCString.Find("\n", false, start, -1);
         }
-        printf_stderr("//\n");
-*/
+
+        printf_stderr("////////////////////////////////////////\n");
     }
-    // HACK
 
     mSource = source;
     mCleanSource = sourceCString;
 }
 
 void
 WebGLShader::CompileShader()
 {
--- a/dom/canvas/WebGLShader.h
+++ b/dom/canvas/WebGLShader.h
@@ -71,17 +71,17 @@ public:
                                         const std::vector<nsCString>& varyings,
                                         GLenum bufferMode,
                                         std::vector<std::string>* out_mappedVaryings) const;
 
     // Other funcs
     size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
     void Delete();
 
-    WebGLContext* GetParentObject() const { return Context(); }
+    WebGLContext* GetParentObject() const { return mContext; }
 
     virtual JSObject* WrapObject(JSContext* js, JS::Handle<JSObject*> givenProto) override;
 
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLShader)
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLShader)
 
 public:
     const GLuint mGLName;
--- a/dom/canvas/WebGLStrongTypes.h
+++ b/dom/canvas/WebGLStrongTypes.h
@@ -142,16 +142,17 @@ public:
         MOZ_ASSERT(IsValueLegal(mValue));
     }
 
     GLenum get() const {
         MOZ_ASSERT(mValue != NonexistantGLenum);
         return mValue;
     }
 
+
     bool operator==(const StrongGLenum& other) const {
         return get() == other.get();
     }
 
     bool operator!=(const StrongGLenum& other) const {
         return get() != other.get();
     }
 
--- a/dom/canvas/WebGLSync.cpp
+++ b/dom/canvas/WebGLSync.cpp
@@ -29,17 +29,17 @@ WebGLSync::Delete()
     mContext->gl->fDeleteSync(mGLName);
     mGLName = 0;
     LinkedListElement<WebGLSync>::remove();
 }
 
 WebGLContext*
 WebGLSync::GetParentObject() const
 {
-    return Context();
+    return mContext;
 }
 
 // -------------------------------------------------------------------------
 // IMPLEMENT NS
 JSObject*
 WebGLSync::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto)
 {
     return dom::WebGLSyncBinding::Wrap(cx, this, givenProto);
--- a/dom/canvas/WebGLTexelConversions.cpp
+++ b/dom/canvas/WebGLTexelConversions.cpp
@@ -321,83 +321,83 @@ public:
     bool Success() const {
         return mSuccess;
     }
 };
 
 } // end anonymous namespace
 
 bool
-WebGLContext::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)
+ConvertImage(size_t width, size_t height,
+             const void* srcBegin, size_t srcStride, gl::OriginPos srcOrigin,
+             WebGLTexelFormat srcFormat, bool srcPremultiplied,
+             void* dstBegin, size_t dstStride, gl::OriginPos dstOrigin,
+             WebGLTexelFormat dstFormat, bool dstPremultiplied)
 {
-    if (width <= 0 || height <= 0)
-        return true;
-
-    const bool FormatsRequireNoPremultiplicationOp =
-        !HasAlpha(srcFormat) ||
-        !HasColor(srcFormat) ||
-        !HasColor(dstFormat);
-
-    if (srcFormat == dstFormat &&
-        (FormatsRequireNoPremultiplicationOp || srcPremultiplied == dstPremultiplied))
-    {
-        // fast exit path: we just have to memcpy all the rows.
-        //
-        // The case where absolutely nothing needs to be done is supposed to have
-        // been handled earlier (in TexImage2D_base, etc).
-        //
-        // So the case we're handling here is when even though no format conversion is needed,
-        // we still might have to flip vertically and/or to adjust to a different stride.
-
-        MOZ_ASSERT(mPixelStoreFlipY || srcStride != dstStride, "Performance trap -- should handle this case earlier, to avoid memcpy");
-
-        size_t row_size = width * dstTexelSize; // doesn't matter, src and dst formats agree
-        const uint8_t* ptr = src;
-        const uint8_t* src_end = src + height * srcStride;
-
-        uint8_t* dst_row = mPixelStoreFlipY
-                           ? dst + (height-1) * dstStride
-                           : dst;
-        ptrdiff_t dstStrideSigned(dstStride);
-        ptrdiff_t dst_delta = mPixelStoreFlipY ? -dstStrideSigned : dstStrideSigned;
-
-        while(ptr != src_end) {
-            memcpy(dst_row, ptr, row_size);
-            ptr += srcStride;
-            dst_row += dst_delta;
-        }
-        return true;
-    }
-
     if (srcFormat == WebGLTexelFormat::FormatNotSupportingAnyConversion ||
         dstFormat == WebGLTexelFormat::FormatNotSupportingAnyConversion)
     {
         return false;
     }
 
-    uint8_t* dstStart = dst;
-    ptrdiff_t signedDstStride = dstStride;
-    if (mPixelStoreFlipY) {
-        dstStart = dst + (height - 1) * dstStride;
-        signedDstStride = -signedDstStride;
+    if (!width || !height)
+        return true;
+
+    const bool shouldYFlip = (srcOrigin != dstOrigin);
+
+    const bool canSkipPremult = (!HasAlpha(srcFormat) ||
+                                 !HasColor(srcFormat) ||
+                                 !HasColor(dstFormat));
+
+    WebGLTexelPremultiplicationOp premultOp;
+    if (canSkipPremult) {
+        premultOp = WebGLTexelPremultiplicationOp::None;
+    } else if (!srcPremultiplied && dstPremultiplied) {
+        premultOp = WebGLTexelPremultiplicationOp::Premultiply;
+    } else if (srcPremultiplied && !dstPremultiplied) {
+        premultOp = WebGLTexelPremultiplicationOp::Unpremultiply;
+    } else {
+        premultOp = WebGLTexelPremultiplicationOp::None;
     }
 
-    WebGLImageConverter converter(width, height, src, dstStart, srcStride, signedDstStride);
+    const uint8_t* srcItr = (const uint8_t*)srcBegin;
+    const uint8_t* const srcEnd = srcItr + srcStride * height;
+    uint8_t* dstItr = (uint8_t*)dstBegin;
+    ptrdiff_t dstItrStride = dstStride;
+    if (shouldYFlip) {
+         dstItr = dstItr + dstStride * (height - 1);
+         dstItrStride = -dstItrStride;
+    }
 
-    const WebGLTexelPremultiplicationOp premultiplicationOp
-        = FormatsRequireNoPremultiplicationOp     ? WebGLTexelPremultiplicationOp::None
-        : (!srcPremultiplied && dstPremultiplied) ? WebGLTexelPremultiplicationOp::Premultiply
-        : (srcPremultiplied && !dstPremultiplied) ? WebGLTexelPremultiplicationOp::Unpremultiply
-                                                  : WebGLTexelPremultiplicationOp::None;
+    if (srcFormat == dstFormat && premultOp == WebGLTexelPremultiplicationOp::None) {
+        // Fast exit path: we just have to memcpy all the rows.
+        //
+        // The case where absolutely nothing needs to be done is supposed to have
+        // been handled earlier (in TexImage2D_base, etc).
+        //
+        // So the case we're handling here is when even though no format conversion is
+        // needed, we still might have to flip vertically and/or to adjust to a different
+        // stride.
+
+        MOZ_ASSERT(!shouldYFlip || srcStride != dstStride,
+                   "Performance trap -- should handle this case earlier to avoid memcpy");
 
-    converter.run(srcFormat, dstFormat, premultiplicationOp);
+        const auto bytesPerPixel = TexelBytesForFormat(srcFormat);
+        const size_t bytesPerRow = bytesPerPixel * width;
+
+        while (srcItr != srcEnd) {
+            memcpy(dstItr, srcItr, bytesPerRow);
+            srcItr += srcStride;
+            dstItr += dstItrStride;
+        }
+        return true;
+    }
+
+    WebGLImageConverter converter(width, height, srcItr, dstItr, srcStride, dstItrStride);
+    converter.run(srcFormat, dstFormat, premultOp);
 
     if (!converter.Success()) {
         // the dst image may be left uninitialized, so we better not try to
         // continue even in release builds. This should never happen anyway,
         // and would be a bug in our code.
         NS_RUNTIMEABORT("programming mistake in WebGL texture conversions");
     }
 
--- a/dom/canvas/WebGLTexelConversions.h
+++ b/dom/canvas/WebGLTexelConversions.h
@@ -33,16 +33,24 @@
 #endif
 
 #include "WebGLTypes.h"
 #include <stdint.h>
 #include "mozilla/Attributes.h"
 
 namespace mozilla {
 
+bool ConvertImage(size_t width, size_t height,
+                  const void* srcBegin, size_t srcStride, gl::OriginPos srcOrigin,
+                  WebGLTexelFormat srcFormat, bool srcPremultiplied,
+                  void* dstBegin, size_t dstStride, gl::OriginPos dstOrigin,
+                  WebGLTexelFormat dstFormat, bool dstPremultiplied);
+
+//////////////////////////////////////////////////////////////////////////////////////////
+
 // single precision float
 // seeeeeeeemmmmmmmmmmmmmmmmmmmmmmm
 
 // half precision float
 // seeeeemmmmmmmmmm
 
 // IEEE 16bits floating point:
 const uint16_t kFloat16Value_Zero     = 0x0000; // = 0000000000000000b
--- a/dom/canvas/WebGLTexture.cpp
+++ b/dom/canvas/WebGLTexture.cpp
@@ -98,17 +98,17 @@ WebGLTexture::ImageInfo::OnRespecify() c
 }
 
 size_t
 WebGLTexture::ImageInfo::MemoryUsage() const
 {
     if (!IsDefined())
         return 0;
 
-    const auto bytesPerTexel = mFormat->formatInfo->bytesPerPixel;
+    const auto bytesPerTexel = mFormat->format->estimatedBytesPerPixel;
     return size_t(mWidth) * size_t(mHeight) * size_t(mDepth) * bytesPerTexel;
 }
 
 void
 WebGLTexture::ImageInfo::SetIsDataInitialized(bool isDataInitialized, WebGLTexture* tex)
 {
     MOZ_ASSERT(tex);
     MOZ_ASSERT(this >= &tex->mImageInfoArr[0]);
@@ -164,28 +164,28 @@ WebGLTexture::MemoryUsage() const
         return 0;
 
     size_t result = 0;
     MOZ_CRASH("todo");
     return result;
 }
 
 void
-WebGLTexture::SetImageInfoAtFace(uint8_t face, uint32_t level, const ImageInfo& val)
+WebGLTexture::SetImageInfo(ImageInfo* target, const ImageInfo& newInfo)
 {
-    ImageInfoAtFace(face, level) = val;
+    *target = newInfo;
 
     InvalidateFakeBlackCache();
 }
 
 void
-WebGLTexture::SetImageInfosAtLevel(uint32_t level, const ImageInfo& val)
+WebGLTexture::SetImageInfosAtLevel(uint32_t level, const ImageInfo& newInfo)
 {
     for (uint8_t i = 0; i < mFaceCount; i++) {
-        ImageInfoAtFace(i, level) = val;
+        ImageInfoAtFace(i, level) = newInfo;
     }
 
     InvalidateFakeBlackCache();
 }
 
 static inline uint32_t
 MaxMipmapLevelsForSize(const WebGLTexture::ImageInfo& info)
 {
@@ -288,42 +288,16 @@ WebGLTexture::IsCubeComplete() const
         {
             return false;
         }
     }
 
     return true;
 }
 
-static bool
-IsColorFormat(TexInternalFormat format)
-{
-    TexInternalFormat unsizedformat = UnsizedInternalFormatFromInternalFormat(format);
-
-    // ALPHA *is* a "color format"!
-    return unsizedformat != LOCAL_GL_DEPTH_COMPONENT &&
-           unsizedformat != LOCAL_GL_DEPTH_STENCIL &&
-           unsizedformat != LOCAL_GL_STENCIL_INDEX;
-}
-
-static bool
-FormatSupportsFiltering(WebGLContext* webgl, TexInternalFormat format)
-{
-    TexType type = TypeFromInternalFormat(format);
-
-    if (type == LOCAL_GL_FLOAT)
-        return webgl->IsExtensionEnabled(WebGLExtensionID::OES_texture_float_linear);
-
-    MOZ_ASSERT(type != LOCAL_GL_HALF_FLOAT_OES);
-    if (type == LOCAL_GL_HALF_FLOAT)
-        return webgl->IsExtensionEnabled(WebGLExtensionID::OES_texture_half_float_linear);
-
-    return true;
-}
-
 bool
 WebGLTexture::IsComplete(const char** const out_reason) const
 {
     // Texture completeness is established at GLES 3.0.4, p160-161.
     // "[A] texture is complete unless any of the following conditions hold true:"
 
     // "* Any dimension of the `level_base` array is not positive."
     const ImageInfo& baseImageInfo = BaseImageInfo();
@@ -356,17 +330,17 @@ WebGLTexture::IsComplete(const char** co
     }
 
     const bool isMinFilteringNearest = (mMinFilter == LOCAL_GL_NEAREST ||
                                         mMinFilter == LOCAL_GL_NEAREST_MIPMAP_NEAREST);
     const bool isMagFilteringNearest = (mMagFilter == LOCAL_GL_NEAREST);
     const bool isFilteringNearestOnly = (isMinFilteringNearest && isMagFilteringNearest);
     if (!isFilteringNearestOnly) {
         auto formatUsage = baseImageInfo.mFormat;
-        auto format = formatUsage->formatInfo;
+        auto format = formatUsage->format;
 
         // "* The effective internal format specified for the texture arrays is a sized
         //    internal color format that is not texture-filterable, and either the
         //    magnification filter is not NEAREST or the minification filter is neither
         //    NEAREST nor NEAREST_MIPMAP_NEAREST."
         // Since all (GLES3) unsized color formats are filterable just like their sized
         // equivalents, we don't have to care whether its sized or not.
         if (format->isColorFormat && !formatUsage->isFilterable) {
@@ -461,27 +435,28 @@ WebGLTexture::MaxEffectiveMipmapLevel() 
     const auto& imageInfo = BaseImageInfo();
     MOZ_ASSERT(imageInfo.IsDefined());
 
     uint32_t maxLevelBySize = mBaseMipmapLevel + imageInfo.MaxMipmapLevels() - 1;
     return std::min<uint32_t>(maxLevelBySize, mMaxMipmapLevel);
 }
 
 bool
-WebGLTexture::ResolveFakeBlackStatus(WebGLTextureFakeBlackStatus* const out)
+WebGLTexture::ResolveFakeBlackStatus(const char* funcName,
+                                     WebGLTextureFakeBlackStatus* const out)
 {
-    if (!ResolveFakeBlackStatus())
+    if (!ResolveFakeBlackStatus(funcName))
         return false;
 
     *out = mFakeBlackStatus;
     return true;
 }
 
 bool
-WebGLTexture::ResolveFakeBlackStatus()
+WebGLTexture::ResolveFakeBlackStatus(const char* funcName)
 {
     if (MOZ_LIKELY(mFakeBlackStatus != WebGLTextureFakeBlackStatus::Unknown))
         return true;
 
     const char* incompleteReason;
     if (!IsComplete(&incompleteReason)) {
         if (incompleteReason) {
             mContext->GenerateWarning("An active texture is going to be rendered as if it"
@@ -522,225 +497,60 @@ WebGLTexture::ResolveFakeBlackStatus()
 
     // Alright, we have both initialized and uninitialized data, so we have to initialize
     // the uninitialized images. Feel free to be slow.
     mContext->GenerateWarning("An active texture contains TexImages with uninitialized"
                               " data along with TexImages with initialized data, forcing"
                               " the implementation to (slowly) initialize the"
                               " uninitialized TexImages.");
 
+    GLenum baseTexImageTarget = mTarget.get();
+    if (baseTexImageTarget == LOCAL_GL_TEXTURE_CUBE_MAP)
+        baseTexImageTarget = LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X;
+
     for (uint32_t level = mBaseMipmapLevel; level <= maxLevel; level++) {
         for (uint8_t face = 0; face < mFaceCount; face++) {
-            if (!EnsureInitializedImageData(face, level))
+            TexImageTarget target = baseTexImageTarget + face;
+            if (!EnsureImageDataInitialized(funcName, target, level))
                 return false; // The world just exploded.
         }
     }
 
     mFakeBlackStatus = WebGLTextureFakeBlackStatus::NotNeeded;
     return true;
 }
 
-static bool
-ClearByMask(WebGLContext* webgl, GLbitfield mask)
-{
-    gl::GLContext* gl = webgl->GL();
-    MOZ_ASSERT(gl->IsCurrent());
-
-    GLenum status = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
-    if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE)
-        return false;
-
-    bool colorAttachmentsMask[WebGLContext::kMaxColorAttachments] = {false};
-    if (mask & LOCAL_GL_COLOR_BUFFER_BIT) {
-        colorAttachmentsMask[0] = true;
-    }
-
-    webgl->ForceClearFramebufferWithDefaultValues(false, mask, colorAttachmentsMask);
-    return true;
-}
-
-// `mask` from glClear.
-static bool
-ClearWithTempFB(WebGLContext* webgl, GLuint tex,
-                TexImageTarget texImageTarget, GLint level,
-                TexInternalFormat baseInternalFormat,
-                GLsizei width, GLsizei height)
+bool
+WebGLTexture::EnsureImageDataInitialized(const char* funcName, TexImageTarget target,
+                                         uint32_t level)
 {
-    MOZ_ASSERT(texImageTarget == LOCAL_GL_TEXTURE_2D);
-
-    gl::GLContext* gl = webgl->GL();
-    MOZ_ASSERT(gl->IsCurrent());
-
-    gl::ScopedFramebuffer fb(gl);
-    gl::ScopedBindFramebuffer autoFB(gl, fb.FB());
-    GLbitfield mask = 0;
-
-    switch (baseInternalFormat.get()) {
-    case LOCAL_GL_LUMINANCE:
-    case LOCAL_GL_LUMINANCE_ALPHA:
-    case LOCAL_GL_ALPHA:
-    case LOCAL_GL_RGB:
-    case LOCAL_GL_RGBA:
-    case LOCAL_GL_BGR:
-    case LOCAL_GL_BGRA:
-        mask = LOCAL_GL_COLOR_BUFFER_BIT;
-        gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0,
-                                  texImageTarget.get(), tex, level);
-        break;
-    case LOCAL_GL_DEPTH_COMPONENT32_OES:
-    case LOCAL_GL_DEPTH_COMPONENT24_OES:
-    case LOCAL_GL_DEPTH_COMPONENT16:
-    case LOCAL_GL_DEPTH_COMPONENT:
-        mask = LOCAL_GL_DEPTH_BUFFER_BIT;
-        gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT,
-                                  texImageTarget.get(), tex, level);
-        break;
-
-    case LOCAL_GL_DEPTH24_STENCIL8:
-    case LOCAL_GL_DEPTH_STENCIL:
-        mask = LOCAL_GL_DEPTH_BUFFER_BIT |
-               LOCAL_GL_STENCIL_BUFFER_BIT;
-        gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT,
-                                  texImageTarget.get(), tex, level);
-        gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_STENCIL_ATTACHMENT,
-                                  texImageTarget.get(), tex, level);
-        break;
-
-    default:
-        return false;
-    }
-    MOZ_ASSERT(mask);
-
-    if (ClearByMask(webgl, mask))
+    auto& imageInfo = ImageInfoAt(target, level);
+    if (imageInfo.IsDataInitialized())
         return true;
 
-    // Failed to simply build an FB from the tex, but maybe it needs a
-    // color buffer to be complete.
-
-    if (mask & LOCAL_GL_COLOR_BUFFER_BIT) {
-        // Nope, it already had one.
-        return false;
-    }
-
-    gl::ScopedRenderbuffer rb(gl);
-    {
-        // Only GLES guarantees RGBA4.
-        GLenum format = gl->IsGLES() ? LOCAL_GL_RGBA4 : LOCAL_GL_RGBA8;
-        gl::ScopedBindRenderbuffer rbBinding(gl, rb.RB());
-        gl->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER, format, width, height);
-    }
-
-    gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0,
-                                 LOCAL_GL_RENDERBUFFER, rb.RB());
-    mask |= LOCAL_GL_COLOR_BUFFER_BIT;
-
-    // Last chance!
-    return ClearByMask(webgl, mask);
-}
-
-static TexImageTarget
-TexImageTargetFromFace(TexTarget target, uint8_t face)
-{
-    if (target != LOCAL_GL_TEXTURE_CUBE_MAP)
-        return target.get();
-
-    return LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X + face;
+    return InitializeImageData(funcName, target, level);
 }
 
 bool
-WebGLTexture::EnsureInitializedImageData(uint8_t face, uint32_t level)
+WebGLTexture::InitializeImageData(const char* funcName, TexImageTarget target,
+                                  uint32_t level)
 {
-    ImageInfo& imageInfo = ImageInfoAtFace(face, level);
+    auto& imageInfo = ImageInfoAt(target, level);
     MOZ_ASSERT(imageInfo.IsDefined());
-
-    if (imageInfo.IsDataInitialized())
-        return true;
-
-    mContext->MakeContextCurrent();
-
-    const TexImageTarget texImageTarget = TexImageTargetFromFace(mTarget, face);
-
-    // Try to clear with glClear.
-    if (mTarget == LOCAL_GL_TEXTURE_2D) {
-        bool cleared = ClearWithTempFB(mContext, mGLName, texImageTarget, level,
-                                       imageInfo.mFormat, imageInfo.mHeight,
-                                       imageInfo.mWidth);
-        if (cleared) {
-            imageInfo.SetIsDataInitialized(true, this);
-            return true;
-        }
-    }
-
-    // That didn't work. Try uploading zeros then.
-    auto formatUsage = imageInfo.mFormat;
-    auto format = formatUsage->formatInfo;
-
-    const auto unpackAlignment = mContext->mPixelStoreUnpackAlignment;
-    CheckedUint32 checked_byteLength = WebGLContext::GetImageSize(imageInfo.mHeight,
-                                                                  imageInfo.mWidth,
-                                                                  imageInfo.mDepth,
-                                                                  format->bytesPerTexel,
-                                                                  unpackAlignment);
-    MOZ_ASSERT(checked_byteLength.isValid()); // Should have been checked earlier.
-
-    size_t byteCount = checked_byteLength.value();
+    MOZ_ASSERT(!imageInfo.IsDataInitialized());
 
-    UniquePtr<uint8_t> zeros((uint8_t*)calloc(1, byteCount));
-    if (zeros == nullptr) {
-        // Failed to allocate memory. Lose the context. Return OOM error.
-        mContext->ForceLoseContext(true);
-        mContext->ErrorOutOfMemory("EnsureInitializedImageData: Failed to alloc %u "
-                                   "bytes to clear image target `%s` level `%d`.",
-                                   byteCount, mContext->EnumName(texImageTarget.get()),
-                                   level);
-         return false;
-    }
-
-    gl::GLContext* gl = mContext->gl;
-    gl::ScopedBindTexture autoBindTex(gl, mGLName, mTarget.get());
-
-    GLenum driverInternalFormat = LOCAL_GL_NONE;
-    GLenum driverUnpackFormat = LOCAL_GL_NONE;
-    GLenum driverUnpackType = LOCAL_GL_NONE;
-    DriverFormatsFromEffectiveInternalFormat(gl, imageFormat, &driverInternalFormat,
-                                             &driverUnpackFormat, &driverUnpackType);
+    const bool respecifyTexture = false;
+    const auto& usage = imageInfo.mFormat;
+    const auto& width = imageInfo.mWidth;
+    const auto& height = imageInfo.mHeight;
+    const auto& depth = imageInfo.mDepth;
 
-    mContext->GetAndFlushUnderlyingGLErrors();
-    if (texImageTarget == LOCAL_GL_TEXTURE_3D) {
-        gl->fTexSubImage3D(texImageTarget.get(), level, 0, 0, 0, imageInfo.mWidth,
-                           imageInfo.mHeight, imageInfo.mDepth, driverUnpackFormat,
-                           driverUnpackType, zeros.get());
-    } else {
-        MOZ_ASSERT(imageInfo.mDepth == 1);
-        gl->fTexSubImage2D(texImageTarget.get(), level, 0, 0, imageInfo.mWidth,
-                           imageInfo.mHeight, driverUnpackFormat, driverUnpackType,
-                           zeros.get());
-    }
-
-    GLenum error = mContext->GetAndFlushUnderlyingGLErrors();
-    if (error) {
-        // Should only be OUT_OF_MEMORY. Anyway, there's no good way to recover
-        // from this here.
-        if (error != LOCAL_GL_OUT_OF_MEMORY) {
-            printf_stderr("Error: 0x%4x\n", error);
-            gfxCriticalError() << "GL context GetAndFlushUnderlyingGLErrors " << gfx::hexa(error);
-            // Errors on texture upload have been related to video
-            // memory exposure in the past, which is a security issue.
-            // Force loss of context.
-            mContext->ForceLoseContext(true);
-            return false;
-        }
-
-        // Out-of-memory uploading pixels to GL. Lose context and report OOM.
-        mContext->ForceLoseContext(true);
-        mContext->ErrorOutOfMemory("EnsureNoUninitializedImageData: Failed to "
-                                   "upload texture of width: %u, height: %u, "
-                                   "depth: %u to target %s level %d.",
-                                   imageInfo.mWidth, imageInfo.mHeight, imageInfo.mDepth,
-                                   mContext->EnumName(texImageTarget.get()), level);
+    if (!ZeroTextureData(mContext, funcName, respecifyTexture, target, level, usage, 0, 0,
+                         0, width, height, depth))
+    {
         return false;
     }
 
     imageInfo.SetIsDataInitialized(true, this);
     return true;
 }
 
 void
@@ -755,46 +565,57 @@ WebGLTexture::ClampLevelBaseAndMax()
     //  `[level_base, levels-1]`, where `levels` is the parameter passed to
     //   TexStorage* for the texture object."
     mBaseMipmapLevel = Clamp<uint32_t>(mBaseMipmapLevel, 0, mImmutableLevelCount - 1);
     mMaxMipmapLevel = Clamp<uint32_t>(mMaxMipmapLevel, mBaseMipmapLevel,
                                       mImmutableLevelCount - 1);
 }
 
 void
-WebGLTexture::PopulateMipChain(uint32_t baseLevel, uint32_t maxLevel)
+WebGLTexture::PopulateMipChain(uint32_t firstLevel, uint32_t lastLevel)
 {
-    const ImageInfo& baseImageInfo = ImageInfoAtFace(0, baseLevel);
+    const ImageInfo& baseImageInfo = ImageInfoAtFace(0, firstLevel);
     MOZ_ASSERT(baseImageInfo.IsDefined());
 
     uint32_t refWidth = baseImageInfo.mWidth;
     uint32_t refHeight = baseImageInfo.mHeight;
     uint32_t refDepth = baseImageInfo.mDepth;
     MOZ_ASSERT(refWidth && refHeight && refDepth);
 
-    for (uint32_t level = baseLevel; level <= maxLevel; level++) {
+    for (uint32_t level = firstLevel; level <= lastLevel; level++) {
         const ImageInfo cur(baseImageInfo.mFormat, refWidth, refHeight, refDepth,
                             baseImageInfo.IsDataInitialized());
 
         SetImageInfosAtLevel(level, cur);
 
-        // Higher levels are unaffected.
-        if (refWidth == 1 &&
-            refHeight == 1 &&
-            refDepth == 1)
-        {
-            break;
+        bool isMinimal = (refWidth == 1 &&
+                          refHeight == 1);
+        if (mTarget == LOCAL_GL_TEXTURE_3D) {
+            isMinimal &= (refDepth == 1);
         }
 
-        refWidth  = std::max(uint32_t(1), refWidth  / 2);
+        // Higher levels are unaffected.
+        if (isMinimal)
+            break;
+
+        refWidth = std::max(uint32_t(1), refWidth / 2);
         refHeight = std::max(uint32_t(1), refHeight / 2);
-        refDepth  = std::max(uint32_t(1), refDepth  / 2);
+        if (mTarget == LOCAL_GL_TEXTURE_3D) { // But not TEXTURE_2D_ARRAY!
+            refDepth = std::max(uint32_t(1), refDepth / 2);
+        }
     }
 }
 
+void
+WebGLTexture::InvalidateFakeBlackCache()
+{
+    mContext->InvalidateFakeBlackCache();
+    mFakeBlackStatus = WebGLTextureFakeBlackStatus::Unknown;
+}
+
 //////////////////////////////////////////////////////////////////////////////////////////
 // GL calls
 
 bool
 WebGLTexture::BindTexture(TexTarget texTarget)
 {
     // silently ignore a deleted texture
     if (IsDeleted())
@@ -855,24 +676,24 @@ WebGLTexture::GenerateMipmap(TexTarget t
     }
 
     if (!mContext->IsWebGL2() && !baseImageInfo.IsPowerOfTwo()) {
         mContext->ErrorInvalidOperation("generateMipmap: The base level of the texture does not"
                               " have power-of-two dimensions.");
         return;
     }
 
-    auto format = baseImageInfo.mFormat;
-    if (IsTextureFormatCompressed(format)) {
+    auto format = baseImageInfo.mFormat->format;
+    if (format->compression) {
         mContext->ErrorInvalidOperation("generateMipmap: Texture data at base level is"
                               " compressed.");
         return;
     }
 
-    if ((IsGLDepthFormat(format) || IsGLDepthStencilFormat(format)))
+    if (format->hasDepth)
         return mContext->ErrorInvalidOperation("generateMipmap: Depth textures are not supported");
 
     // Done with validation. Do the operation.
 
     mContext->MakeContextCurrent();
     gl::GLContext* gl = mContext->gl;
 
     if (gl->WorkAroundDriverBugs()) {
@@ -1106,14 +927,16 @@ WebGLTexture::TexParameter(TexTarget tex
 
     mContext->MakeContextCurrent();
     if (maybeIntParam)
         mContext->gl->fTexParameteri(texTarget.get(), pname, intParam);
     else
         mContext->gl->fTexParameterf(texTarget.get(), pname, floatParam);
 }
 
+////////////////////////////////////////////////////////////////////////////////
+
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLTexture)
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLTexture, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLTexture, Release)
 
 } // namespace mozilla
--- a/dom/canvas/WebGLTexture.h
+++ b/dom/canvas/WebGLTexture.h
@@ -10,30 +10,35 @@
 #include <map>
 
 #include "mozilla/Assertions.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/dom/TypedArray.h"
 #include "mozilla/LinkedList.h"
 #include "nsWrapperCache.h"
 
-#include "WebGLContextUtils.h"
 #include "WebGLFramebufferAttachable.h"
 #include "WebGLObjectModel.h"
 #include "WebGLStrongTypes.h"
 
 namespace mozilla {
 class ErrorResult;
+class WebGLContext;
 
 namespace dom {
 class Element;
 class ImageData;
 class ArrayBufferViewOrSharedArrayBufferView;
 } // namespace dom
 
+namespace webgl {
+class TexUnpackBlob;
+} // namespace webgl
+
+
 bool
 DoesTargetMatchDimensions(WebGLContext* webgl, TexImageTarget target, uint8_t dims,
                           const char* funcName);
 
 
 // NOTE: When this class is switched to new DOM bindings, update the (then-slow)
 // WrapObject calls in GetParameter and GetFramebufferAttachmentParameter.
 class WebGLTexture final
@@ -81,36 +86,36 @@ protected:
     // decl for `mImageInfoArr`.
 
 public:
     class ImageInfo;
 
     // And in turn, it needs these forwards:
 protected:
     // We need to forward these.
-    void SetImageInfoAtFace(uint8_t face, uint32_t level, const ImageInfo& val);
-    void SetImageInfosAtLevel(uint32_t level, const ImageInfo& val);
+    void SetImageInfo(ImageInfo* target, const ImageInfo& newInfo);
+    void SetImageInfosAtLevel(uint32_t level, const ImageInfo& newInfo);
 
 public:
     // We store information about the various images that are part of this
     // texture. (cubemap faces, mipmap levels)
     class ImageInfo
     {
-        friend void WebGLTexture::SetImageInfoAtFace(uint8_t face, uint32_t level,
-                                                     const ImageInfo& val);
+        friend void WebGLTexture::SetImageInfo(ImageInfo* target,
+                                               const ImageInfo& newInfo);
         friend void WebGLTexture::SetImageInfosAtLevel(uint32_t level,
-                                                       const ImageInfo& val);
+                                                       const ImageInfo& newInfo);
 
     public:
         static const ImageInfo kUndefined;
 
         // This is the "effective internal format" of the texture, an official
         // OpenGL spec concept, see OpenGL ES 3.0.3 spec, section 3.8.3, page
         // 126 and below.
-        const TexInternalFormat mFormat;
+        const webgl::FormatUsageInfo* const mFormat;
 
         const uint32_t mWidth;
         const uint32_t mHeight;
         const uint32_t mDepth;
 
     protected:
         bool mIsDataInitialized;
 
@@ -120,27 +125,25 @@ public:
         ImageInfo()
             : mFormat(LOCAL_GL_NONE)
             , mWidth(0)
             , mHeight(0)
             , mDepth(0)
             , mIsDataInitialized(false)
         { }
 
-        ImageInfo(TexInternalFormat format, uint32_t width, uint32_t height,
+        ImageInfo(const webgl::FormatUsageInfo* format, uint32_t width, uint32_t height,
                   uint32_t depth, bool isDataInitialized)
             : mFormat(format)
             , mWidth(width)
             , mHeight(height)
             , mDepth(depth)
             , mIsDataInitialized(isDataInitialized)
         {
-            MOZ_ASSERT(mFormat != LOCAL_GL_NONE);
-            MOZ_ASSERT_IF(!IsCompressedTextureFormat(mFormat.get()),
-                          mFormat != UnsizedInternalFormatFromInternalFormat(mFormat));
+            MOZ_ASSERT(mFormat);
         }
 
         void Clear();
 
         ~ImageInfo() {
             if (!IsDefined())
                 Clear();
         }
@@ -187,174 +190,103 @@ public:
     explicit WebGLTexture(WebGLContext* webgl, GLuint tex);
 
     void Delete();
 
     bool HasEverBeenBound() const { return mTarget != LOCAL_GL_NONE; }
     TexTarget Target() const { return mTarget; }
 
     WebGLContext* GetParentObject() const {
-        return Context();
+        return mContext;
     }
 
     virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
 
 protected:
     ~WebGLTexture() {
         DeleteOnce();
     }
+
 public:
     ////////////////////////////////////
     // GL calls
     bool BindTexture(TexTarget texTarget);
     void GenerateMipmap(TexTarget texTarget);
     JS::Value GetTexParameter(TexTarget texTarget, GLenum pname);
     bool IsTexture() const;
     void TexParameter(TexTarget texTarget, GLenum pname, GLint* maybeIntParam,
                       GLfloat* maybeFloatParam);
 
     ////////////////////////////////////
     // WebGLTextureUpload.cpp
 
-    void CompressedTexImage2D(TexImageTarget texImageTarget, GLint level,
-                              GLenum internalFormat, GLsizei width, GLsizei height,
-                              GLint border, const dom::ArrayBufferViewOrSharedArrayBufferView& view);
-
-    void CompressedTexImage3D(TexImageTarget texImageTarget, GLint level,
-                              GLenum internalFormat, GLsizei width, GLsizei height,
-                              GLsizei depth, GLint border, GLsizei imageSize,
-                              const dom::ArrayBufferViewOrSharedArrayBufferView& view);
-
-
-    void CompressedTexSubImage2D(TexImageTarget texImageTarget, GLint level,
-                                 GLint xOffset, GLint yOffset, GLsizei width,
-                                 GLsizei height, GLenum unpackFormat,
-                                 const dom::ArrayBufferViewOrSharedArrayBufferView& view);
-
-    void CompressedTexSubImage3D(TexImageTarget texImageTarget, GLint level,
-                                 GLint xOffset, GLint yOffset, GLint zOffset,
-                                 GLsizei width, GLsizei height, GLsizei depth,
-                                 GLenum unpackFormat, GLsizei imageSize,
-                                 const dom::ArrayBufferViewOrSharedArrayBufferView& view);
-
-
-    void CopyTexImage2D(TexImageTarget texImageTarget, GLint level, GLenum internalFormat,
-                        GLint x, GLint y, GLsizei width, GLsizei height, GLint border);
-
-
-    void CopyTexSubImage2D(TexImageTarget texImageTarget, GLint level, GLint xOffset,
-                           GLint yOffset, GLint x, GLint y, GLsizei width,
-                           GLsizei height);
-
-    void CopyTexSubImage3D(TexImageTarget texImageTarget, GLint level, GLint xOffset,
-                           GLint yOffset, GLint zOffset, GLint x, GLint y, GLsizei width,
-                           GLsizei height);
-
+    void TexOrSubImage(bool isSubImage, const char* funcName, TexImageTarget target,
+                       GLint level, GLenum internalFormat, GLint xOffset, GLint yOffset,
+                       GLint zOffset, GLsizei width, GLsizei height, GLsizei depth,
+                       GLint border, GLenum unpackFormat, GLenum unpackType,
+                       const dom::Nullable<dom::ArrayBufferViewOrSharedArrayBufferView>& maybeView);
 
-    void TexImage2D(TexImageTarget texImageTarget, GLint level, GLenum internalFormat,
-                    GLsizei width, GLsizei height, GLint border, GLenum unpackFormat,
-                    GLenum unpackType,
-                    const dom::Nullable<dom::ArrayBufferViewOrSharedArrayBufferView>& maybeView,
-                    ErrorResult* const out_rv);
-    void TexImage2D(TexImageTarget texImageTarget, GLint level, GLenum internalFormat,
-                    GLenum unpackFormat, GLenum unpackType, dom::ImageData* imageData,
-                    ErrorResult* const out_rv);
-    void TexImage2D(TexImageTarget texImageTarget, GLint level, GLenum internalFormat,
-                    GLenum unpackFormat, GLenum unpackType, dom::Element* elem,
-                    ErrorResult* const out_rv);
-
-    void TexImage3D(TexImageTarget target, GLint level, GLenum internalFormat,
-                    GLsizei width, GLsizei height, GLsizei depth, GLint border,
-                    GLenum unpackFormat, GLenum unpackType,
-                    const dom::Nullable<dom::ArrayBufferViewOrSharedArrayBufferView>& maybeView,
-                    ErrorResult* const out_rv);
-
-
-    void TexStorage2D(TexTarget texTarget, GLsizei levels, GLenum internalFormat,
-                      GLsizei width, GLsizei height);
-    void TexStorage3D(TexTarget texTarget, GLsizei levels, GLenum internalFormat,
-                      GLsizei width, GLsizei height, GLsizei depth);
-
+    void TexOrSubImage(bool isSubImage, const char* funcName, TexImageTarget target,
+                       GLint level, GLenum internalFormat, GLint xOffset, GLint yOffset,
+                       GLint zOffset, GLenum unpackFormat, GLenum unpackType,
+                       dom::ImageData* imageData);
 
-    void TexSubImage2D(TexImageTarget texImageTarget, GLint level, GLint xOffset,
-                       GLint yOffset, GLsizei width, GLsizei height, GLenum unpackFormat,
-                       GLenum unpackType,
-                       const dom::Nullable<dom::ArrayBufferViewOrSharedArrayBufferView>& maybeView,
-                       ErrorResult* const out_rv);
-    void TexSubImage2D(TexImageTarget texImageTarget, GLint level, GLint xOffset,
-                       GLint yOffset, GLenum unpackFormat, GLenum unpackType,
-                       dom::ImageData* imageData, ErrorResult* const out_rv);
-    void TexSubImage2D(TexImageTarget texImageTarget, GLint level, GLint xOffset,
-                       GLint yOffset, GLenum unpackFormat, GLenum unpackType,
-                       dom::Element* elem, ErrorResult* const out_rv);
-
-    void TexSubImage3D(TexImageTarget texImageTarget, 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* const out_rv);
-    void TexSubImage3D(TexImageTarget texImageTarget, GLint level, GLint xOffset,
-                       GLint yOffset, GLint zOffset, GLenum unpackFormat,
-                       GLenum unpackType, dom::ImageData* imageData,
-                       ErrorResult* const out_rv);
-    void TexSubImage3D(TexImageTarget texImageTarget, GLint level, GLint xOffset,
-                       GLint yOffset, GLint zOffset, GLenum unpackFormat,
-                       GLenum unpackType, dom::Element* elem, ErrorResult* const out_rv);
+    void TexOrSubImage(bool isSubImage, const char* funcName, TexImageTarget target,
+                       GLint level, GLenum internalFormat, GLint xOffset, GLint yOffset,
+                       GLint zOffset, GLenum unpackFormat, GLenum unpackType,
+                       dom::Element* elem, ErrorResult* const out_error);
 
 protected:
-
-    /** Like glTexImage2D, but if the call may change the texture size, checks
-     * any GL error generated by this glTexImage2D call and returns it.
-     */
-    GLenum CheckedTexImage2D(TexImageTarget texImageTarget, GLint level,
-                             TexInternalFormat internalFormat, GLsizei width,
-                             GLsizei height, GLint border, TexFormat format,
-                             TexType type, const GLvoid* data);
+    void TexOrSubImage(bool isSubImage, const char* funcName, TexImageTarget target,
+                       GLint level, GLenum internalFormat, GLint xOffset, GLint yOffset,
+                       GLint zOffset, GLint border, GLenum unpackFormat,
+                       GLenum unpackType, webgl::TexUnpackBlob* blob);
 
-    bool ValidateTexStorage(TexImageTarget texImageTarget, GLsizei levels, GLenum internalFormat,
-                            GLsizei width, GLsizei height, GLsizei depth,
-                            const char* funcName);
-    void SpecifyTexStorage(GLsizei levels, TexInternalFormat internalFormat,
-                           GLsizei width, GLsizei height, GLsizei depth);
-
-    void CopyTexSubImage2D_base(TexImageTarget texImageTarget,
-                                GLint level, GLenum rawInternalFormat,
-                                GLint xoffset, GLint yoffset, GLint x, GLint y,
-                                GLsizei width, GLsizei height, GLint border, bool isSub);
+    bool ValidateTexImageSpecification(const char* funcName, TexImageTarget target,
+                                       GLint level, GLsizei width, GLsizei height,
+                                       GLsizei depth, GLint border,
+                                       WebGLTexture::ImageInfo** const out_imageInfo);
+    bool ValidateTexImageSelection(const char* funcName, TexImageTarget target,
+                                   GLint level, GLint xOffset, GLint yOffset,
+                                   GLint zOffset, GLsizei width, GLsizei height,
+                                   GLsizei depth,
+                                   WebGLTexture::ImageInfo** const out_imageInfo);
 
-    bool TexImageFromVideoElement(TexImageTarget texImageTarget, GLint level,
-                                  GLenum internalFormat, GLenum unpackFormat,
-                                  GLenum unpackType, dom::Element* elem);
+public:
+    void TexStorage(const char* funcName, TexTarget target, GLsizei levels,
+                    GLenum sizedFormat, GLsizei width, GLsizei height, GLsizei depth);
+protected:
+    void TexImage(const char* funcName, TexImageTarget target, GLint level,
+                  GLenum internalFormat, GLint border, GLenum unpackFormat,
+                  GLenum unpackType, webgl::TexUnpackBlob* blob);
+    void TexSubImage(const char* funcName, TexImageTarget target, GLint level,
+                     GLint xOffset, GLint yOffset, GLint zOffset, GLenum unpackFormat,
+                     GLenum unpackType, webgl::TexUnpackBlob* blob);
+public:
+    void CompressedTexImage(const char* funcName, TexImageTarget target, GLint level,
+                            GLenum internalFormat, GLsizei width, GLsizei height,
+                            GLsizei depth, GLint border,
+                            const dom::ArrayBufferViewOrSharedArrayBufferView& view);
+    void CompressedTexSubImage(const char* funcName, TexImageTarget target, GLint level,
+                               GLint xOffset, GLint yOffset, GLint zOffset, GLsizei width,
+                               GLsizei height, GLsizei depth, GLenum sizedUnpackFormat,
+                               const dom::ArrayBufferViewOrSharedArrayBufferView& view);
+    void CopyTexImage2D(TexImageTarget target, GLint level, GLenum internalFormat,
+                        GLint x, GLint y, GLsizei width, GLsizei height, GLint border);
+    void CopyTexSubImage(const char* funcName, TexImageTarget target, GLint level,
+                         GLint xOffset, GLint yOffset, GLint zOffset, GLint x, GLint y,
+                         GLsizei width, GLsizei height);
 
-    // If jsArrayType is MaxTypedArrayViewType, it means no array.
-    void TexImage2D_base(TexImageTarget texImageTarget, GLint level,
-                         GLenum internalFormat, GLsizei width, GLsizei height,
-                         GLsizei srcStrideOrZero, GLint border, GLenum unpackFormat,
-                         GLenum unpackType, void* data, uint32_t byteLength,
-                         js::Scalar::Type jsArrayType, WebGLTexelFormat srcFormat,
-                         bool srcPremultiplied);
-    void TexSubImage2D_base(TexImageTarget texImageTarget, GLint level, GLint xOffset,
-                            GLint yOffset, GLsizei width, GLsizei height,
-                            GLsizei srcStrideOrZero, GLenum unpackFormat,
-                            GLenum unpackType, void* pixels, uint32_t byteLength,
-                            js::Scalar::Type jsArrayType, WebGLTexelFormat srcFormat,
-                            bool srcPremultiplied);
+    ////////////////////////////////////
 
-    bool ValidateTexStorage(TexTarget texTarget, GLsizei levels, GLenum internalFormat,
-                                      GLsizei width, GLsizei height, GLsizei depth,
-                                      const char* info);
-    bool ValidateSizedInternalFormat(GLenum internalFormat, const char* info);
-
+protected:
     void ClampLevelBaseAndMax();
 
     void PopulateMipChain(uint32_t baseLevel, uint32_t maxLevel);
 
-    ////////////////////////////////////
-
-protected:
     uint32_t MaxEffectiveMipmapLevel() const;
 
     static uint8_t FaceForTarget(TexImageTarget texImageTarget) {
         GLenum rawTexImageTarget = texImageTarget.get();
         switch (rawTexImageTarget) {
         case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X:
         case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
         case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
@@ -388,35 +320,33 @@ public:
     const ImageInfo& ImageInfoAt(TexImageTarget texImageTarget, GLint level) const {
         return const_cast<WebGLTexture*>(this)->ImageInfoAt(texImageTarget, level);
     }
 
     void SetImageInfoAt(TexImageTarget texImageTarget, GLint level,
                         const ImageInfo& val)
     {
         auto face = FaceForTarget(texImageTarget);
-        return SetImageInfoAtFace(face, level, val);
+        ImageInfo* target = &ImageInfoAt(texImageTarget, level);
+        SetImageInfo(target, val);
     }
 
     const ImageInfo& BaseImageInfo() const {
         if (mBaseMipmapLevel >= kMaxLevelCount)
             return ImageInfo::kUndefined;
 
         return ImageInfoAtFace(0, mBaseMipmapLevel);
     }
 
     size_t MemoryUsage() const;
 
+    bool InitializeImageData(const char* funcName, TexImageTarget target, uint32_t level);
 protected:
-    bool EnsureInitializedImageData(uint8_t face, uint32_t level);
-
-    bool EnsureInitializedImageData(TexImageTarget texImageTarget, uint32_t level) {
-        auto face = FaceForTarget(texImageTarget);
-        return EnsureInitializedImageData(face, level);
-    }
+    bool EnsureImageDataInitialized(const char* funcName, TexImageTarget target,
+                                    uint32_t level);
 
     bool CheckFloatTextureFilterParams() const {
         // Without OES_texture_float_linear, only NEAREST and
         // NEAREST_MIMPAMP_NEAREST are supported.
         return mMagFilter == LOCAL_GL_NEAREST &&
                (mMinFilter == LOCAL_GL_NEAREST ||
                 mMinFilter == LOCAL_GL_NEAREST_MIPMAP_NEAREST);
     }
@@ -445,26 +375,24 @@ public:
     bool IsComplete(const char** const out_reason) const;
 
     bool IsMipmapCubeComplete() const;
 
     bool IsCubeMap() const { return (mTarget == LOCAL_GL_TEXTURE_CUBE_MAP); }
 
     // Fake black status
 protected:
-    bool ResolveFakeBlackStatus();
+    bool ResolveFakeBlackStatus(const char* funcName);
 public:
-    bool ResolveFakeBlackStatus(WebGLTextureFakeBlackStatus* const out);
+    bool ResolveFakeBlackStatus(const char* funcName,
+                                WebGLTextureFakeBlackStatus* const out);
 
     WebGLTextureFakeBlackStatus FakeBlackStatus() const { return mFakeBlackStatus; }
 
-    void InvalidateFakeBlackCache() {
-        mContext->InvalidateFakeBlackCache();
-        mFakeBlackStatus = WebGLTextureFakeBlackStatus::Unknown;
-    }
+    void InvalidateFakeBlackCache();
 };
 
 inline TexImageTarget
 TexImageTargetForTargetAndFace(TexTarget target, uint8_t face)
 {
     switch (target.get()) {
     case LOCAL_GL_TEXTURE_2D:
     case LOCAL_GL_TEXTURE_3D:
@@ -473,11 +401,28 @@ TexImageTargetForTargetAndFace(TexTarget
     case LOCAL_GL_TEXTURE_CUBE_MAP:
         MOZ_ASSERT(face < 6);
         return LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X + face;
     default:
         MOZ_CRASH();
     }
 }
 
+already_AddRefed<mozilla::layers::Image>
+ImageFromVideo(dom::HTMLVideoElement* elem);
+
+GLenum
+DoTexImage(gl::GLContext* gl, TexImageTarget target, GLint level, GLenum internalFormat,
+           GLsizei width, GLsizei height, GLsizei depth, GLenum unpackFormat,
+           GLenum unpackType, const void* data);
+GLenum
+DoTexSubImage(gl::GLContext* gl, TexImageTarget target, GLint level, GLint xOffset,
+              GLint yOffset, GLint zOffset, GLsizei width, GLsizei height,
+              GLsizei depth, GLenum unpackFormat, GLenum unpackType, const void* data);
+GLenum
+DoCompressedTexSubImage(gl::GLContext* gl, TexImageTarget target, GLint level,
+                        GLint xOffset, GLint yOffset, GLint zOffset, GLsizei width,
+                        GLsizei height, GLsizei depth, GLenum sizedUnpackFormat,
+                        GLsizei dataSize, const void* data);
+
 } // namespace mozilla
 
 #endif // WEBGL_TEXTURE_H_
--- a/dom/canvas/WebGLTextureUpload.cpp
+++ b/dom/canvas/WebGLTextureUpload.cpp
@@ -6,26 +6,330 @@
 #include "WebGLTexture.h"
 
 #include <algorithm>
 #include "CanvasUtils.h"
 #include "GLBlitHelper.h"
 #include "GLContext.h"
 #include "mozilla/dom/HTMLVideoElement.h"
 #include "mozilla/dom/ImageData.h"
+#include "mozilla/gfx/SourceSurfaceRawData.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/Scoped.h"
 #include "ScopedGLHelpers.h"
+#include "TexUnpackBlob.h"
 #include "WebGLContext.h"
 #include "WebGLContextUtils.h"
 #include "WebGLFramebuffer.h"
 #include "WebGLTexelConversions.h"
 
 namespace mozilla {
 
+/* This file handles:
+ * TexStorage2D(texTarget, levels, internalFormat, width, height)
+ * TexStorage3D(texTarget, levels, intenralFormat, width, height, depth)
+ *
+ * TexImage2D(texImageTarget, level, internalFormat, width, height, border, unpackFormat,
+ *            unpackType, data)
+ * TexImage3D(texImageTarget, level, internalFormat, width, height, depth, border,
+ *            unpackFormat, unpackType, data)
+ * TexSubImage2D(texImageTarget, level, xOffset, yOffset, width, height, unpackFormat,
+ *               unpackType, data)
+ * TexSubImage3D(texImageTarget, level, xOffset, yOffset, zOffset, width, height, depth,
+ *               unpackFormat, unpackType, data)
+ *
+ * CompressedTexImage2D(texImageTarget, level, internalFormat, width, height, border,
+ *                      imageSize, data)
+ * CompressedTexImage3D(texImageTarget, level, internalFormat, width, height, depth,
+ *                      border, imageSize, data)
+ * CompressedTexSubImage2D(texImageTarget, level, xOffset, yOffset, width, height,
+ *                         sizedUnpackFormat, imageSize, data)
+ * CompressedTexSubImage3D(texImageTarget, level, xOffset, yOffset, zOffset, width,
+ *                         height, depth, sizedUnpackFormat, imageSize, data)
+ *
+ * CopyTexImage2D(texImageTarget, level, internalFormat, x, y, width, height, border)
+ * CopyTexImage3D - "Because the framebuffer is inhererntly two-dimensional, there is no
+ *                   CopyTexImage3D command."
+ * CopyTexSubImage2D(texImageTarget, level, xOffset, yOffset, x, y, width, height)
+ * CopyTexSubImage3D(texImageTarget, level, xOffset, yOffset, zOffset, x, y, width,
+ *                   height)
+ */
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// Some functions need an extra level of indirection, particularly for DOM Elements.
+
+already_AddRefed<mozilla::layers::Image>
+ImageFromVideo(dom::HTMLVideoElement* elem)
+{
+    uint16_t readyState;
+    if (NS_SUCCEEDED(elem->GetReadyState(&readyState)) &&
+        readyState < elem->HAVE_CURRENT_DATA)
+    {
+        // No frame inside, just return
+        return nullptr;
+    }
+
+    RefPtr<layers::ImageContainer> container = elem->GetImageContainer();
+    if (!container)
+        return nullptr;
+
+    nsAutoTArray<layers::ImageContainer::OwningImage, 4> currentImages;
+    container->GetCurrentImages(&currentImages);
+
+    if (!currentImages.Length())
+        return nullptr;
+
+    RefPtr<mozilla::layers::Image> ret = currentImages[0].mImage;
+    return ret.forget();
+}
+
+static already_AddRefed<gfx::DataSourceSurface>
+DataFromElement(dom::Element* elem, WebGLContext* webgl)
+{
+    const auto sfeRes = webgl->SurfaceFromElement(elem);
+
+    RefPtr<gfx::DataSourceSurface> data;
+    WebGLTexelFormat srcFormat;
+    nsresult rv = webgl->SurfaceFromElementResultToImageSurface(sfeRes, &data,
+                                                                &srcFormat);
+    if (NS_FAILED(rv) || !data)
+        return nullptr;
+
+    return data.forget();
+}
+
+////////////////////////////////////////
+// ArrayBufferView?
+
+static inline bool
+DoesJSTypeMatchUnpackType(GLenum unpackType, js::Scalar::Type jsType)
+{
+    switch (unpackType) {
+    case LOCAL_GL_BYTE:
+        return jsType == js::Scalar::Type::Int8;
+
+    case LOCAL_GL_UNSIGNED_BYTE:
+        return jsType == js::Scalar::Type::Uint8 ||
+               jsType == js::Scalar::Type::Uint8Clamped;
+
+    case LOCAL_GL_SHORT:
+        return jsType == js::Scalar::Type::Int16;
+
+    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:
+    case LOCAL_GL_HALF_FLOAT:
+    case LOCAL_GL_HALF_FLOAT_OES:
+        return jsType == js::Scalar::Type::Uint16;
+
+    case LOCAL_GL_INT:
+        return jsType == js::Scalar::Type::Int32;
+
+    case LOCAL_GL_UNSIGNED_INT:
+    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:
+    case LOCAL_GL_UNSIGNED_INT_24_8:
+        return jsType == js::Scalar::Type::Uint32;
+
+    case LOCAL_GL_FLOAT:
+        return jsType == js::Scalar::Type::Float32;
+
+    default:
+        return false;
+    }
+}
+
+static bool
+ValidateUnpackArrayType(WebGLContext* webgl, const char* funcName, GLenum unpackType,
+                        js::Scalar::Type jsType)
+{
+    if (DoesJSTypeMatchUnpackType(unpackType, jsType))
+        return true;
+
+    webgl->ErrorInvalidOperation("%s: `pixels` be compatible with unpack `type`.",
+                                 funcName);
+    return false;
+}
+
+static UniquePtr<webgl::TexUnpackBlob>
+UnpackBlobFromMaybeView(WebGLContext* webgl, const char* funcName, GLsizei width,
+                        GLsizei height, GLsizei depth, GLenum unpackType,
+                        const dom::Nullable<dom::ArrayBufferViewOrSharedArrayBufferView>& maybeView)
+{
+    size_t dataSize;
+    const void* data;
+    if (maybeView.IsNull()) {
+        dataSize = 0;
+        data = nullptr;
+    } else {
+        const auto& view = maybeView.Value();
+        void* mutData;
+        js::Scalar::Type jsType;
+        ComputeLengthAndData(view, &mutData, &dataSize, &jsType);
+        data = mutData;
+
+        if (!ValidateUnpackArrayType(webgl, funcName, unpackType, jsType))
+            return nullptr;
+    }
+
+    UniquePtr<webgl::TexUnpackBlob> ret;
+    ret.reset(new webgl::TexUnpackBytes(width, height, depth, dataSize, data));
+    return Move(ret);
+}
+
+void
+WebGLTexture::TexOrSubImage(bool isSubImage, const char* funcName, TexImageTarget target,
+                            GLint level, GLenum internalFormat, GLint xOffset,
+                            GLint yOffset, GLint zOffset, GLsizei width, GLsizei height,
+                            GLsizei depth, GLint border, GLenum unpackFormat,
+                            GLenum unpackType,
+                            const dom::Nullable<dom::ArrayBufferViewOrSharedArrayBufferView>& maybeView)
+{
+    UniquePtr<webgl::TexUnpackBlob> blob;
+    blob = UnpackBlobFromMaybeView(mContext, funcName, width, height, depth, unpackType,
+                                   maybeView);
+    if (!blob)
+        return;
+
+    TexOrSubImage(isSubImage, funcName, target, level, internalFormat, xOffset, yOffset,
+                  zOffset, border, unpackFormat, unpackType, blob.get());
+}
+
+////////////////////////////////////////
+// ImageData
+
+static UniquePtr<webgl::TexUnpackBlob>
+UnpackBlobFromImageData(WebGLContext* webgl, const char* funcName, GLenum unpackType,
+                        dom::ImageData* imageData, dom::Uint8ClampedArray* scopedArr)
+{
+    if (!imageData) {
+        // Spec says to generate an INVALID_VALUE error
+        webgl->ErrorInvalidValue("%s: null ImageData", funcName);
+        return nullptr;
+    }
+
+    DebugOnly<bool> inited = scopedArr->Init(imageData->GetDataObject());
+    MOZ_ASSERT(inited);
+
+    scopedArr->ComputeLengthAndData();
+    const DebugOnly<size_t> dataSize = scopedArr->Length();
+    const void* const data = scopedArr->Data();
+
+    const gfx::IntSize size(imageData->Width(), imageData->Height());
+    const size_t stride = size.width * 4;
+    const gfx::SurfaceFormat surfFormat = gfx::SurfaceFormat::R8G8B8A8;
+    const bool ownsData = false;
+
+    MOZ_ASSERT(dataSize == stride * size.height);
+
+    const RefPtr<gfx::SourceSurfaceRawData> surf = new gfx::SourceSurfaceRawData;
+
+    uint8_t* wrappableData = (uint8_t*)data;
+    surf->InitWrappingData(wrappableData, size, stride, surfFormat, ownsData);
+
+    // WhatWG "HTML Living Standard" (30 October 2015):
+    // "The getImageData(sx, sy, sw, sh) method [...] Pixels must be returned as
+    //  non-premultiplied alpha values."
+    const bool surfIsAlphaPremult = false;
+
+    UniquePtr<webgl::TexUnpackBlob> ret;
+    ret.reset(new webgl::TexUnpackSurface(surf, surfIsAlphaPremult));
+    return Move(ret);
+}
+
+void
+WebGLTexture::TexOrSubImage(bool isSubImage, const char* funcName, TexImageTarget target,
+                            GLint level, GLenum internalFormat, GLint xOffset,
+                            GLint yOffset, GLint zOffset, GLenum unpackFormat,
+                            GLenum unpackType, dom::ImageData* imageData)
+{
+    dom::Uint8ClampedArray scopedArr;
+
+    UniquePtr<webgl::TexUnpackBlob> blob;
+    blob = UnpackBlobFromImageData(mContext, funcName, unpackType, imageData, &scopedArr);
+    if (!blob)
+        return;
+
+    const GLint border = 0;
+    TexOrSubImage(isSubImage, funcName, target, level, internalFormat, xOffset, yOffset,
+                  zOffset, border, unpackFormat, unpackType, blob.get());
+}
+
+////////////////////////////////////////
+// dom::Element
+
+void
+WebGLTexture::TexOrSubImage(bool isSubImage, const char* funcName, TexImageTarget target,
+                            GLint level, GLenum internalFormat, GLint xOffset,
+                            GLint yOffset, GLint zOffset, GLenum unpackFormat,
+                            GLenum unpackType, dom::Element* elem,
+                            ErrorResult* const out_error)
+{
+    auto sfer = mContext->SurfaceFromElement(elem);
+    if (!sfer.mSourceSurface) {
+        mContext->ErrorInvalidOperation("%s: Failed to get data from DOM element.",
+                                        funcName);
+        return;
+    }
+
+    if (sfer.mIsWriteOnly) {
+        mContext->GenerateWarning("%s: Element is write-only.", funcName);
+        out_error->Throw(NS_ERROR_DOM_SECURITY_ERR);
+        return;
+    }
+
+    if (!sfer.mCORSUsed) {
+        auto& srcPrincipal = sfer.mPrincipal;
+        nsIPrincipal* dstPrincipal = mContext->GetCanvas()->NodePrincipal();
+
+        if (!dstPrincipal->Subsumes(srcPrincipal)) {
+            mContext->GenerateWarning("%s: Cross-origin elements require CORS.",
+                                      funcName);
+            out_error->Throw(NS_ERROR_DOM_SECURITY_ERR);
+            return;
+        }
+    }
+
+    auto& srcSurf = sfer.mSourceSurface;
+
+    UniquePtr<webgl::TexUnpackBlob> blob;
+    blob.reset(new webgl::TexUnpackSurface(srcSurf, sfer.mIsPremultiplied));
+
+    const GLint border = 0;
+    TexOrSubImage(isSubImage, funcName, target, level, internalFormat, xOffset, yOffset,
+                  zOffset, border, unpackFormat, unpackType, blob.get());
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////
+
+void
+WebGLTexture::TexOrSubImage(bool isSubImage, const char* funcName, TexImageTarget target,
+                            GLint level, GLenum internalFormat, GLint xOffset,
+                            GLint yOffset, GLint zOffset, GLint border,
+                            GLenum unpackFormat, GLenum unpackType,
+                            webgl::TexUnpackBlob* blob)
+{
+    if (isSubImage) {
+        TexSubImage(funcName, target, level, xOffset, yOffset, zOffset, unpackFormat,
+                    unpackType, blob);
+    } else {
+        TexImage(funcName, target, level, internalFormat, border, unpackFormat,
+                 unpackType, blob);
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////
+
+
+/* This needs to be done (but cached) per texture per draw call.
+
 // 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
 };
@@ -35,17 +339,17 @@ static const GLenum kLegacyLuminanceAlph
 };
 
 static void
 SetLegacyTextureSwizzle(gl::GLContext* gl, GLenum target, GLenum internalformat)
 {
     if (!gl->IsCoreProfile())
         return;
 
-    /* Only support swizzling on core profiles. */
+    // 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;
@@ -56,1472 +360,1405 @@ SetLegacyTextureSwizzle(gl::GLContext* g
         break;
 
     case LOCAL_GL_LUMINANCE_ALPHA:
         gl->fTexParameteriv(target, LOCAL_GL_TEXTURE_SWIZZLE_RGBA,
                             (GLint*) kLegacyLuminanceAlphaSwizzle);
         break;
     }
 }
+*/
 
+//////////////////////////////////////////////////////////////////////////////////////////
+// Utils
+
+static bool
+ValidateTexImage(WebGLContext* webgl, WebGLTexture* texture, const char* funcName,
+                 TexImageTarget target, GLint level,
+                 WebGLTexture::ImageInfo** const out_imageInfo)
+{
+    // Check level
+    if (level < 0) {
+        webgl->ErrorInvalidValue("%s: `level` must be >= 0.", funcName);
+        return false;
+    }
+
+    if (level > 31) {
+        // Right-shift is only defined for bits-1, so 31 for GLsizei.
+        webgl->ErrorInvalidValue("%s: `level` is too large.", funcName);
+        return false;
+    }
+
+    WebGLTexture::ImageInfo& imageInfo = texture->ImageInfoAt(target, level);
+
+    *out_imageInfo = &imageInfo;
+    return true;
+}
+
+
+// For *TexImage*
+bool
+WebGLTexture::ValidateTexImageSpecification(const char* funcName, TexImageTarget target,
+                                            GLint level, GLsizei width, GLsizei height,
+                                            GLsizei depth, GLint border,
+                                            WebGLTexture::ImageInfo** const out_imageInfo)
+{
+    if (mImmutable) {
+        mContext->ErrorInvalidOperation("%s: Specified texture is immutable.", funcName);
+        return false;
+    }
+
+    // Check border
+    if (border != 0) {
+        mContext->ErrorInvalidValue("%s: `border` must be 0.", funcName);
+        return false;
+    }
+
+    if (level < 0 || width < 0 || height < 0 || depth < 0) {
+        /* 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."
+         */
+        mContext->ErrorInvalidValue("%s: `level`/`width`/`height`/`depth` must be >= 0.",
+                                    funcName);
+        return false;
+    }
+
+    /* GLES 3.0.4, p133-134:
+     * GL_MAX_TEXTURE_SIZE is *not* the max allowed texture size. Rather, it is the
+     * max (width/height) size guaranteed not to generate an INVALID_VALUE for too-large
+     * dimensions. Sizes larger than GL_MAX_TEXTURE_SIZE *may or may not* result in an
+     * INVALID_VALUE, or possibly GL_OOM.
+     *
+     * However, we have needed to set our maximums lower in the past to prevent resource
+     * corruption. Therefore we have mImplMaxTextureSize, which is neither necessarily
+     * lower nor higher than MAX_TEXTURE_SIZE.
+     *
+     * Note that mImplMaxTextureSize must be >= than the advertized MAX_TEXTURE_SIZE.
+     * For simplicity, we advertize MAX_TEXTURE_SIZE as mImplMaxTextureSize.
+     */
+
+    uint32_t maxWidthHeight = 0;
+    uint32_t maxDepth = 0;
+
+    if (level <= 31) {
+        switch (target.get()) {
+        case LOCAL_GL_TEXTURE_2D:
+            maxWidthHeight = mContext->mImplMaxTextureSize >> level;
+            maxDepth = 1;
+            break;
+
+        case LOCAL_GL_TEXTURE_3D:
+            maxWidthHeight = mContext->mImplMax3DTextureSize >> level;
+            maxDepth = maxWidthHeight;
+            break;
+
+        case LOCAL_GL_TEXTURE_2D_ARRAY:
+            maxWidthHeight = mContext->mImplMaxTextureSize >> level;
+            // "The maximum number of layers for two-dimensional array textures (depth)
+            //  must be at least MAX_ARRAY_TEXTURE_LAYERS for all levels."
+            maxDepth = mContext->mImplMaxArrayTextureLayers;
+            break;
+
+        default: // cube maps
+            MOZ_ASSERT(IsCubeMap());
+            maxWidthHeight = mContext->mImplMaxCubeMapTextureSize >> level;
+            maxDepth = 1;
+            break;
+        }
+    }
+
+    if (uint32_t(width) > maxWidthHeight ||
+        uint32_t(height) > maxWidthHeight ||
+        uint32_t(depth) > maxDepth)
+    {
+        mContext->ErrorInvalidValue("%s: Requested size at this level is unsupported.",
+                                    funcName);
+        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+.
+         */
+        bool requirePOT = (!mContext->IsWebGL2() && level != 0);
+
+        if (requirePOT) {
+            if (!IsPowerOfTwo(uint32_t(width)) || !IsPowerOfTwo(uint32_t(height))) {
+                mContext->ErrorInvalidValue("%s: For level > 0, width and height must be"
+                                            " powers of two.",
+                                            funcName);
+                return false;
+            }
+        }
+    }
+
+    WebGLTexture::ImageInfo* imageInfo;
+    if (!ValidateTexImage(mContext, this, funcName, target, level, &imageInfo))
+        return false;
+
+    *out_imageInfo = imageInfo;
+    return true;
+}
+
+// For *TexSubImage*
 bool
-DoesTargetMatchDimensions(WebGLContext* webgl, TexImageTarget target, uint8_t funcDims,
-                          const char* funcName)
+WebGLTexture::ValidateTexImageSelection(const char* funcName, TexImageTarget target,
+                                        GLint level, GLint xOffset, GLint yOffset,
+                                        GLint zOffset, GLsizei width, GLsizei height,
+                                        GLsizei depth,
+                                        WebGLTexture::ImageInfo** const out_imageInfo)
+{
+    if (mImmutable) {
+        mContext->ErrorInvalidOperation("%s: Specified texture is immutable.", funcName);
+        return false;
+    }
+
+    // The conformance test wants bad arg checks before imageInfo checks.
+    if (xOffset < 0 || yOffset < 0 || zOffset < 0 ||
+        width < 0 || height < 0 || depth < 0)
+    {
+        mContext->ErrorInvalidValue("%s: Offsets and dimensions must be >=0.", funcName);
+        return false;
+    }
+
+    WebGLTexture::ImageInfo* imageInfo;
+    if (!ValidateTexImage(mContext, this, funcName, target, level, &imageInfo))
+        return false;
+
+    if (!imageInfo->IsDefined()) {
+        mContext->ErrorInvalidOperation("%s: The specified TexImage has not yet been"
+                                        " specified.",
+                                        funcName);
+        return false;
+    }
+
+    const auto totalX = CheckedUint32(xOffset) + width;
+    const auto totalY = CheckedUint32(yOffset) + height;
+    const auto totalZ = CheckedUint32(zOffset) + depth;
+
+    if (!totalX.isValid() || totalX.value() > imageInfo->mWidth ||
+        !totalY.isValid() || totalY.value() > imageInfo->mHeight ||
+        !totalZ.isValid() || totalZ.value() > imageInfo->mDepth)
+    {
+        mContext->ErrorInvalidValue("%s: Offset+size must be <= the size of the existing"
+                                    " specified image.",
+                                    funcName);
+        return false;
+    }
+
+    *out_imageInfo = imageInfo;
+    return true;
+}
+
+static bool
+ValidateCompressedTexUnpack(WebGLContext* webgl, const char* funcName, GLsizei width,
+                            GLsizei height, GLsizei depth,
+                            const webgl::FormatInfo* format, size_t dataSize)
+{
+    auto compression = format->compression;
+
+    auto bytesPerBlock = compression->bytesPerBlock;
+    auto blockWidth = compression->blockWidth;
+    auto blockHeight = compression->blockHeight;
+
+    CheckedUint32 widthInBlocks = (width % blockWidth) ? width / blockWidth + 1
+                                                       : width / blockWidth;
+    CheckedUint32 heightInBlocks = (height % blockHeight) ? height / blockHeight + 1
+                                                          : height / blockHeight;
+    CheckedUint32 blocksPerImage = widthInBlocks * heightInBlocks;
+    CheckedUint32 bytesPerImage = bytesPerBlock * blocksPerImage;
+    CheckedUint32 bytesNeeded = bytesPerImage * depth;
+
+    if (!bytesNeeded.isValid()) {
+        webgl->ErrorOutOfMemory("%s: Overflow while computing the needed buffer size.",
+                                funcName);
+        return false;
+    }
+
+    if (dataSize != bytesNeeded.value()) {
+        webgl->ErrorInvalidValue("%s: Provided buffer's size must match expected size."
+                                 " (needs %u, has %u)",
+                                 funcName, bytesNeeded.value(), dataSize);
+        return false;
+    }
+
+    return true;
+}
+
+static bool
+DoChannelsMatchForCopyTexImage(const webgl::FormatInfo* srcFormat,
+                               const webgl::FormatInfo* dstFormat)
 {
-    uint8_t targetDims;
+    // GLES 3.0.4 p140 Table 3.16 "Valid CopyTexImage source framebuffer/destination
+    // texture base internal format combinations."
+
+    switch (srcFormat->unsizedFormat) {
+    case webgl::UnsizedFormat::RGBA:
+        switch (dstFormat->unsizedFormat) {
+        case webgl::UnsizedFormat::A:
+        case webgl::UnsizedFormat::L:
+        case webgl::UnsizedFormat::LA:
+        case webgl::UnsizedFormat::R:
+        case webgl::UnsizedFormat::RG:
+        case webgl::UnsizedFormat::RGB:
+        case webgl::UnsizedFormat::RGBA:
+            return true;
+        default:
+            return false;
+        }
+
+    case webgl::UnsizedFormat::RGB:
+        switch (dstFormat->unsizedFormat) {
+        case webgl::UnsizedFormat::L:
+        case webgl::UnsizedFormat::R:
+        case webgl::UnsizedFormat::RG:
+        case webgl::UnsizedFormat::RGB:
+            return true;
+        default:
+            return false;
+        }
+
+    case webgl::UnsizedFormat::RG:
+        switch (dstFormat->unsizedFormat) {
+        case webgl::UnsizedFormat::L:
+        case webgl::UnsizedFormat::R:
+        case webgl::UnsizedFormat::RG:
+            return true;
+        default:
+            return false;
+        }
+
+    case webgl::UnsizedFormat::R:
+        switch (dstFormat->unsizedFormat) {
+        case webgl::UnsizedFormat::L:
+        case webgl::UnsizedFormat::R:
+            return true;
+        default:
+            return false;
+        }
+
+    default:
+        return false;
+    }
+}
+
+static bool
+EnsureImageDataInitializedForUpload(WebGLTexture* tex, const char* funcName,
+                                    TexImageTarget target, GLint level, GLint xOffset,
+                                    GLint yOffset, GLint zOffset, GLsizei width,
+                                    GLsizei height, GLsizei depth,
+                                    WebGLTexture::ImageInfo* imageInfo,
+                                    bool* const out_uploadWillInitialize)
+{
+    *out_uploadWillInitialize = false;
+
+    if (!imageInfo->IsDataInitialized()) {
+        const bool isFullUpload = (!xOffset && !yOffset && !zOffset &&
+                                   width == imageInfo->mWidth &&
+                                   height == imageInfo->mHeight &&
+                                   depth == imageInfo->mDepth);
+        if (isFullUpload) {
+            *out_uploadWillInitialize = true;
+        } else {
+            WebGLContext* webgl = tex->mContext;
+            webgl->GenerateWarning("%s: Texture has not been initialized prior to a"
+                                   " partial upload, forcing the browser to clear it."
+                                   " This may be slow.",
+                                   funcName);
+            if (!tex->InitializeImageData(funcName, target, level)) {
+                MOZ_ASSERT(false, "Unexpected failure to init image data.");
+                return false;
+            }
+        }
+    }
+
+    return true;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////
+// Actual calls
+
+static inline GLenum
+DoTexStorage(gl::GLContext* gl, TexTarget target, GLsizei levels, GLenum sizedFormat,
+             GLsizei width, GLsizei height, GLsizei depth)
+{
+    gl::GLContext::LocalErrorScope errorScope(*gl);
+
+    switch (target.get()) {
+    case LOCAL_GL_TEXTURE_2D:
+    case LOCAL_GL_TEXTURE_CUBE_MAP:
+        MOZ_ASSERT(depth == 1);
+        gl->fTexStorage2D(target.get(), levels, sizedFormat, width, height);
+        break;
+
+    case LOCAL_GL_TEXTURE_3D:
+    case LOCAL_GL_TEXTURE_2D_ARRAY:
+        gl->fTexStorage3D(target.get(), levels, sizedFormat, width, height, depth);
+        break;
+
+    default:
+        MOZ_CRASH("bad target");
+    }
+
+    return errorScope.GetError();
+}
+
+static bool
+Is3D(TexImageTarget target)
+{
     switch (target.get()) {
     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;
+        return false;
+
+    case LOCAL_GL_TEXTURE_3D:
+    case LOCAL_GL_TEXTURE_2D_ARRAY:
+        return true;
+
+    default:
+        MOZ_CRASH("bad target");
+    }
+}
+
+GLenum
+DoTexImage(gl::GLContext* gl, TexImageTarget target, GLint level, GLenum internalFormat,
+           GLsizei width, GLsizei height, GLsizei depth, GLenum unpackFormat,
+           GLenum unpackType, const void* data)
+{
+    const GLint border = 0;
+
+    gl::GLContext::LocalErrorScope errorScope(*gl);
+
+    if (Is3D(target)) {
+        gl->fTexImage3D(target.get(), level, internalFormat, width, height, depth,
+                        border, unpackFormat, unpackType, data);
+    } else {
+        MOZ_ASSERT(depth == 1);
+        gl->fTexImage2D(target.get(), level, internalFormat, width, height, border,
+                        unpackFormat, unpackType, data);
+    }
+
+    return errorScope.GetError();
+}
+
+GLenum
+DoTexSubImage(gl::GLContext* gl, TexImageTarget target, GLint level, GLint xOffset,
+              GLint yOffset, GLint zOffset, GLsizei width, GLsizei height, GLsizei depth,
+              GLenum unpackFormat, GLenum unpackType, const void* data)
+{
+    gl::GLContext::LocalErrorScope errorScope(*gl);
+
+    if (Is3D(target)) {
+        gl->fTexSubImage3D(target.get(), level, xOffset, yOffset, zOffset, width, height,
+                           depth, unpackFormat, unpackType, data);
+    } else {
+        MOZ_ASSERT(zOffset == 0);
+        MOZ_ASSERT(depth == 1);
+        gl->fTexSubImage2D(target.get(), level, xOffset, yOffset, width, height,
+                           unpackFormat, unpackType, data);
+    }
+
+    return errorScope.GetError();
+}
+
+static inline GLenum
+DoCompressedTexImage(gl::GLContext* gl, TexImageTarget target, GLint level,
+                     GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth,
+                     GLint border, GLsizei dataSize, const void* data)
+{
+    gl::GLContext::LocalErrorScope errorScope(*gl);
+
+    if (Is3D(target)) {
+        gl->fCompressedTexImage3D(target.get(), level, internalFormat, width, height,
+                                  depth, border, dataSize, data);
+    } else {
+        MOZ_ASSERT(depth == 1);
+        gl->fCompressedTexImage2D(target.get(), level, internalFormat, width, height,
+                                  border, dataSize, data);
+    }
+
+    return errorScope.GetError();
+}
+
+GLenum
+DoCompressedTexSubImage(gl::GLContext* gl, TexImageTarget target, GLint level,
+                        GLint xOffset, GLint yOffset, GLint zOffset, GLsizei width,
+                        GLsizei height, GLsizei depth, GLenum sizedUnpackFormat,
+                        GLsizei dataSize, const void* data)
+{
+    gl::GLContext::LocalErrorScope errorScope(*gl);
+
+    if (Is3D(target)) {
+        gl->fCompressedTexSubImage3D(target.get(), level, xOffset, yOffset, zOffset,
+                                     width, height, depth, sizedUnpackFormat, dataSize,
+                                     data);
+    } else {
+        MOZ_ASSERT(zOffset == 0);
+        MOZ_ASSERT(depth == 1);
+        gl->fCompressedTexSubImage2D(target.get(), level, xOffset, yOffset, width,
+                                     height, sizedUnpackFormat, dataSize, data);
+    }
+
+    return errorScope.GetError();
+}
+
+static inline GLenum
+DoCopyTexImage2D(gl::GLContext* gl, TexImageTarget target, GLint level,
+                 GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height,
+                 GLint border)
+{
+    gl::GLContext::LocalErrorScope errorScope(*gl);
+
+    MOZ_ASSERT(!Is3D(target));
+    gl->fCopyTexImage2D(target.get(), level, internalFormat, x, y, width, height,
+                        border);
+
+    return errorScope.GetError();
+}
+
+static inline GLenum
+DoCopyTexSubImage(gl::GLContext* gl, TexImageTarget target, GLint level, GLint xOffset,
+                  GLint yOffset, GLint zOffset, GLint x, GLint y, GLsizei width,
+                  GLsizei height)
+{
+    gl::GLContext::LocalErrorScope errorScope(*gl);
+
+    if (Is3D(target)) {
+        gl->fCopyTexSubImage3D(target.get(), level, xOffset, yOffset, zOffset, x, y,
+                               width, height);
+    } else {
+        MOZ_ASSERT(zOffset == 0);
+        gl->fCopyTexSubImage2D(target.get(), level, xOffset, yOffset, x, y, width,
+                               height);
+    }
+
+    return errorScope.GetError();
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////
+// Actual (mostly generic) function implementations
+
+static bool
+ValidateCompressedTexImageRestrictions(const char* funcName, WebGLContext* webgl,
+                                       TexImageTarget target, uint32_t level,
+                                       const webgl::FormatInfo* format, uint32_t width,
+                                       uint32_t height, uint32_t depth)
+{
+    const auto fnIsDimValid_S3TC = [level](uint32_t size, uint32_t blockSize) {
+        if (size % blockSize == 0)
+            return true;
+
+        if (level == 0)
+            return false;
+
+        return (size == 0 || size == 1 || size == 2);
+    };
+
+    bool supports2DArray = false;
+    switch (format->compression->family) {
+    case webgl::CompressionFamily::PVRTC:
+        if (!IsPowerOfTwo(width) || !IsPowerOfTwo(height)) {
+            webgl->ErrorInvalidValue("%s: %s requires power-of-two width and height.",
+                                     funcName, format->name);
+            return false;
+        }
+
         break;
 
+    // Block-aligned:
+    case webgl::CompressionFamily::ES3:
+        supports2DArray = true;
+        break;
+
+    case webgl::CompressionFamily::S3TC:
+        supports2DArray = true;
+
+        if (!fnIsDimValid_S3TC(width, format->compression->blockWidth) ||
+            !fnIsDimValid_S3TC(height, format->compression->blockHeight))
+        {
+            webgl->ErrorInvalidOperation("%s: %s requires that width and height are"
+                                         " block-aligned, or, if level>0, equal to 0, 1,"
+                                         " or 2.",
+                                         funcName, format->name);
+            return false;
+        }
+
+        break;
+
+    // Default: There are no restrictions on CompressedTexImage.
+    default: // ATC, ETC1
+        break;
+    }
+
+    bool targetSupported = true;
+    switch (target.get()) {
     case LOCAL_GL_TEXTURE_3D:
-        targetDims = 3;
+        targetSupported = false;
+        break;
+
+    case LOCAL_GL_TEXTURE_2D_ARRAY:
+        targetSupported = supports2DArray;
+        break;
+    }
+
+    if (!targetSupported) {
+        webgl->ErrorInvalidOperation("%s: This target is not supported by this %s.",
+                                     funcName, format->name);
+        return false;
+    }
+
+    return true;
+}
+
+void
+WebGLTexture::TexStorage(const char* funcName, TexTarget target, GLsizei levels,
+                         GLenum sizedFormat, GLsizei width, GLsizei height, GLsizei depth)
+{
+    // Check levels
+    if (levels < 1) {
+        mContext->ErrorInvalidValue("%s: `levels` must be >= 1.", funcName);
+        return;
+    }
+
+    if (levels > 31) {
+        // Right-shift is only defined for bits-1, so 31 for GLsizei.
+        // Besides,
+        mContext->ErrorInvalidValue("%s: `level` is too large.", funcName);
+        return;
+    }
+
+    const TexImageTarget testTarget = IsCubeMap() ? LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X
+                                                  : target.get();
+    const GLint testLevel = 0;
+    const GLint border = 0;
+
+    WebGLTexture::ImageInfo* testImageInfo;
+    if (!ValidateTexImageSpecification(funcName, testTarget, testLevel, width, height,
+                                       depth, border, &testImageInfo))
+    {
+        return;
+    }
+    MOZ_ASSERT(testImageInfo);
+    mozilla::unused << testImageInfo;
+
+    auto dstUsage = mContext->mFormatUsage->GetSizedTexUsage(sizedFormat);
+    if (!dstUsage) {
+        mContext->ErrorInvalidEnum("%s: Invalid internalformat: 0x%04x", funcName,
+                                   sizedFormat);
+        return;
+    }
+    auto dstFormat = dstUsage->format;
+
+    if (dstFormat->compression) {
+        if (!ValidateCompressedTexImageRestrictions(funcName, mContext, testTarget,
+                                                    testLevel, dstFormat, width, height,
+                                                    depth))
+        {
+            return;
+        }
+    }
+
+    ////////////////////////////////////
+    // Do the thing!
+
+    mContext->gl->MakeCurrent();
+
+    GLenum error = DoTexStorage(mContext->gl, target.get(), levels, sizedFormat, width,
+                                height, depth);
+
+    if (error == LOCAL_GL_OUT_OF_MEMORY) {
+        mContext->ErrorOutOfMemory("%s: Ran out of memory during texture allocation.",
+                                   funcName);
+        return;
+    }
+    if (error) {
+        MOZ_RELEASE_ASSERT(false, "We should have caught all other errors.");
+        mContext->ErrorInvalidOperation("%s: Unexpected error during texture allocation.",
+                                        funcName);
+        return;
+    }
+
+    ////////////////////////////////////
+    // Update our specification data.
+
+    const bool isDataInitialized = false;
+    const WebGLTexture::ImageInfo newInfo(dstUsage, width, height, depth,
+                                          isDataInitialized);
+    SetImageInfosAtLevel(0, newInfo);
+
+    PopulateMipChain(0, levels-1);
+}
+
+////////////////////////////////////////
+// Tex(Sub)Image
+
+static bool
+ValidateUnpackEnums(const webgl::PackingInfo& pi, WebGLContext* webgl,
+                    const char* funcName)
+{
+    switch (pi.format) {
+    case LOCAL_GL_RED:
+    case LOCAL_GL_RED_INTEGER:
+    case LOCAL_GL_DEPTH_COMPONENT:
+    case LOCAL_GL_LUMINANCE:
+    case LOCAL_GL_ALPHA:
+
+    case LOCAL_GL_RG:
+    case LOCAL_GL_RG_INTEGER:
+    case LOCAL_GL_LUMINANCE_ALPHA:
+    case LOCAL_GL_DEPTH_STENCIL:
+
+    case LOCAL_GL_RGB:
+    case LOCAL_GL_RGB_INTEGER:
+
+    case LOCAL_GL_RGBA:
+    case LOCAL_GL_RGBA_INTEGER:
         break;
 
     default:
-        MOZ_CRASH("Unhandled texImageTarget.");
+        webgl->ErrorInvalidEnum("%s: Invalid format enum: 0x%04x",
+                                funcName, pi.format);
+        return false;
     }
 
-    if (targetDims != funcDims) {
-        webgl->ErrorInvalidEnum("%s: `target` must match function dimensions.", funcName);
+    switch (pi.type) {
+    case LOCAL_GL_UNSIGNED_BYTE:
+    case LOCAL_GL_UNSIGNED_SHORT:
+    case LOCAL_GL_UNSIGNED_INT:
+    case LOCAL_GL_BYTE:
+    case LOCAL_GL_SHORT:
+    case LOCAL_GL_INT:
+    case LOCAL_GL_FLOAT:
+    case LOCAL_GL_HALF_FLOAT:
+    case LOCAL_GL_HALF_FLOAT_OES:
+
+    case LOCAL_GL_UNSIGNED_INT_24_8:
+    case LOCAL_GL_FLOAT_32_UNSIGNED_INT_24_8_REV:
+
+    case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
+    case LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV:
+    case LOCAL_GL_UNSIGNED_INT_5_9_9_9_REV:
+
+    case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
+    case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
+    case LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV:
+        break;
+
+    default:
+        webgl->ErrorInvalidEnum("%s: Invalid type enum: 0x%04x",
+                                funcName, pi.type);
         return false;
     }
 
     return true;
 }
 
 void
-WebGLTexture::SpecifyTexStorage(GLsizei levels, TexInternalFormat internalFormat,
-                                GLsizei width, GLsizei height, GLsizei depth)
+WebGLTexture::TexImage(const char* funcName, TexImageTarget target, GLint level,
+                       GLenum internalFormat, GLint border, GLenum unpackFormat,
+                       GLenum unpackType, webgl::TexUnpackBlob* blob)
 {
-    mImmutable = true;
-    mImmutableLevelCount = levels;
-    ClampLevelBaseAndMax();
-
-    // GLES 3.0.4, p136:
-    // "* Any existing levels that are not replaced are reset to their
-    //    initial state."
-    for (auto& cur : mImageInfoArr) {
-        cur.Clear();
-    }
-
-    const bool isDataInitialized = false;
-    const ImageInfo baseImageInfo(internalFormat, width, height, depth,
-                                  isDataInitialized);
-
-    SetImageInfoAtFace(0, 0, baseImageInfo);
-    PopulateMipChain(0, mImmutableLevelCount);
-}
+    ////////////////////////////////////
+    // Get dest info
 
-void
-WebGLTexture::CompressedTexImage2D(TexImageTarget texImageTarget,
-                                   GLint level,
-                                   GLenum internalFormat,
-                                   GLsizei width, GLsizei height, GLint border,
-                                   const dom::ArrayBufferViewOrSharedArrayBufferView& view)
-{
-    const WebGLTexImageFunc func = WebGLTexImageFunc::CompTexImage;
-    const WebGLTexDimensions dims = WebGLTexDimensions::Tex2D;
-
-    const char funcName[] = "compressedTexImage2D";
-    if (!DoesTargetMatchDimensions(mContext, texImageTarget, 2, funcName))
-        return;
-
-    if (!mContext->ValidateTexImage(texImageTarget.get(), level, internalFormat,
-                          0, 0, 0, width, height, 0,
-                          border, LOCAL_GL_NONE,
-                          LOCAL_GL_NONE,
-                          func, dims))
-    {
-        return;
-    }
-
-    size_t byteLength;
-    void* data;
-    js::Scalar::Type dataType;
-    ComputeLengthAndData(view, &data, &byteLength, &dataType);
-
-    if (!mContext->ValidateCompTexImageDataSize(level, internalFormat, width, height, byteLength, func, dims)) {
-        return;
-    }
-
-    if (!mContext->ValidateCompTexImageSize(level, internalFormat, 0, 0, width, height, width, height, func, dims))
+    WebGLTexture::ImageInfo* imageInfo;
+    if (!ValidateTexImageSpecification(funcName, target, level, blob->mWidth,
+                                       blob->mHeight, blob->mDepth, border, &imageInfo))
     {
         return;
     }
-
-    if (mImmutable) {
-        return mContext->ErrorInvalidOperation(
-            "compressedTexImage2D: disallowed because the texture bound to "
-            "this target has already been made immutable by texStorage2D");
-    }
-
-    mContext->MakeContextCurrent();
-    gl::GLContext* gl = mContext->gl;
-    gl->fCompressedTexImage2D(texImageTarget.get(), level, internalFormat, width, height, border, byteLength, data);
-
-    const uint32_t depth = 1;
-    const bool isDataInitialized = true;
-    const ImageInfo imageInfo(internalFormat, width, height, depth, isDataInitialized);
+    MOZ_ASSERT(imageInfo);
 
-    SetImageInfoAt(texImageTarget, level, imageInfo);
-}
-
-void
-WebGLTexture::CompressedTexSubImage2D(TexImageTarget texImageTarget, GLint level, GLint xOffset,
-                                      GLint yOffset, GLsizei width, GLsizei height,
-                                      GLenum internalFormat,
-                                      const dom::ArrayBufferViewOrSharedArrayBufferView& view)
-{
-    const WebGLTexImageFunc func = WebGLTexImageFunc::CompTexSubImage;
-    const WebGLTexDimensions dims = WebGLTexDimensions::Tex2D;
-
-    const char funcName[] = "compressedTexSubImage2D";
-    if (!DoesTargetMatchDimensions(mContext, texImageTarget, 2, funcName))
+    const webgl::PackingInfo srcPacking = { unpackFormat, unpackType };
+    if (!ValidateUnpackEnums(srcPacking, mContext, funcName))
         return;
 
-    if (!mContext->ValidateTexImage(texImageTarget.get(),
-                          level, internalFormat,
-                          xOffset, yOffset, 0,
-                          width, height, 0,
-                          0, LOCAL_GL_NONE, LOCAL_GL_NONE,
-                          func, dims))
-    {
-        return;
+    auto dstUsage = mContext->mFormatUsage->GetSizedTexUsage(internalFormat);
+    if (!dstUsage) {
+        if (internalFormat != unpackFormat) {
+            mContext->ErrorInvalidOperation("%s: Unsized internalFormat must match"
+                                            " unpack format.",
+                                            funcName);
+            return;
+        }
+
+        dstUsage = mContext->mFormatUsage->GetUnsizedTexUsage(srcPacking);
     }
 
-    ImageInfo& levelInfo = ImageInfoAt(texImageTarget, level);
-
-    if (internalFormat != levelInfo.mFormat) {
-        return mContext->ErrorInvalidOperation("compressedTexImage2D: internalFormat does not match the existing image");
-    }
-
-    size_t byteLength;
-    void* data;
-    js::Scalar::Type dataType;
-    ComputeLengthAndData(view, &data, &byteLength, &dataType);
-
-    if (!mContext->ValidateCompTexImageDataSize(level, internalFormat, width, height, byteLength, func, dims))
-        return;
-
-    if (!mContext->ValidateCompTexImageSize(level, internalFormat,
-                                  xOffset, yOffset,
-                                  width, height,
-                                  levelInfo.mWidth, levelInfo.mHeight,
-                                  func, dims))
-    {
+    if (!dstUsage) {
+        mContext->ErrorInvalidOperation("%s: Invalid internalformat/format/type:"
+                                        " 0x%04x/0x%04x/0x%04x",
+                                        funcName, internalFormat, unpackFormat,
+                                        unpackType);
         return;
     }
 
-    if (!levelInfo.IsDataInitialized()) {
-        bool coversWholeImage = xOffset == 0 &&
-                                yOffset == 0 &&
-                                uint32_t(width) == levelInfo.mWidth &&
-                                uint32_t(height) == levelInfo.mHeight;
-        if (coversWholeImage) {
-            levelInfo.SetIsDataInitialized(true, this);
-        } else {
-            if (!EnsureInitializedImageData(texImageTarget, level))
-                return;
-        }
-    }
-
-    mContext->MakeContextCurrent();
-    gl::GLContext* gl = mContext->gl;
-    gl->fCompressedTexSubImage2D(texImageTarget.get(), level, xOffset, yOffset, width, height, internalFormat, byteLength, data);
-}
-
-void
-WebGLTexture::CopyTexSubImage2D_base(TexImageTarget texImageTarget, GLint level,
-                                     GLenum rawInternalFormat,
-                                     GLint xOffset, GLint yOffset, GLint x,
-                                     GLint y, GLsizei width, GLsizei height, GLint border,
-                                     bool sub)
-{
-    WebGLTexImageFunc func = sub
-                             ? WebGLTexImageFunc::CopyTexSubImage
-                             : WebGLTexImageFunc::CopyTexImage;
-    WebGLTexDimensions dims = WebGLTexDimensions::Tex2D;
-    const char* info = InfoFrom(func, dims);
-
-    // TODO: This changes with color_buffer_float. Reassess when the
-    // patch lands.
-    if (!mContext->ValidateTexImage(texImageTarget, level, rawInternalFormat,
-                          xOffset, yOffset, 0,
-                          width, height, 0,
-                          border,
-                          LOCAL_GL_NONE, LOCAL_GL_NONE,
-                          func, dims))
-    {
-        return;
-    }
-
-    TexInternalFormat internalFormat = rawInternalFormat;
-
-    if (!mContext->mBoundReadFramebuffer)
-        mContext->ClearBackbufferIfNeeded();
-
-    mContext->MakeContextCurrent();
-    gl::GLContext* gl = mContext->gl;
-
-    if (mImmutable) {
-        if (!sub) {
-            return mContext->ErrorInvalidOperation("copyTexImage2D: disallowed because the texture bound to this target has already been made immutable by texStorage2D");
-        }
-    }
-
-    TexInternalFormat srcFormat;
-    uint32_t srcWidth;
-    uint32_t srcHeight;
-    if (!mContext->ValidateCurFBForRead("readPixels", &srcFormat, &srcWidth, &srcHeight))
-        return;
-
-    if (!mContext->ValidateCopyTexImage(srcFormat, internalFormat, func, dims))
+    ////////////////////////////////////
+    // Get source info
+    const bool isFunc3D = Is3D(target);
+    if (!blob->ValidateUnpack(mContext, funcName, isFunc3D, srcPacking))
         return;
 
-    TexFormat intermediateFormat;
-    TexType intermediateType;
-    CopyTexImageIntermediateFormatAndType(srcFormat, &intermediateFormat,
-                                          &intermediateType);
-
-    TexInternalFormat destEffectiveFormat =
-        EffectiveInternalFormatFromUnsizedInternalFormatAndType(internalFormat, intermediateType);
-
-    // this should never fail, validation happened earlier.
-    MOZ_ASSERT(destEffectiveFormat != LOCAL_GL_NONE);
-
-    const bool widthOrHeightIsZero = (width == 0 || height == 0);
-    if (gl->WorkAroundDriverBugs() &&
-        sub && widthOrHeightIsZero)
-    {
-        // NV driver on Linux complains that CopyTexSubImage2D(level=0,
-        // xOffset=0, yOffset=2, x=0, y=0, width=0, height=0) from a 300x150 FB
-        // to a 0x2 texture. This a useless thing to do, but technically legal.
-        // NV331.38 generates INVALID_VALUE.
-        return mContext->DummyFramebufferOperation(info);
-    }
-
-    // check if the memory size of this texture may change with this call
-    bool sizeMayChange = !sub;
-    const WebGLTexture::ImageInfo& imageInfo = ImageInfoAt(texImageTarget, level);
-    if (!sub && imageInfo.IsDefined()) {
-        const uint32_t depth = 1;
-        sizeMayChange = uint32_t(width) != imageInfo.mWidth ||
-                        uint32_t(height) != imageInfo.mHeight ||
-                        depth != imageInfo.mDepth ||
-                        destEffectiveFormat != imageInfo.mFormat;
-    }
-
-    if (sizeMayChange)
-        mContext->GetAndFlushUnderlyingGLErrors();
-
-    if (CanvasUtils::CheckSaneSubrectSize(x, y, width, height, srcWidth, srcHeight)) {
-        if (sub)
-            gl->fCopyTexSubImage2D(texImageTarget.get(), level, xOffset, yOffset, x, y, width, height);
-        else
-            gl->fCopyTexImage2D(texImageTarget.get(), level, internalFormat.get(), x, y, width, height, 0);
-    } else {
-        // the rect doesn't fit in the framebuffer
-
-        // first, we initialize the texture as black
-        if (!sub) {
-            mContext->GenerateWarning("%s: Source rect reaches outside the bounds of the"
-                                      " source framebuffer. WebGL requires clearing this"
-                                      " out-of-bounds data to zeros, which is slow.",
-                                      info);
-
-            // We use CopyTexImage to initialize to ensure we get the right internal
-            // format in the driver.
-            // We don't need to pass x and y, since we only need to width and height to be
-            // right for the TexImage. In cases where x or y is 'tricky' (INT32_MIN), the
-            // driver may have issues. (Seen on Win32 Try runs)
-            gl->fCopyTexImage2D(texImageTarget.get(), level, internalFormat.get(), 0, 0,
-                                width, height, 0);
-
-            // In GLES, read pixels outside the FB bounds are undefined, so we'll need to
-            // clear them outselves.
-            const uint32_t depth = 1;
-            const bool isDataInitialized = false;
-            const ImageInfo imageInfo(destEffectiveFormat, width, height, depth,
-                                      isDataInitialized);
+    ////////////////////////////////////
+    // Check that source and dest info are compatible
+    auto dstFormat = dstUsage->format;
 
-            SetImageInfoAt(texImageTarget, level, imageInfo);
-
-            if (!EnsureInitializedImageData(texImageTarget, level))
-                return;
-        }
-
-        // if we are completely outside of the framebuffer, we can exit now with our black texture
-        if (   x >= int32_t(srcWidth)
-            || x+width <= 0
-            || y >= int32_t(srcHeight)
-            || y+height <= 0)
+    if (!mContext->IsWebGL2() && dstFormat->hasDepth) {
+        if (target != LOCAL_GL_TEXTURE_2D ||
+            blob->mHasData ||
+            level != 0)
         {
-            // we are completely outside of range, can exit now with buffer filled with zeros
-            return mContext->DummyFramebufferOperation(info);
-        }
-
-        GLint trimmedXOffset = xOffset;
-        GLint trimmedYOffset = yOffset;
-        GLint trimmedX = x;
-        GLint trimmedY = y;
-        GLsizei trimmedWidth = width;
-        GLsizei trimmedHeight = height;
-
-        if (x < 0) {
-            GLint diff = 0 - x;
-            MOZ_ASSERT(diff > 0);
-            trimmedX += diff;
-            trimmedXOffset += diff;
-            trimmedWidth -= diff;
-        }
-
-        if (y < 0) {
-            GLint diff = 0 - y;
-            MOZ_ASSERT(diff > 0);
-            trimmedY += diff;
-            trimmedYOffset += diff;
-            trimmedHeight -= diff;
-        }
-
-        if (x + width > GLint(srcWidth)) {
-            GLint diff = x + width - GLint(srcWidth);
-            MOZ_ASSERT(diff > 0);
-            trimmedWidth -= diff;
-        }
-
-        if (y + height > GLint(srcHeight)) {
-            GLint diff = y + height - GLint(srcHeight);
-            MOZ_ASSERT(diff > 0);
-            trimmedHeight -= diff;
-        }
-
-        MOZ_ASSERT(trimmedX >= 0);
-        MOZ_ASSERT(trimmedY >= 0);
-        MOZ_ASSERT(trimmedWidth >= 0);
-        MOZ_ASSERT(trimmedHeight >= 0);
-
-        gl->fCopyTexSubImage2D(texImageTarget.get(), level, trimmedXOffset,
-                               trimmedYOffset, trimmedX, trimmedY, trimmedWidth,
-                               trimmedHeight);
-    }
-
-    if (sizeMayChange) {
-        GLenum error = mContext->GetAndFlushUnderlyingGLErrors();
-        if (error) {
-            mContext->GenerateWarning("copyTexImage2D generated error %s", mContext->ErrorName(error));
+            mContext->ErrorInvalidOperation("%s: With format %s, this function may only"
+                                            " be called with target=TEXTURE_2D,"
+                                            " data=null, and level=0.",
+                                            funcName, dstFormat->name);
             return;
         }
     }
 
-    if (!sub) {
-        const uint32_t depth = 1;
-        const bool isDataInitialized = true;
-        const ImageInfo imageInfo(destEffectiveFormat, width, height, depth,
-                                  isDataInitialized);
+    const webgl::DriverUnpackInfo* driverUnpackInfo;
+    if (!dstUsage->IsUnpackValid(srcPacking, &driverUnpackInfo)) {
+        mContext->ErrorInvalidOperation("%s: Mismatched internalFormat and format/type:"
+                                        " 0x%04x and 0x%04x/0x%04x",
+                                        funcName, internalFormat, unpackFormat,
+                                        unpackType);
+        return;
+    }
 
-        SetImageInfoAt(texImageTarget, level, imageInfo);
-    }
-}
+    ////////////////////////////////////
+    // Do the thing!
+
+    mContext->gl->MakeCurrent();
+
+    // It's tempting to do allocation first, and TexSubImage second, but this is generally
+    // slower.
+
+    const bool isSubImage = false;
+    const GLint xOffset = 0;
+    const GLint yOffset = 0;
+    const GLint zOffset = 0;
 
-void
-WebGLTexture::CopyTexImage2D(TexImageTarget texImageTarget,
-                             GLint level,
-                             GLenum internalFormat,
-                             GLint x,
-                             GLint y,
-                             GLsizei width,
-                             GLsizei height,
-                             GLint border)
-{
-    const char funcName[] = "copyTexImage2D";
-    if (!DoesTargetMatchDimensions(mContext, texImageTarget, 2, funcName))
+    GLenum glError;
+    blob->TexOrSubImage(isSubImage, funcName, this, target, level, driverUnpackInfo,
+                        xOffset, yOffset, zOffset, &glError);
+
+    if (glError == LOCAL_GL_OUT_OF_MEMORY) {
+        mContext->ErrorOutOfMemory("%s: Driver ran out of memory during upload.",
+                                   funcName);
         return;
+    }
 
-    CopyTexSubImage2D_base(texImageTarget, level, internalFormat, 0, 0, x, y, width,
-                           height, border, false);
+    if (glError) {
+        mContext->ErrorInvalidOperation("%s: Unexpected error during upload: 0x04x",
+                                        funcName, glError);
+        MOZ_ASSERT(false, "Unexpected GL error.");
+        return;
+    }
+
+    ////////////////////////////////////
+    // Update our specification data.
+
+    const ImageInfo newImageInfo(dstUsage, blob->mWidth, blob->mHeight, blob->mDepth,
+                                 blob->mHasData);
+    SetImageInfo(imageInfo, newImageInfo);
 }
 
 void
-WebGLTexture::CopyTexSubImage2D(TexImageTarget texImageTarget,
-                                GLint level,
-                                GLint xOffset,
-                                GLint yOffset,
-                                GLint x,
-                                GLint y,
-                                GLsizei width,
-                                GLsizei height)
+WebGLTexture::TexSubImage(const char* funcName, TexImageTarget target, GLint level,
+                          GLint xOffset, GLint yOffset, GLint zOffset,
+                          GLenum unpackFormat, GLenum unpackType,
+                          webgl::TexUnpackBlob* blob)
 {
-    switch (texImageTarget.get()) {
-    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:
-        break;
-    default:
-        return mContext->ErrorInvalidEnumInfo("copyTexSubImage2D: target", texImageTarget.get());
-    }
-
-    if (level < 0)
-        return mContext->ErrorInvalidValue("copyTexSubImage2D: level may not be negative");
-
-    GLsizei maxTextureSize = mContext->MaxTextureSizeForTarget(TexImageTargetToTexTarget(texImageTarget));
-    if (!(maxTextureSize >> level))
-        return mContext->ErrorInvalidValue("copyTexSubImage2D: 2^level exceeds maximum texture size");
+    ////////////////////////////////////
+    // Get dest info
 
-    if (width < 0 || height < 0)
-        return mContext->ErrorInvalidValue("copyTexSubImage2D: width and height may not be negative");
-
-    if (xOffset < 0 || yOffset < 0)
-        return mContext->ErrorInvalidValue("copyTexSubImage2D: xOffset and yOffset may not be negative");
-
-    WebGLTexture::ImageInfo& imageInfo = ImageInfoAt(texImageTarget, level);
-    if (!imageInfo.IsDefined())
-        return mContext->ErrorInvalidOperation("copyTexSubImage2D: no texture image previously defined for this level and face");
-
-    GLsizei texWidth = imageInfo.mWidth;
-    GLsizei texHeight = imageInfo.mHeight;
-
-    if (xOffset + width > texWidth || xOffset + width < 0)
-      return mContext->ErrorInvalidValue("copyTexSubImage2D: xOffset+width is too large");
+    WebGLTexture::ImageInfo* imageInfo;
+    if (!ValidateTexImageSelection(funcName, target, level, xOffset, yOffset, zOffset,
+                                   blob->mWidth, blob->mHeight, blob->mDepth, &imageInfo))
+    {
+        return;
+    }
+    MOZ_ASSERT(imageInfo);
 
-    if (yOffset + height > texHeight || yOffset + height < 0)
-      return mContext->ErrorInvalidValue("copyTexSubImage2D: yOffset+height is too large");
-
-    if (!mContext->mBoundReadFramebuffer)
-        mContext->ClearBackbufferIfNeeded();
+    auto dstUsage = imageInfo->mFormat;
+    auto dstFormat = dstUsage->format;
 
-    if (!imageInfo.IsDataInitialized()) {
-        bool coversWholeImage = xOffset == 0 &&
-                                yOffset == 0 &&
-                                width == texWidth &&
-                                height == texHeight;
-        if (coversWholeImage) {
-            imageInfo.SetIsDataInitialized(true, this);
-        } else {
-            if (!EnsureInitializedImageData(texImageTarget, level))
-                return;
-        }
+    if (dstFormat->compression) {
+        mContext->ErrorInvalidEnum("%s: Specified TexImage must not be compressed.",
+                                   funcName);
+        return;
     }
 
-    TexInternalFormat unsizedInternalFormat;
-    TexType type;
-    UnsizedInternalFormatAndTypeFromEffectiveInternalFormat(imageInfo.mFormat,
-                                                            &unsizedInternalFormat,
-                                                            &type);
-    const GLint border = 0;
-    CopyTexSubImage2D_base(texImageTarget, level, unsizedInternalFormat.get(), xOffset,
-                           yOffset, x, y, width, height, border, true);
-}
-
-
-GLenum
-WebGLTexture::CheckedTexImage2D(TexImageTarget texImageTarget,
-                                       GLint level,
-                                       TexInternalFormat internalFormat,
-                                       GLsizei width,
-                                       GLsizei height,
-                                       GLint border,
-                                       TexFormat unpackFormat,
-                                       TexType unpackType,
-                                       const GLvoid* data)
-{
-    TexInternalFormat effectiveInternalFormat =
-        EffectiveInternalFormatFromInternalFormatAndType(internalFormat, unpackType);
-    bool sizeMayChange = true;
-
-    const WebGLTexture::ImageInfo& imageInfo = ImageInfoAt(texImageTarget, level);
-    if (imageInfo.IsDefined()) {
-        sizeMayChange = uint32_t(width) != imageInfo.mWidth ||
-                        uint32_t(height) != imageInfo.mHeight ||
-                        effectiveInternalFormat != imageInfo.mFormat;
+    if (!mContext->IsWebGL2() && dstFormat->hasDepth) {
+        mContext->ErrorInvalidOperation("%s: Function may not be called on a texture of"
+                                        " format %s.",
+                                        funcName, dstFormat->name);
+        return;
     }
 
-    gl::GLContext* gl = mContext->gl;
+    ////////////////////////////////////
+    // Get source info
+
+    const webgl::PackingInfo srcPacking = { unpackFormat, unpackType };
+    if (!ValidateUnpackEnums(srcPacking, mContext, funcName))
+        return;
 
-    // Convert to format and type required by OpenGL 'driver'.
-    GLenum driverType = LOCAL_GL_NONE;
-    GLenum driverInternalFormat = LOCAL_GL_NONE;
-    GLenum driverFormat = LOCAL_GL_NONE;
-    DriverFormatsFromEffectiveInternalFormat(gl,
-                                             effectiveInternalFormat,
-                                             &driverInternalFormat,
-                                             &driverFormat,
-                                             &driverType);
-
-    if (sizeMayChange) {
-        mContext->GetAndFlushUnderlyingGLErrors();
-    }
-
-    if (driverFormat == LOCAL_GL_ALPHA) {
-        MOZ_ASSERT(true);
+    const webgl::DriverUnpackInfo* driverUnpackInfo;
+    if (!dstUsage->IsUnpackValid(srcPacking, &driverUnpackInfo)) {
+        mContext->ErrorInvalidOperation("%s: Mismatched internalFormat and format/type:"
+                                        " %s and 0x%04x/0x%04x",
+                                        funcName, dstFormat->name, unpackFormat,
+                                        unpackType);
+        return;
     }
 
-    gl->fTexImage2D(texImageTarget.get(), level, driverInternalFormat, width, height, border, driverFormat, driverType, data);
-
-    SetLegacyTextureSwizzle(gl, texImageTarget.get(), internalFormat.get());
+    const bool isFunc3D = Is3D(target);
+    if (!blob->ValidateUnpack(mContext, funcName, isFunc3D, srcPacking))
+        return;
 
-    GLenum error = LOCAL_GL_NO_ERROR;
-    if (sizeMayChange) {
-        error = mContext->GetAndFlushUnderlyingGLErrors();
-    }
-
-    return error;
-}
+    ////////////////////////////////////
+    // Do the thing!
 
-void
-WebGLTexture::TexImage2D_base(TexImageTarget texImageTarget, GLint level,
-                              GLenum internalFormat,
-                              GLsizei width, GLsizei height, GLsizei srcStrideOrZero,
-                              GLint border,
-                              GLenum unpackFormat,
-                              GLenum unpackType,
-                              void* data, uint32_t byteLength,
-                              js::Scalar::Type jsArrayType,
-                              WebGLTexelFormat srcFormat, bool srcPremultiplied)
-{
-    const WebGLTexImageFunc func = WebGLTexImageFunc::TexImage;
-    const WebGLTexDimensions dims = WebGLTexDimensions::Tex2D;
+    mContext->gl->MakeCurrent();
 
-    if (unpackType == LOCAL_GL_HALF_FLOAT_OES) {
-        unpackType = LOCAL_GL_HALF_FLOAT;
-    }
-
-    if (!mContext->ValidateTexImage(texImageTarget, level, internalFormat,
-                          0, 0, 0,
-                          width, height, 0,
-                          border, unpackFormat, unpackType, func, dims))
+    bool uploadWillInitialize;
+    if (!EnsureImageDataInitializedForUpload(this, funcName, target, level, xOffset,
+                                             yOffset, zOffset, blob->mWidth,
+                                             blob->mHeight, blob->mDepth, imageInfo,
+                                             &uploadWillInitialize))
     {
         return;
     }
 
-    const bool isDepthTexture = unpackFormat == LOCAL_GL_DEPTH_COMPONENT ||
-                                unpackFormat == LOCAL_GL_DEPTH_STENCIL;
-
-    if (isDepthTexture && !mContext->IsWebGL2()) {
-        if (data != nullptr || level != 0)
-            return mContext->ErrorInvalidOperation("texImage2D: "
-                                         "with format of DEPTH_COMPONENT or DEPTH_STENCIL, "
-                                         "data must be nullptr, "
-                                         "level must be zero");
-    }
-
-    if (!mContext->ValidateTexInputData(unpackType, jsArrayType, func, dims))
-        return;
-
-    TexInternalFormat effectiveInternalFormat =
-        EffectiveInternalFormatFromInternalFormatAndType(internalFormat, unpackType);
+    const bool isSubImage = true;
 
-    if (effectiveInternalFormat == LOCAL_GL_NONE) {
-        return mContext->ErrorInvalidOperation("texImage2D: bad combination of internalFormat and type");
-    }
+    GLenum glError;
+    blob->TexOrSubImage(isSubImage, funcName, this, target, level, driverUnpackInfo,
+                        xOffset, yOffset, zOffset, &glError);
 
-    size_t srcTexelSize = size_t(-1);
-    if (srcFormat == WebGLTexelFormat::Auto) {
-        // we need to find the exact sized format of the source data. Slightly abusing
-        // EffectiveInternalFormatFromInternalFormatAndType for that purpose. Really, an unsized source format
-        // is the same thing as an unsized internalFormat.
-        TexInternalFormat effectiveSourceFormat =
-            EffectiveInternalFormatFromInternalFormatAndType(unpackFormat, unpackType);
-        MOZ_ASSERT(effectiveSourceFormat != LOCAL_GL_NONE); // should have validated format/type combo earlier
-        const size_t srcbitsPerTexel = GetBitsPerTexel(effectiveSourceFormat);
-        MOZ_ASSERT((srcbitsPerTexel % 8) == 0); // should not have compressed formats here.
-        srcTexelSize = srcbitsPerTexel / 8;
-    } else {
-        srcTexelSize = WebGLTexelConversions::TexelBytesForFormat(srcFormat);
+    if (glError == LOCAL_GL_OUT_OF_MEMORY) {
+        mContext->ErrorOutOfMemory("%s: Driver ran out of memory during upload.",
+                                   funcName);
+        return;
     }
 
-    CheckedUint32 checked_neededByteLength =
-        mContext->GetImageSize(height, width, 1, srcTexelSize, mContext->mPixelStoreUnpackAlignment);
-
-    CheckedUint32 checked_plainRowSize = CheckedUint32(width) * srcTexelSize;
-    CheckedUint32 checked_alignedRowSize =
-        RoundedToNextMultipleOf(checked_plainRowSize.value(), mContext->mPixelStoreUnpackAlignment);
-
-    if (!checked_neededByteLength.isValid())
-        return mContext->ErrorInvalidOperation("texImage2D: integer overflow computing the needed buffer size");
-
-    uint32_t bytesNeeded = checked_neededByteLength.value();
-
-    if (byteLength && byteLength < bytesNeeded)
-        return mContext->ErrorInvalidOperation("texImage2D: not enough data for operation (need %d, have %d)",
-                                 bytesNeeded, byteLength);
-
-    if (mImmutable) {
-        return mContext->ErrorInvalidOperation(
-            "texImage2D: disallowed because the texture "
-            "bound to this target has already been made immutable by texStorage2D");
-    }
-    mContext->MakeContextCurrent();
-
-    nsAutoArrayPtr<uint8_t> convertedData;
-    void* pixels = nullptr;
-    bool isDataInitialized = false;
-
-    WebGLTexelFormat dstFormat = GetWebGLTexelFormat(effectiveInternalFormat);
-    WebGLTexelFormat actualSrcFormat = srcFormat == WebGLTexelFormat::Auto ? dstFormat : srcFormat;
-
-    if (byteLength) {
-        size_t   bitsPerTexel = GetBitsPerTexel(effectiveInternalFormat);
-        MOZ_ASSERT((bitsPerTexel % 8) == 0); // should not have compressed formats here.
-        size_t   dstTexelSize = bitsPerTexel / 8;
-        size_t   srcStride = srcStrideOrZero ? srcStrideOrZero : checked_alignedRowSize.value();
-        size_t   dstPlainRowSize = dstTexelSize * width;
-        size_t   unpackAlignment = mContext->mPixelStoreUnpackAlignment;
-        size_t   dstStride = ((dstPlainRowSize + unpackAlignment-1) / unpackAlignment) * unpackAlignment;
-
-        if (actualSrcFormat == dstFormat &&
-            srcPremultiplied == mContext->mPixelStorePremultiplyAlpha &&
-            srcStride == dstStride &&
-            !mContext->mPixelStoreFlipY)
-        {
-            // no conversion, no flipping, so we avoid copying anything and just pass the source pointer
-            pixels = data;
-        } else {
-            size_t convertedDataSize = height * dstStride;
-            convertedData = new (fallible) uint8_t[convertedDataSize];
-            if (!convertedData) {
-                mContext->ErrorOutOfMemory("texImage2D: Ran out of memory when allocating"
-                                 " a buffer for doing format conversion.");
-                return;
-            }
-            if (!mContext->ConvertImage(width, height, srcStride, dstStride,
-                              static_cast<uint8_t*>(data), convertedData,
-                              actualSrcFormat, srcPremultiplied,
-                              dstFormat, mContext->mPixelStorePremultiplyAlpha, dstTexelSize))
-            {
-                return mContext->ErrorInvalidOperation("texImage2D: Unsupported texture format conversion");
-            }
-            pixels = reinterpret_cast<void*>(convertedData.get());
-        }
-        isDataInitialized = true;
-    }
-
-    GLenum error = CheckedTexImage2D(texImageTarget, level, internalFormat, width,
-                                     height, border, unpackFormat, unpackType, pixels);
-
-    if (error) {
-        mContext->GenerateWarning("texImage2D generated error %s", mContext->ErrorName(error));
+    if (glError) {
+        mContext->ErrorInvalidOperation("%s: Unexpected error during upload: 0x04x",
+                                        funcName, glError);
+        MOZ_ASSERT(false, "Unexpected GL error.");
         return;
     }
 
-    const uint32_t depth = 1;
-    const ImageInfo imageInfo(effectiveInternalFormat, width, height, depth,
-                              isDataInitialized);
-    SetImageInfoAt(texImageTarget, level, imageInfo);
+    ////////////////////////////////////
+    // Update our specification data?
+
+    if (uploadWillInitialize) {
+        imageInfo->SetIsDataInitialized(true, this);
+    }
 }
 
-void
-WebGLTexture::TexImage2D(TexImageTarget texImageTarget, GLint level,
-                         GLenum internalFormat, GLsizei width,
-                         GLsizei height, GLint border, GLenum unpackFormat,
-                         GLenum unpackType, const dom::Nullable<dom::ArrayBufferViewOrSharedArrayBufferView>& maybeView,
-                         ErrorResult* const out_rv)
-{
-    void* data;
-    size_t length;
-    js::Scalar::Type jsArrayType;
-    if (maybeView.IsNull()) {
-        data = nullptr;
-        length = 0;
-        jsArrayType = js::Scalar::MaxTypedArrayViewType;
-    } else {
-        const auto& view = maybeView.Value();
-        ComputeLengthAndData(view, &data, &length, &jsArrayType);
-    }
-
-    const char funcName[] = "texImage2D";
-    if (!DoesTargetMatchDimensions(mContext, texImageTarget, 2, funcName))
-        return;
-
-    return TexImage2D_base(texImageTarget, level, internalFormat, width, height, 0, border, unpackFormat, unpackType,
-                           data, length, jsArrayType,
-                           WebGLTexelFormat::Auto, false);
-}
+////////////////////////////////////////
+// CompressedTex(Sub)Image
 
 void
-WebGLTexture::TexImage2D(TexImageTarget texImageTarget, GLint level,
-                         GLenum internalFormat, GLenum unpackFormat,
-                         GLenum unpackType, dom::ImageData* imageData, ErrorResult* const out_rv)
+WebGLTexture::CompressedTexImage(const char* funcName, TexImageTarget target, GLint level,
+                                 GLenum internalFormat, GLsizei width, GLsizei height,
+                                 GLsizei depth, GLint border,
+                                 const dom::ArrayBufferViewOrSharedArrayBufferView& view)
 {
-    if (!imageData) {
-        // Spec says to generate an INVALID_VALUE error
-        return mContext->ErrorInvalidValue("texImage2D: null ImageData");
-    }
-
-    dom::Uint8ClampedArray arr;
-    DebugOnly<bool> inited = arr.Init(imageData->GetDataObject());
-    MOZ_ASSERT(inited);
-    arr.ComputeLengthAndData();
-
-    void* pixelData = arr.Data();
-    const uint32_t pixelDataLength = arr.Length();
-
-    const char funcName[] = "texImage2D";
-    if (!DoesTargetMatchDimensions(mContext, texImageTarget, 2, funcName))
-        return;
+    ////////////////////////////////////
+    // Get dest info
 
-    return TexImage2D_base(texImageTarget, level, internalFormat, imageData->Width(),
-                           imageData->Height(), 4*imageData->Width(), 0,
-                           unpackFormat, unpackType, pixelData, pixelDataLength, js::Scalar::MaxTypedArrayViewType,
-                           WebGLTexelFormat::RGBA8, false);
-}
+    WebGLTexture::ImageInfo* imageInfo;
+    if (!ValidateTexImageSpecification(funcName, target, level, width, height, depth,
+                                       border, &imageInfo))
+    {
+        return;
+    }
+    MOZ_ASSERT(imageInfo);
 
-void
-WebGLTexture::TexImage2D(TexImageTarget texImageTarget, GLint level,
-                GLenum internalFormat, GLenum unpackFormat, GLenum unpackType,
-                dom::Element* elem, ErrorResult* out_rv)
-{
-    const char funcName[] = "texImage2D";
-    if (!DoesTargetMatchDimensions(mContext, texImageTarget, 2, funcName))
-        return;
-
-    if (level < 0)
-        return mContext->ErrorInvalidValue("texImage2D: level is negative");
-
-    const int32_t maxLevel = mContext->MaxTextureLevelForTexImageTarget(texImageTarget);
-    if (level > maxLevel) {
-        mContext->ErrorInvalidValue("texImage2D: level %d is too large, max is %d",
-                          level, maxLevel);
+    auto usage = mContext->mFormatUsage->GetSizedTexUsage(internalFormat);
+    if (!usage) {
+        mContext->ErrorInvalidEnum("%s: Invalid internalFormat: 0x%04x", funcName,
+                                   internalFormat);
         return;
     }
 
-    // Trying to handle the video by GPU directly first
-    if (TexImageFromVideoElement(texImageTarget, level, internalFormat,
-                                 unpackFormat, unpackType, elem))
+    auto format = usage->format;
+    if (!format->compression) {
+        mContext->ErrorInvalidEnum("%s: Specified internalFormat must be compressed.",
+                                   funcName);
+        return;
+    }
+
+    ////////////////////////////////////
+    // Get source info
+
+    void* mutData;
+    size_t dataSize;
+    js::Scalar::Type jsType;
+    ComputeLengthAndData(view, &mutData, &dataSize, &jsType);
+    const void* data = mutData;
+
+    if (!ValidateCompressedTexUnpack(mContext, funcName, width, height, depth, format,
+                                     dataSize))
     {
         return;
     }
 
-    RefPtr<gfx::DataSourceSurface> data;
-    WebGLTexelFormat srcFormat;
-    nsLayoutUtils::SurfaceFromElementResult res = mContext->SurfaceFromElement(elem);
-    *out_rv = mContext->SurfaceFromElementResultToImageSurface(res, data, &srcFormat);
-    if (out_rv->Failed() || !data)
-        return;
-
-    gfx::IntSize size = data->GetSize();
-    uint32_t byteLength = data->Stride() * size.height;
-    return TexImage2D_base(texImageTarget, level, internalFormat,
-                           size.width, size.height, data->Stride(), 0,
-                           unpackFormat, unpackType, data->GetData(), byteLength,
-                           js::Scalar::MaxTypedArrayViewType, srcFormat,
-                           res.mIsPremultiplied);
-}
-
+    ////////////////////////////////////
+    // Check that source is compatible with dest
 
-void
-WebGLTexture::TexSubImage2D_base(TexImageTarget texImageTarget, GLint level,
-                                 GLint xOffset, GLint yOffset,
-                                 GLsizei width, GLsizei height, GLsizei srcStrideOrZero,
-                                 GLenum unpackFormat, GLenum unpackType,
-                                 void* data, uint32_t byteLength,
-                                 js::Scalar::Type jsArrayType,
-                                 WebGLTexelFormat srcFormat, bool srcPremultiplied)
-{
-    const WebGLTexImageFunc func = WebGLTexImageFunc::TexSubImage;
-    const WebGLTexDimensions dims = WebGLTexDimensions::Tex2D;
-
-    if (unpackType == LOCAL_GL_HALF_FLOAT_OES)
-        unpackType = LOCAL_GL_HALF_FLOAT;
-
-    const char funcName[] = "texSubImage2D";
-    if (!DoesTargetMatchDimensions(mContext, texImageTarget, 2, funcName))
-        return;
-
-    WebGLTexture::ImageInfo& imageInfo = ImageInfoAt(texImageTarget, level);
-    if (!imageInfo.IsDefined())
-        return mContext->ErrorInvalidOperation("texSubImage2D: no previously defined texture image");
-
-    const TexInternalFormat existingEffectiveInternalFormat = imageInfo.mFormat;
-
-    if (!mContext->ValidateTexImage(texImageTarget, level,
-                          existingEffectiveInternalFormat.get(),
-                          xOffset, yOffset, 0,
-                          width, height, 0,
-                          0, unpackFormat, unpackType, func, dims))
+    if (!ValidateCompressedTexImageRestrictions(funcName, mContext, target, level, format,
+                                                width, height, depth))
     {
         return;
     }
 
-    if (!mContext->ValidateTexInputData(unpackType, jsArrayType, func, dims))
-        return;
+    ////////////////////////////////////
+    // Do the thing!
 
-    if (unpackType != TypeFromInternalFormat(existingEffectiveInternalFormat)) {
-        return mContext->ErrorInvalidOperation("texSubImage2D: type differs from that of the existing image");
-    }
+    mContext->gl->MakeCurrent();
 
-    size_t srcTexelSize = size_t(-1);
-    if (srcFormat == WebGLTexelFormat::Auto) {
-        const size_t bitsPerTexel = GetBitsPerTexel(existingEffectiveInternalFormat);
-        MOZ_ASSERT((bitsPerTexel % 8) == 0); // should not have compressed formats here.
-        srcTexelSize = bitsPerTexel / 8;
-    } else {
-        srcTexelSize = WebGLTexelConversions::TexelBytesForFormat(srcFormat);
+    GLenum error = DoCompressedTexImage(mContext->gl, target, level, internalFormat,
+                                        width, height, depth, border, dataSize, data);
+    if (error == LOCAL_GL_OUT_OF_MEMORY) {
+        mContext->ErrorOutOfMemory("%s: Ran out of memory during upload.", funcName);
+        return;
+    }
+    if (error) {
+        MOZ_RELEASE_ASSERT(false, "We should have caught all other errors.");
+        mContext->GenerateWarning("%s: Unexpected error during texture upload. Context"
+                                  " lost.",
+                                  funcName);
+        mContext->ForceLoseContext();
+        return;
     }
 
-    if (width == 0 || height == 0)
-        return; // ES 2.0 says it has no effect, we better return right now
-
-    CheckedUint32 checked_neededByteLength =
-        mContext->GetImageSize(height, width, 1, srcTexelSize, mContext->mPixelStoreUnpackAlignment);
-
-    CheckedUint32 checked_plainRowSize = CheckedUint32(width) * srcTexelSize;
-
-    CheckedUint32 checked_alignedRowSize =
-        RoundedToNextMultipleOf(checked_plainRowSize.value(), mContext->mPixelStoreUnpackAlignment);
-
-    if (!checked_neededByteLength.isValid())
-        return mContext->ErrorInvalidOperation("texSubImage2D: integer overflow computing the needed buffer size");
-
-    uint32_t bytesNeeded = checked_neededByteLength.value();
-
-    if (byteLength < bytesNeeded)
-        return mContext->ErrorInvalidOperation("texSubImage2D: not enough data for operation (need %d, have %d)", bytesNeeded, byteLength);
-
-    if (!imageInfo.IsDataInitialized()) {
-        bool coversWholeImage = xOffset == 0 &&
-                                yOffset == 0 &&
-                                uint32_t(width) == imageInfo.mWidth &&
-                                uint32_t(height) == imageInfo.mHeight;
-        if (coversWholeImage) {
-            imageInfo.SetIsDataInitialized(true, this);
-        } else {
-            if (!EnsureInitializedImageData(texImageTarget, level))
-                return;
-        }
-    }
-    mContext->MakeContextCurrent();
-    gl::GLContext* gl = mContext->gl;
-
-    size_t   srcStride = srcStrideOrZero ? srcStrideOrZero : checked_alignedRowSize.value();
-    uint32_t dstTexelSize = GetBitsPerTexel(existingEffectiveInternalFormat) / 8;
-    size_t   dstPlainRowSize = dstTexelSize * width;
-    // There are checks above to ensure that this won't overflow.
-    size_t   dstStride = RoundedToNextMultipleOf(dstPlainRowSize, mContext->mPixelStoreUnpackAlignment).value();
+    ////////////////////////////////////
+    // Update our specification data.
 
-    void* pixels = data;
-    nsAutoArrayPtr<uint8_t> convertedData;
-
-    WebGLTexelFormat dstFormat = GetWebGLTexelFormat(existingEffectiveInternalFormat);
-    WebGLTexelFormat actualSrcFormat = srcFormat == WebGLTexelFormat::Auto ? dstFormat : srcFormat;
-
-    // no conversion, no flipping, so we avoid copying anything and just pass the source pointer
-    bool noConversion = (actualSrcFormat == dstFormat &&
-                         srcPremultiplied == mContext->mPixelStorePremultiplyAlpha &&
-                         srcStride == dstStride &&
-                         !mContext->mPixelStoreFlipY);
-
-    if (!noConversion) {
-        size_t convertedDataSize = height * dstStride;
-        convertedData = new (fallible) uint8_t[convertedDataSize];
-        if (!convertedData) {
-            mContext->ErrorOutOfMemory("texImage2D: Ran out of memory when allocating"
-                             " a buffer for doing format conversion.");
-            return;
-        }
-        if (!mContext->ConvertImage(width, height, srcStride, dstStride,
-                          static_cast<const uint8_t*>(data), convertedData,
-                          actualSrcFormat, srcPremultiplied,
-                          dstFormat, mContext->mPixelStorePremultiplyAlpha, dstTexelSize))
-        {
-            return mContext->ErrorInvalidOperation("texSubImage2D: Unsupported texture format conversion");
-        }
-        pixels = reinterpret_cast<void*>(convertedData.get());
-    }
-
-    GLenum driverType = LOCAL_GL_NONE;
-    GLenum driverInternalFormat = LOCAL_GL_NONE;
-    GLenum driverFormat = LOCAL_GL_NONE;
-    DriverFormatsFromEffectiveInternalFormat(gl,
-                                             existingEffectiveInternalFormat,
-                                             &driverInternalFormat,
-                                             &driverFormat,
-                                             &driverType);
-
-    gl->fTexSubImage2D(texImageTarget.get(), level, xOffset, yOffset, width, height, driverFormat, driverType, pixels);
+    const bool isDataInitialized = true;
+    const ImageInfo newImageInfo(usage, width, height, depth, isDataInitialized);
+    SetImageInfo(imageInfo, newImageInfo);
 }
 
-void
-WebGLTexture::TexSubImage2D(TexImageTarget texImageTarget, GLint level,
-                            GLint xOffset, GLint yOffset,
-                            GLsizei width, GLsizei height,
-                            GLenum unpackFormat, GLenum unpackType,
-                            const dom::Nullable<dom::ArrayBufferViewOrSharedArrayBufferView>& maybeView,
-                            ErrorResult* const out_rv)
-{
-    if (maybeView.IsNull())
-        return mContext->ErrorInvalidValue("texSubImage2D: pixels must not be null!");
-
-    const auto& view = maybeView.Value();
-    size_t length;
-    void* data;
-    js::Scalar::Type jsArrayType;
-    ComputeLengthAndData(view, &data, &length, &jsArrayType);
-
-    const char funcName[] = "texSubImage2D";
-    if (!DoesTargetMatchDimensions(mContext, texImageTarget, 2, funcName))
-        return;
-
-    return TexSubImage2D_base(texImageTarget, level, xOffset, yOffset,
-                              width, height, 0, unpackFormat, unpackType,
-                              data, length, jsArrayType,
-                              WebGLTexelFormat::Auto, false);
-}
-
-void
-WebGLTexture::TexSubImage2D(TexImageTarget texImageTarget, GLint level,
-                            GLint xOffset, GLint yOffset,
-                            GLenum unpackFormat, GLenum unpackType, dom::ImageData* imageData,
-                            ErrorResult* const out_rv)
+static inline bool
+IsSubImageBlockAligned(const webgl::CompressedFormatInfo* compression,
+                       const WebGLTexture::ImageInfo* imageInfo, GLint xOffset,
+                       GLint yOffset, GLsizei width, GLsizei height)
 {
-    if (!imageData)
-        return mContext->ErrorInvalidValue("texSubImage2D: pixels must not be null!");
-
-    dom::Uint8ClampedArray arr;
-    DebugOnly<bool> inited = arr.Init(imageData->GetDataObject());
-    MOZ_ASSERT(inited);
-    arr.ComputeLengthAndData();
-
-    return TexSubImage2D_base(texImageTarget, level, xOffset, yOffset,
-                              imageData->Width(), imageData->Height(),
-                              4*imageData->Width(), unpackFormat, unpackType,
-                              arr.Data(), arr.Length(),
-                              js::Scalar::MaxTypedArrayViewType,
-                              WebGLTexelFormat::RGBA8, false);
-}
-
-
-
-bool
-WebGLTexture::TexImageFromVideoElement(TexImageTarget texImageTarget,
-                                       GLint level, GLenum internalFormat,
-                                       GLenum unpackFormat, GLenum unpackType,
-                                       mozilla::dom::Element* elem)
-{
-    if (unpackType == LOCAL_GL_HALF_FLOAT_OES &&
-        !mContext->gl->IsExtensionSupported(gl::GLContext::OES_texture_half_float))
-    {
-        unpackType = LOCAL_GL_HALF_FLOAT;
-    }
-
-    if (!mContext->ValidateTexImageFormatAndType(unpackFormat, unpackType,
-                                       WebGLTexImageFunc::TexImage,
-                                       WebGLTexDimensions::Tex2D))
+    if (xOffset % compression->blockWidth != 0 ||
+        yOffset % compression->blockHeight != 0)
     {
         return false;
     }
 
-    dom::HTMLVideoElement* video = dom::HTMLVideoElement::FromContentOrNull(elem);
-    if (!video)
-        return false;
-
-    uint16_t readyState;
-    if (NS_SUCCEEDED(video->GetReadyState(&readyState)) &&
-        readyState < nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA)
-    {
-        //No frame inside, just return
-        return false;
-    }
-
-    // If it doesn't have a principal, just bail
-    nsCOMPtr<nsIPrincipal> principal = video->GetCurrentPrincipal();
-    if (!principal)
-        return false;
-
-    mozilla::layers::ImageContainer* container = video->GetImageContainer();
-    if (!container)
+    if (width % compression->blockWidth != 0 && xOffset + width != imageInfo->mWidth)
         return false;
 
-    if (video->GetCORSMode() == CORS_NONE) {
-        bool subsumes;
-        nsresult rv = mContext->mCanvasElement->NodePrincipal()->Subsumes(principal, &subsumes);
-        if (NS_FAILED(rv) || !subsumes) {
-            mContext->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 false;
-        }
-    }
-
-    layers::AutoLockImage lockedImage(container);
-    layers::Image* srcImage = lockedImage.GetImage();
-    if (!srcImage) {
-      return false;
-    }
-
-    const uint32_t width = srcImage->GetSize().width;
-    const uint32_t height = srcImage->GetSize().height;
-
-    gl::GLContext* gl = mContext->gl;
-    gl->MakeCurrent();
-
-    MOZ_ASSERT(level == 0);
-    const WebGLTexture::ImageInfo& info = ImageInfoAt(texImageTarget, 0);
-    bool dimensionsMatch = (info.mWidth == width &&
-                            info.mHeight == height &&
-                            info.mDepth == 1);
-    if (!dimensionsMatch) {
-        // we need to allocation
-        gl->fTexImage2D(texImageTarget.get(), level, internalFormat,
-                        width, height,
-                        0, unpackFormat, unpackType, nullptr);
-    }
-
-    const gl::OriginPos destOrigin = mContext->mPixelStoreFlipY ? gl::OriginPos::BottomLeft
-                                                      : gl::OriginPos::TopLeft;
-    bool ok = gl->BlitHelper()->BlitImageToTexture(srcImage,
-                                                   srcImage->GetSize(),
-                                                   mGLName,
-                                                   texImageTarget.get(),
-                                                   destOrigin);
-    if (!ok)
+    if (height % compression->blockHeight != 0 && yOffset + height != imageInfo->mHeight)
         return false;
 
-    TexInternalFormat effectiveInternalFormat =
-        EffectiveInternalFormatFromInternalFormatAndType(internalFormat,
-                                                         unpackType);
-    MOZ_ASSERT(effectiveInternalFormat != LOCAL_GL_NONE);
-
-    const uint32_t depth = 1;
-    const bool isDataInitialized = true;
-    const ImageInfo imageInfo(effectiveInternalFormat, width, height, depth,
-                              isDataInitialized);
-    SetImageInfoAt(texImageTarget, level, imageInfo);
-
     return true;
 }
 
 void
-WebGLTexture::TexSubImage2D(TexImageTarget texImageTarget, GLint level, GLint xOffset,
-                            GLint yOffset, GLenum unpackFormat, GLenum unpackType,
-                            dom::Element* elem, ErrorResult* const out_rv)
+WebGLTexture::CompressedTexSubImage(const char* funcName, TexImageTarget target,
+                                    GLint level, GLint xOffset, GLint yOffset,
+                                    GLint zOffset, GLsizei width, GLsizei height,
+                                    GLsizei depth, GLenum sizedUnpackFormat,
+                                    const dom::ArrayBufferViewOrSharedArrayBufferView& view)
 {
-    const char funcName[] = "texSubImage2D";
-    if (!DoesTargetMatchDimensions(mContext, texImageTarget, 2, funcName))
-        return;
+    ////////////////////////////////////
+    // Get dest info
 
-    if (level < 0)
-        return mContext->ErrorInvalidValue("texSubImage2D: level is negative");
+    WebGLTexture::ImageInfo* imageInfo;
+    if (!ValidateTexImageSelection(funcName, target, level, xOffset, yOffset, zOffset,
+                                   width, height, depth, &imageInfo))
+    {
+        return;
+    }
+    MOZ_ASSERT(imageInfo);
+
+    auto dstUsage = imageInfo->mFormat;
+    auto dstFormat = dstUsage->format;
 
-    const int32_t maxLevel = mContext->MaxTextureLevelForTexImageTarget(texImageTarget);
-    if (level > maxLevel) {
-        mContext->ErrorInvalidValue("texSubImage2D: level %d is too large, max is %d",
-                          level, maxLevel);
+    ////////////////////////////////////
+    // Get source info
+
+    void* mutData;
+    size_t dataSize;
+    js::Scalar::Type jsType;
+    ComputeLengthAndData(view, &mutData, &dataSize, &jsType);
+    const void* data = mutData;
+
+    auto srcUsage = mContext->mFormatUsage->GetSizedTexUsage(sizedUnpackFormat);
+    if (!srcUsage->format->compression) {
+        mContext->ErrorInvalidEnum("%s: Specified format must be compressed.", funcName);
         return;
     }
 
-    const WebGLTexture::ImageInfo& imageInfo = ImageInfoAt(texImageTarget, level);
-    const TexInternalFormat internalFormat = imageInfo.mFormat;
+    if (srcUsage != dstUsage) {
+        mContext->ErrorInvalidOperation("%s: `format` must match the format of the"
+                                        " existing texture image.",
+                                        funcName);
+        return;
+    }
 
-    // Trying to handle the video by GPU directly first
-    if (TexImageFromVideoElement(texImageTarget, level, internalFormat.get(), unpackFormat, unpackType, elem))
+    auto format = srcUsage->format;
+    MOZ_ASSERT(format == dstFormat);
+    if (!ValidateCompressedTexUnpack(mContext, funcName, width, height, depth, format,
+                                     dataSize))
     {
         return;
     }
 
-    RefPtr<gfx::DataSourceSurface> data;
-    WebGLTexelFormat srcFormat;
-    nsLayoutUtils::SurfaceFromElementResult res = mContext->SurfaceFromElement(elem);
-    *out_rv = mContext->SurfaceFromElementResultToImageSurface(res, data, &srcFormat);
-    if (out_rv->Failed() || !data)
+    ////////////////////////////////////
+    // Check that source is compatible with dest
+
+    switch (format->compression->family) {
+    // Forbidden:
+    case webgl::CompressionFamily::ETC1:
+    case webgl::CompressionFamily::ATC:
+        mContext->ErrorInvalidOperation("%s: Format does not allow sub-image"
+                                        " updates.", funcName);
         return;
 
-    gfx::IntSize size = data->GetSize();
-    uint32_t byteLength = data->Stride() * size.height;
-    TexSubImage2D_base(texImageTarget, level, xOffset, yOffset, size.width,
-                       size.height, data->Stride(), unpackFormat, unpackType, data->GetData(),
-                       byteLength, js::Scalar::MaxTypedArrayViewType, srcFormat,
-                       res.mIsPremultiplied);
+    // Block-aligned:
+    case webgl::CompressionFamily::ES3:  // Yes, the ES3 formats don't match the ES3
+    case webgl::CompressionFamily::S3TC: // default behavior.
+        if (!IsSubImageBlockAligned(dstFormat->compression, imageInfo, xOffset, yOffset,
+                                    width, height))
+        {
+            mContext->ErrorInvalidOperation("%s: Format requires block-aligned sub-image"
+                                            " updates.",
+                                            funcName);
+            return;
+        }
+        break;
+
+    // Full-only: (The ES3 default)
+    default: // PVRTC
+        if (xOffset || yOffset ||
+            width != imageInfo->mWidth ||
+            height != imageInfo->mHeight)
+        {
+            mContext->ErrorInvalidOperation("%s: Format does not allow partial sub-image"
+                                            " updates.",
+                                            funcName);
+            return;
+        }
+        break;
+    }
+
+    ////////////////////////////////////
+    // Do the thing!
+
+    mContext->gl->MakeCurrent();
+
+    bool uploadWillInitialize;
+    if (!EnsureImageDataInitializedForUpload(this, funcName, target, level, xOffset,
+                                             yOffset, zOffset, width, height, depth,
+                                             imageInfo, &uploadWillInitialize))
+    {
+        return;
+    }
+
+    GLenum error = DoCompressedTexSubImage(mContext->gl, target, level, xOffset, yOffset,
+                                           zOffset, width, height, depth,
+                                           sizedUnpackFormat, dataSize, data);
+    if (error == LOCAL_GL_OUT_OF_MEMORY) {
+        mContext->ErrorOutOfMemory("%s: Ran out of memory during upload.", funcName);
+        return;
+    }
+    if (error) {
+        MOZ_RELEASE_ASSERT(false, "We should have caught all other errors.");
+        mContext->GenerateWarning("%s: Unexpected error during texture upload. Context"
+                                  " lost.",
+                                  funcName);
+        mContext->ForceLoseContext();
+        return;
+    }
+
+    ////////////////////////////////////
+    // Update our specification data?
+
+    if (uploadWillInitialize) {
+        imageInfo->SetIsDataInitialized(true, this);
+    }
 }
 
-bool
-WebGLTexture::ValidateSizedInternalFormat(GLenum internalFormat, const char* info)
+////////////////////////////////////////
+// CopyTex(Sub)Image
+
+static bool
+ValidateCopyTexImageFormats(WebGLContext* webgl, const char* funcName,
+                            const webgl::FormatInfo* srcFormat,
+                            const webgl::FormatInfo* dstFormat)
 {
-    switch (internalFormat) {
-        // Sized Internal Formats
-        // https://www.khronos.org/opengles/sdk/docs/man3/html/glTexStorage2D.xhtml
-    case LOCAL_GL_R8:
-    case LOCAL_GL_R8_SNORM:
-    case LOCAL_GL_R16F:
-    case LOCAL_GL_R32F:
-    case LOCAL_GL_R8UI:
-    case LOCAL_GL_R8I:
-    case LOCAL_GL_R16UI:
-    case LOCAL_GL_R16I:
-    case LOCAL_GL_R32UI:
-    case LOCAL_GL_R32I:
-    case LOCAL_GL_RG8:
-    case LOCAL_GL_RG8_SNORM:
-    case LOCAL_GL_RG16F:
-    case LOCAL_GL_RG32F:
-    case LOCAL_GL_RG8UI:
-    case LOCAL_GL_RG8I:
-    case LOCAL_GL_RG16UI:
-    case LOCAL_GL_RG16I:
-    case LOCAL_GL_RG32UI:
-    case LOCAL_GL_RG32I:
-    case LOCAL_GL_RGB8:
-    case LOCAL_GL_SRGB8:
-    case LOCAL_GL_RGB565:
-    case LOCAL_GL_RGB8_SNORM:
-    case LOCAL_GL_R11F_G11F_B10F:
-    case LOCAL_GL_RGB9_E5:
-    case LOCAL_GL_RGB16F:
-    case LOCAL_GL_RGB32F:
-    case LOCAL_GL_RGB8UI:
-    case LOCAL_GL_RGB8I:
-    case LOCAL_GL_RGB16UI:
-    case LOCAL_GL_RGB16I:
-    case LOCAL_GL_RGB32UI:
-    case LOCAL_GL_RGB32I:
-    case LOCAL_GL_RGBA8:
-    case LOCAL_GL_SRGB8_ALPHA8:
-    case LOCAL_GL_RGBA8_SNORM:
-    case LOCAL_GL_RGB5_A1:
-    case LOCAL_GL_RGBA4:
-    case LOCAL_GL_RGB10_A2:
-    case LOCAL_GL_RGBA16F:
-    case LOCAL_GL_RGBA32F:
-    case LOCAL_GL_RGBA8UI:
-    case LOCAL_GL_RGBA8I:
-    case LOCAL_GL_RGB10_A2UI:
-    case LOCAL_GL_RGBA16UI:
-    case LOCAL_GL_RGBA16I:
-    case LOCAL_GL_RGBA32I:
-    case LOCAL_GL_RGBA32UI:
-    case LOCAL_GL_DEPTH_COMPONENT16:
-    case LOCAL_GL_DEPTH_COMPONENT24:
-    case LOCAL_GL_DEPTH_COMPONENT32F:
-    case LOCAL_GL_DEPTH24_STENCIL8:
-    case LOCAL_GL_DEPTH32F_STENCIL8:
-        return true;
-    }
-
-    if (IsCompressedTextureFormat(internalFormat))
-        return true;
-
-    nsCString name;
-    mContext->EnumName(internalFormat, &name);
-    mContext->ErrorInvalidEnum("%s: invalid internal format %s", info, name.get());
-
-    return false;
-}
-
-bool
-WebGLTexture::ValidateTexStorage(TexTarget texTarget, GLsizei levels,
-                                 GLenum internalFormat, GLsizei width, GLsizei height,
-                                 GLsizei depth, const char* funcName)
-{
-    // INVALID_OPERATION is generated if the texture object currently bound to target
-    // already has TEXTURE_IMMUTABLE_FORMAT set to TRUE.
-    // While there isn't an explicit reference to this, it seems to fall out of GLES
-    // 3.0.4:
-    // p138: "Using [TexImage*] with the same texture will result in an
-    //        INVALID_OPERATION error being generated, even if it does not affect the"
-    //        dimensions or unpackFormat[.]"
-    // p136: "* If executing the pseudo-code would result in any other error, the error is
-    //          generated and the command will have no effect."
-    // p137-138: The pseudo-code uses TexImage*.
-    //
-    if (mImmutable) {
-        mContext->ErrorInvalidOperation("%s: Texture is already immutable.", funcName);
+    MOZ_ASSERT(!srcFormat->compression);
+    if (dstFormat->compression) {
+        webgl->ErrorInvalidEnum("%s: Specified destination must not have a compressed"
+                                " format.",
+                                funcName);
         return false;
     }
 
-    // INVALID_ENUM is generated if internalformat is not a valid sized internal unpackFormat.
-    if (!ValidateSizedInternalFormat(internalFormat, funcName))
-        return false;
-
-    // INVALID_VALUE is generated if width, height or levels are less than 1.
-    if (width < 1 || height < 1 || depth < 1 || levels < 1) {
-        mContext->ErrorInvalidValue("%s: Width, height, depth, and levels must be >= 1.",
-                                    funcName);
+    if (dstFormat->effectiveFormat == webgl::EffectiveFormat::RGB9_E5) {
+        webgl->ErrorInvalidOperation("%s: RGB9_E5 is an invalid destination for"
+                                     " CopyTex(Sub)Image. (GLES 3.0.4 p145)",
+                                     funcName);
         return false;
     }
 
-    // INVALID_OPERATION is generated if levels is greater than floor(log2(max(width, height, depth)))+1.
-    GLsizei largest = std::max(std::max(width, height), depth);
-    GLsizei maxLevels = FloorLog2(largest) + 1;
-    if (levels > maxLevels) {
-        mContext->ErrorInvalidOperation("%s: Too many levels for given texture dimensions.",
-                                        funcName);
+    if (!DoChannelsMatchForCopyTexImage(srcFormat, dstFormat)) {
+        webgl->ErrorInvalidOperation("%s: Destination channels must be compatible with"
+                                     " source channels. (GLES 3.0.4 p140 Table 3.16)",
+                                     funcName);
         return false;
     }
 
     return true;
 }
 
+// There is no CopyTexImage3D.
 void
-WebGLTexture::TexStorage2D(TexTarget texTarget, GLsizei levels, GLenum rawInternalFormat,
-                           GLsizei width, GLsizei height)
+WebGLTexture::CopyTexImage2D(TexImageTarget target, GLint level, GLenum internalFormat,
+                             GLint x, GLint y, GLsizei width, GLsizei height,
+                             GLint border)
 {
-    if (texTarget != LOCAL_GL_TEXTURE_2D && texTarget != LOCAL_GL_TEXTURE_CUBE_MAP) {
-        mContext->ErrorInvalidEnum("texStorage2D: target must be TEXTURE_2D or"
-                                   " TEXTURE_CUBE_MAP.");
+    const char funcName[] = "CopyTexImage2D";
+
+    const uint8_t depth = 1;
+
+    ////////////////////////////////////
+    // Get dest info
+
+    WebGLTexture::ImageInfo* imageInfo;
+    if (!ValidateTexImageSpecification(funcName, target, level, width, height, depth,
+                                       border, &imageInfo))
+    {
+        return;
+    }
+    MOZ_ASSERT(imageInfo);
+
+    ////////////////////////////////////
+    // Get source info
+
+    const webgl::FormatUsageInfo* srcUsage;
+    uint32_t srcWidth;
+    uint32_t srcHeight;
+    if (!mContext->ValidateCurFBForRead(funcName, &srcUsage, &srcWidth, &srcHeight))
+        return;
+    auto srcFormat = srcUsage->format;
+
+    ////////////////////////////////////
+    // Check that source and dest info are compatible
+
+    const auto& fua = mContext->mFormatUsage;
+
+    auto dstUsage = fua->GetSizedTexUsage(internalFormat);
+    if (!dstUsage) {
+        // It must be an unsized format then...
+        webgl::PackingInfo pi = {internalFormat, 0};
+
+        switch (srcFormat->componentType) {
+        case webgl::ComponentType::NormUInt:
+            pi.type = LOCAL_GL_UNSIGNED_BYTE;
+            break;
+
+        case webgl::ComponentType::Float:
+            pi.type = LOCAL_GL_FLOAT;
+            break;
+        }
+
+        dstUsage = fua->GetUnsizedTexUsage(pi);
+    }
+
+    if (!dstUsage) {
+        mContext->ErrorInvalidEnum("%s: Invalid internalFormat 0x%04x for FB format %s.",
+                                   funcName, internalFormat, srcFormat->name);
+        return;
+    }
+    auto dstFormat = dstUsage->format;
+
+    if (!mContext->IsWebGL2() && dstFormat->hasDepth) {
+        mContext->ErrorInvalidOperation("%s: Function may not be called with format %s.",
+                                        funcName, dstFormat->name);
         return;
     }
 
-    const GLsizei depth = 1;
-    if (!ValidateTexStorage(texTarget, levels, rawInternalFormat, width, height, depth,
-                            "texStorage2D"))
-    {
+    if (!ValidateCopyTexImageFormats(mContext, funcName, srcFormat, dstFormat))
         return;
-    }
 
-    TexInternalFormat internalFormat(rawInternalFormat);
+    ////////////////////////////////////
+    // Do the thing!
 
     gl::GLContext* gl = mContext->gl;
     gl->MakeCurrent();
 
-    mContext->GetAndFlushUnderlyingGLErrors();
-    gl->fTexStorage2D(texTarget.get(), levels, internalFormat.get(), width, height);
-    GLenum error = mContext->GetAndFlushUnderlyingGLErrors();
-    if (error) {
-        mContext->GenerateWarning("texStorage2D generated error %s",
-                                  mContext->ErrorName(error));
-        return;
+    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);
+
+    GLenum error;
+    if (rwWidth == width && rwHeight == height) {
+        error = DoCopyTexImage2D(gl, target, level, internalFormat, x, y, width, height,
+                                 border);
+    } else {
+        // 1. Zero the texture data.
+        // 2. CopyTexSubImage the subrect.
+
+        const bool respecifyTexture = true;
+        const uint8_t zOffset = 0;
+        if (!ZeroTextureData(mContext, funcName, respecifyTexture, target, level,
+                             dstUsage, 0, 0, zOffset, width, height, depth))
+        {
+            mContext->ErrorOutOfMemory("%s: Failed to zero texture data.", funcName);
+            MOZ_ASSERT(false, "Failed to zero texture data.");
+            return;
+        }
+
+        if (!rwWidth || !rwHeight) {
+            // There aren't any, so we're 'done'.
+            mContext->DummyFramebufferOperation(funcName);
+            return;
+        }
+
+        error = DoCopyTexSubImage(gl, target, level, writeX, writeY, zOffset, readX,
+                                  readY, rwWidth, rwHeight);
     }
 
-    SpecifyTexStorage(levels, internalFormat, width, height, depth);
-}
-
-void
-WebGLTexture::TexStorage3D(TexTarget texTarget, GLsizei levels, GLenum rawInternalFormat,
-                           GLsizei width, GLsizei height, GLsizei depth)
-{
-    if (texTarget != LOCAL_GL_TEXTURE_3D)
-        return mContext->ErrorInvalidEnum("texStorage3D: target must be TEXTURE_3D.");
-
-    if (!ValidateTexStorage(texTarget, levels, rawInternalFormat, width, height, depth,
-                            "texStorage3D"))
-    {
+    if (error == LOCAL_GL_OUT_OF_MEMORY) {
+        mContext->ErrorOutOfMemory("%s: Ran out of memory during texture copy.",
+                                   funcName);
+        return;
+    }
+    if (error) {
+        MOZ_RELEASE_ASSERT(false, "We should have caught all other errors.");
+        mContext->GenerateWarning("%s: Unexpected error during texture copy. Context"
+                                  " lost.",
+                                  funcName);
+        mContext->ForceLoseContext();
         return;
     }
 
-    TexInternalFormat internalFormat(rawInternalFormat);
-
-    gl::GLContext* gl = mContext->gl;
-    gl->MakeCurrent();
+    ////////////////////////////////////
+    // Update our specification data.
 
-    mContext->GetAndFlushUnderlyingGLErrors();
-    gl->fTexStorage3D(texTarget.get(), levels, internalFormat.get(), width, height, depth);
-    GLenum error = mContext->GetAndFlushUnderlyingGLErrors();
-    if (error) {
-        mContext->GenerateWarning("texStorage3D generated error %s",
-                                  mContext->ErrorName(error));
-        return;
-    }
-
-    SpecifyTexStorage(levels, internalFormat, width, height, depth);
+    const bool isDataInitialized = true;
+    const ImageInfo newImageInfo(dstUsage, width, height, depth, isDataInitialized);
+    SetImageInfo(imageInfo, newImageInfo);
 }
 
 void
-WebGLTexture::TexImage3D(TexImageTarget texImageTarget, GLint level, GLenum internalFormat,
-                          GLsizei width, GLsizei height, GLsizei depth,
-                          GLint border, GLenum unpackFormat, GLenum unpackType,
-                          const dom::Nullable<dom::ArrayBufferViewOrSharedArrayBufferView>& maybeView,
-                          ErrorResult* const out_rv)
+WebGLTexture::CopyTexSubImage(const char* funcName, TexImageTarget target, GLint level,
+                              GLint xOffset, GLint yOffset, GLint zOffset, GLint x,
+                              GLint y, GLsizei width, GLsizei height)
 {
-    void* data;
-    size_t dataLength;
-    js::Scalar::Type jsArrayType;
-    if (maybeView.IsNull()) {
-        data = nullptr;
-        dataLength = 0;
-        jsArrayType = js::Scalar::MaxTypedArrayViewType;
-    } else {
-        const auto& view = maybeView.Value();
-        ComputeLengthAndData(view, &data, &dataLength, &jsArrayType);
+    const GLsizei depth = 1;
+
+    ////////////////////////////////////
+    // Get dest info
+
+    WebGLTexture::ImageInfo* imageInfo;
+    if (!ValidateTexImageSelection(funcName, target, level, xOffset, yOffset, zOffset,
+                                   width, height, depth, &imageInfo))
+    {
+        return;
+    }
+    MOZ_ASSERT(imageInfo);
+
+    auto dstUsage = imageInfo->mFormat;
+    MOZ_ASSERT(dstUsage);
+    auto dstFormat = dstUsage->format;
+
+    if (!mContext->IsWebGL2() && dstFormat->hasDepth) {
+        mContext->ErrorInvalidOperation("%s: Function may not be called on a texture of"
+                                        " format %s.",
+                                        funcName, dstFormat->name);
+        return;
     }
 
-    const char funcName[] = "texImage3D";
-    if (!DoesTargetMatchDimensions(mContext, texImageTarget, 3, funcName))
+    ////////////////////////////////////
+    // Get source info
+
+    const webgl::FormatUsageInfo* srcUsage;
+    uint32_t srcWidth;
+    uint32_t srcHeight;
+    if (!mContext->ValidateCurFBForRead(funcName, &srcUsage, &srcWidth, &srcHeight))
+        return;
+    auto srcFormat = srcUsage->format;
+
+    ////////////////////////////////////
+    // Check that source and dest info are compatible
+
+    if (!ValidateCopyTexImageFormats(mContext, funcName, srcFormat, dstFormat))
         return;
 
-    const WebGLTexImageFunc func = WebGLTexImageFunc::TexImage;
-    const WebGLTexDimensions dims = WebGLTexDimensions::Tex3D;
+    ////////////////////////////////////
+    // Do the thing!
+
+    mContext->gl->MakeCurrent();
+
+    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 (!mContext->ValidateTexImage(texImageTarget, level, internalFormat,
-                          0, 0, 0,
-                          width, height, depth,
-                          border, unpackFormat, unpackType, func, dims))
+    if (!rwWidth || !rwHeight) {
+        // There aren't any, so we're 'done'.
+        mContext->DummyFramebufferOperation(funcName);
+        return;
+    }
+
+    bool uploadWillInitialize;
+    if (!EnsureImageDataInitializedForUpload(this, funcName, target, level, xOffset,
+                                             yOffset, zOffset, width, height, depth,
+                                             imageInfo, &uploadWillInitialize))
     {
         return;
     }
 
-    if (!mContext->ValidateTexInputData(unpackType, jsArrayType, func, dims))
-        return;
-
-    TexInternalFormat effectiveInternalFormat =
-        EffectiveInternalFormatFromInternalFormatAndType(internalFormat, unpackType);
-
-    if (effectiveInternalFormat == LOCAL_GL_NONE) {
-        return mContext->ErrorInvalidOperation("texImage3D: bad combination of internalFormat and unpackType");
-    }
-
-    // we need to find the exact sized format of the source data. Slightly abusing
-    // EffectiveInternalFormatFromInternalFormatAndType for that purpose. Really, an unsized source format
-    // is the same thing as an unsized internalFormat.
-    TexInternalFormat effectiveSourceFormat =
-        EffectiveInternalFormatFromInternalFormatAndType(unpackFormat, unpackType);
-    MOZ_ASSERT(effectiveSourceFormat != LOCAL_GL_NONE); // should have validated unpack format/type combo earlier
-    const size_t srcbitsPerTexel = GetBitsPerTexel(effectiveSourceFormat);
-    MOZ_ASSERT((srcbitsPerTexel % 8) == 0); // should not have compressed formats here.
-    size_t srcTexelSize = srcbitsPerTexel / 8;
-
-    CheckedUint32 checked_neededByteLength =
-        mContext->GetImageSize(height, width, depth, srcTexelSize, mContext->mPixelStoreUnpackAlignment);
-
-    if (!checked_neededByteLength.isValid())
-        return mContext->ErrorInvalidOperation("texSubImage2D: integer overflow computing the needed buffer size");
-
-    uint32_t bytesNeeded = checked_neededByteLength.value();
-
-    if (dataLength && dataLength < bytesNeeded)
-        return mContext->ErrorInvalidOperation("texImage3D: not enough data for operation (need %d, have %d)",
-                                 bytesNeeded, dataLength);
-
-    if (mImmutable) {
-        return mContext->ErrorInvalidOperation(
-            "texImage3D: disallowed because the texture "
-            "bound to this target has already been made immutable by texStorage3D");
-    }
-
-    gl::GLContext* gl = mContext->gl;
-    gl->MakeCurrent();
-
-    GLenum driverUnpackType = LOCAL_GL_NONE;
-    GLenum driverInternalFormat = LOCAL_GL_NONE;
-    GLenum driverUnpackFormat = LOCAL_GL_NONE;
-    DriverFormatsFromEffectiveInternalFormat(gl,
-                                             effectiveInternalFormat,
-                                             &driverInternalFormat,
-                                             &driverUnpackFormat,
-                                             &driverUnpackType);
+    GLenum error = DoCopyTexSubImage(mContext->gl, target, level, xOffset + writeX,
+                                     yOffset + writeY, zOffset, readX, readY, rwWidth,
+                                     rwHeight);
 
-    mContext->GetAndFlushUnderlyingGLErrors();
-    gl->fTexImage3D(texImageTarget.get(), level,
-                    driverInternalFormat,
-                    width, height, depth,
-                    0, driverUnpackFormat, driverUnpackType,
-                    data);
-    GLenum error = mContext->GetAndFlushUnderlyingGLErrors();
-    if (error) {
-        return mContext->GenerateWarning("texImage3D generated error %s", mContext->ErrorName(error));
+    if (error == LOCAL_GL_OUT_OF_MEMORY) {
+        mContext->ErrorOutOfMemory("%s: Ran out of memory during texture copy.",
+                                   funcName);
+        return;
     }
-
-    const bool isDataInitialized = bool(data);
-    const ImageInfo imageInfo(effectiveInternalFormat, width, height, depth,
-                              isDataInitialized);
-    SetImageInfoAt(texImageTarget, level, imageInfo);
-}
-
-void
-WebGLTexture::TexSubImage3D(TexImageTarget texImageTarget, 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* const out_rv)
-{
-    if (maybeView.IsNull())
-        return mContext->ErrorInvalidValue("texSubImage3D: pixels must not be null!");
-
-    const auto& view = maybeView.Value();
-    void* data;
-    size_t dataLength;
-    js::Scalar::Type jsArrayType;
-    ComputeLengthAndData(view, &data, &dataLength, &jsArrayType);
-
-    const char funcName[] = "texSubImage3D";
-    if (!DoesTargetMatchDimensions(mContext, texImageTarget, 3, funcName))
-        return;
-
-    const WebGLTexImageFunc func = WebGLTexImageFunc::TexSubImage;
-    const WebGLTexDimensions dims = WebGLTexDimensions::Tex3D;
-
-    WebGLTexture::ImageInfo& imageInfo = ImageInfoAt(texImageTarget, level);
-    if (!imageInfo.IsDefined()) {
-        return mContext->ErrorInvalidOperation("texSubImage3D: no previously defined texture image");
-    }
-
-    const TexInternalFormat existingEffectiveInternalFormat = imageInfo.mFormat;
-    TexInternalFormat existingUnsizedInternalFormat = LOCAL_GL_NONE;
-    TexType existingType = LOCAL_GL_NONE;
-    UnsizedInternalFormatAndTypeFromEffectiveInternalFormat(existingEffectiveInternalFormat,
-                                                            &existingUnsizedInternalFormat,
-                                                            &existingType);
-
-    if (!mContext->ValidateTexImage(texImageTarget, level, existingEffectiveInternalFormat.get(),
-                          xOffset, yOffset, zOffset,
-                          width, height, depth,
-                          0, unpackFormat, unpackType, func, dims))
-    {
+    if (error) {
+        MOZ_RELEASE_ASSERT(false, "We should have caught all other errors.");
+        mContext->GenerateWarning("%s: Unexpected error during texture copy. Context"
+                                  " lost.",
+                                  funcName);
+        mContext->ForceLoseContext();
         return;
     }
 
-    if (unpackType != existingType) {
-        return mContext->ErrorInvalidOperation("texSubImage3D: type differs from that of the existing image");
-    }
-
-    if (!mContext->ValidateTexInputData(unpackType, jsArrayType, func, dims))
-        return;
-
-    const size_t bitsPerTexel = GetBitsPerTexel(existingEffectiveInternalFormat);
-    MOZ_ASSERT((bitsPerTexel % 8) == 0); // should not have compressed formats here.
-    size_t srcTexelSize = bitsPerTexel / 8;
-
-    if (width == 0 || height == 0 || depth == 0)
-        return; // no effect, we better return right now
-
-    CheckedUint32 checked_neededByteLength =
-        mContext->GetImageSize(height, width, depth, srcTexelSize, mContext->mPixelStoreUnpackAlignment);
-
-    if (!checked_neededByteLength.isValid())
-        return mContext->ErrorInvalidOperation("texSubImage2D: integer overflow computing the needed buffer size");
-
-    uint32_t bytesNeeded = checked_neededByteLength.value();
-
-    if (dataLength < bytesNeeded)
-        return mContext->ErrorInvalidOperation("texSubImage2D: not enough data for operation (need %d, have %d)", bytesNeeded, dataLength);
+    ////////////////////////////////////
+    // Update our specification data?
 
-    if (!imageInfo.IsDataInitialized()) {
-        bool coversWholeImage = xOffset == 0 &&
-                                yOffset == 0 &&
-                                zOffset == 0 &&
-                                uint32_t(width) == imageInfo.mWidth &&
-                                uint32_t(height) == imageInfo.mHeight &&
-                                uint32_t(depth) == imageInfo.mDepth;
-        if (coversWholeImage) {
-            imageInfo.SetIsDataInitialized(true, this);
-        } else {
-            if (!EnsureInitializedImageData(texImageTarget, level))
-                return;
-        }
+    if (uploadWillInitialize) {
+        imageInfo->SetIsDataInitialized(true, this);
     }
-
-    GLenum driverUnpackType = LOCAL_GL_NONE;
-    GLenum driverInternalFormat = LOCAL_GL_NONE;
-    GLenum driverUnpackFormat = LOCAL_GL_NONE;
-    DriverFormatsFromEffectiveInternalFormat(mContext->gl,
-                                             existingEffectiveInternalFormat,
-                                             &driverInternalFormat,
-                                             &driverUnpackFormat,
-                                             &driverUnpackType);
-
-    mContext->MakeContextCurrent();
-    mContext->gl->fTexSubImage3D(texImageTarget.get(), level,
-                       xOffset, yOffset, zOffset,
-                       width, height, depth,
-                       driverUnpackFormat, driverUnpackType, data);
 }
 
-
 } // namespace mozilla
--- a/dom/canvas/WebGLTimerQuery.cpp
+++ b/dom/canvas/WebGLTimerQuery.cpp
@@ -45,17 +45,17 @@ WebGLTimerQuery::Delete()
 {
   mContext->MakeContextCurrent();
   mContext->gl->fDeleteQueries(1, &mGLName);
 }
 
 WebGLContext*
 WebGLTimerQuery::GetParentObject() const
 {
-  return Context();
+  return mContext;
 }
 
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLTimerQuery)
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLTimerQuery, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLTimerQuery, Release)
 
--- a/dom/canvas/WebGLTransformFeedback.cpp
+++ b/dom/canvas/WebGLTransformFeedback.cpp
@@ -36,17 +36,17 @@ WebGLTransformFeedback::Delete()
     mContext->MakeContextCurrent();
     mContext->gl->fDeleteTransformFeedbacks(1, &mGLName);
     removeFrom(mContext->mTransformFeedbacks);
 }
 
 WebGLContext*
 WebGLTransformFeedback::GetParentObject() const
 {
-    return Context();
+    return mContext;
 }
 
 JSObject*
 WebGLTransformFeedback::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto)
 {
     return dom::WebGLTransformFeedbackBinding::Wrap(cx, this, givenProto);
 }
 
--- a/dom/canvas/WebGLTypes.h
+++ b/dom/canvas/WebGLTypes.h
@@ -98,16 +98,17 @@ enum class WebGLTexelFormat : uint8_t {
     R32F, // OES_texture_float
     A32F, // OES_texture_float
     // 2-channel formats
     RA8,
     RA16F, // OES_texture_half_float
     RA32F, // OES_texture_float
     // 3-channel formats
     RGB8,
+    RGBX8, // used for DOM elements. Source format only.
     BGRX8, // used for DOM elements. Source format only.
     RGB565,
     RGB16F, // OES_texture_half_float
     RGB32F, // OES_texture_float
     // 4-channel formats
     RGBA8,
     BGRA8, // used for DOM elements
     RGBA5551,
--- a/dom/canvas/WebGLVertexArray.h
+++ b/dom/canvas/WebGLVertexArray.h
@@ -42,17 +42,17 @@ public:
         return HasAttrib(index) && mAttribs[index].enabled;
     }
 
     // Implement parent classes:
     void Delete();
     bool IsVertexArray();
 
     WebGLContext* GetParentObject() const {
-        return Context();
+        return mContext;
     }
 
     virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
 
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLVertexArray)
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLVertexArray)
 
     GLuint GLName() const { return mGLName; }
--- a/dom/canvas/moz.build
+++ b/dom/canvas/moz.build
@@ -48,18 +48,18 @@ UNIFIED_SOURCES += [
     'DocumentRendererParent.cpp',
     'ImageBitmap.cpp',
     'ImageData.cpp',
     'OffscreenCanvas.cpp',
 ]
 
 # WebGL Sources
 UNIFIED_SOURCES += [
-    'ElementTexSource.cpp',
     'MurmurHash3.cpp',
+    'TexUnpackBlob.cpp',
     'WebGL1Context.cpp',
     'WebGL1ContextBuffers.cpp',
     'WebGL1ContextUniforms.cpp',
     'WebGL2Context.cpp',
     'WebGL2ContextBuffers.cpp',
     'WebGL2ContextDraw.cpp',
     'WebGL2ContextFramebuffers.cpp',
     'WebGL2ContextMRTs.cpp',
--- a/dom/webidl/WebGL2RenderingContext.webidl
+++ b/dom/webidl/WebGL2RenderingContext.webidl
@@ -339,40 +339,50 @@ interface WebGL2RenderingContext : WebGL
     [Throws]
     any getInternalformatParameter(GLenum target, GLenum internalformat, GLenum pname);
     void renderbufferStorageMultisample(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height);
 
     /* Texture objects */
     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);
-    [Throws]
-    void texImage3D(GLenum target, GLint level, GLenum internalformat,
-                    GLsizei width, GLsizei height, GLsizei depth,
-                    GLint border, GLenum format,
+
+    void texImage3D(GLenum target, GLint level, GLenum internalformat, GLsizei width,
+                    GLsizei height, GLsizei depth, GLint border, GLenum format,
                     GLenum type, (ArrayBufferView or SharedArrayBufferView)? pixels);
-    [Throws] void texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset,
-                                GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type,
-                                (ArrayBufferView or SharedArrayBufferView)? pixels);
-    [Throws] void texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset,
-                                GLenum format, GLenum type, ImageData? data);
-    [Throws] void texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset,
-                                GLenum format, GLenum type, HTMLImageElement image);
-    [Throws] void texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset,
-                                GLenum format, GLenum type, HTMLCanvasElement canvas);
-    [Throws] void texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset,
-                                GLenum format, GLenum type, HTMLVideoElement video);
-    void copyTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset,
-                           GLint x, GLint y, GLsizei width, GLsizei height);
+    [Throws] // Can't actually throw.
+    void texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
+                       GLint zoffset, GLsizei width, GLsizei height, GLsizei depth,
+                       GLenum format, GLenum type,
+                       (ArrayBufferView or SharedArrayBufferView)? pixels);
+    [Throws] // Can't actually throw.
+    void texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
+                       GLint zoffset, GLenum format, GLenum type, ImageData? data);
+    [Throws]
+    void texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
+                       GLint zoffset, GLenum format, GLenum type, HTMLImageElement image);
+    [Throws]
+    void texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
+                       GLint zoffset, GLenum format, GLenum type,
+                       HTMLCanvasElement canvas);
+    [Throws]
+    void texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
+                       GLint zoffset, GLenum format, GLenum type, HTMLVideoElement video);
+
+    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, GLsizei imageSize, (ArrayBufferView or SharedArrayBufferView) data);
-    void compressedTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset,
-                                 GLsizei width, GLsizei height, GLsizei depth,
-                                 GLenum format, GLsizei imageSize, (ArrayBufferView or SharedArrayBufferView) data);
+                              GLsizei width, GLsizei height, GLsizei depth, GLint border,
+                              (ArrayBufferView or SharedArrayBufferView) data);
+    void compressedTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
+                                 GLint zoffset, GLsizei width, GLsizei height,
+                                 GLsizei depth, GLenum format,
+                                 (ArrayBufferView or SharedArrayBufferView) data);
 
     /* Programs and shaders */
     [WebGLHandlesContextLoss] GLint getFragDataLocation(WebGLProgram? program, DOMString name);
 
     /* Uniforms and attributes */
     void uniform1ui(WebGLUniformLocation? location, GLuint v0);
     void uniform2ui(WebGLUniformLocation? location, GLuint v0, GLuint v1);
     void uniform3ui(WebGLUniformLocation? location, GLuint v0, GLuint v1, GLuint v2);
--- a/dom/webidl/WebGLRenderingContext.webidl
+++ b/dom/webidl/WebGLRenderingContext.webidl
@@ -689,41 +689,42 @@ interface WebGLRenderingContext {
     void stencilFunc(GLenum func, GLint ref, GLuint mask);
     void stencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask);
     void stencilMask(GLuint mask);
     void stencilMaskSeparate(GLenum face, GLuint mask);
     void stencilOp(GLenum fail, GLenum zfail, GLenum zpass);
     void stencilOpSeparate(GLenum face, GLenum fail, GLenum zfail, GLenum zpass);
 
 
-    [Throws]
+    // Overloads must share [Throws].
+    [Throws] // Can't actually throw.
     void texImage2D(GLenum target, GLint level, GLenum internalformat,
                     GLsizei width, GLsizei height, GLint border, GLenum format,
                     GLenum type, (ArrayBufferView or SharedArrayBufferView)? pixels);
-    [Throws]
+    [Throws] // Can't actually throw.
     void texImage2D(GLenum target, GLint level, GLenum internalformat,
                     GLenum format, GLenum type, ImageData? pixels);
     [Throws]
     void texImage2D(GLenum target, GLint level, GLenum internalformat,
                     GLenum format, GLenum type, HTMLImageElement image); // May throw DOMException
     [Throws]
     void texImage2D(GLenum target, GLint level, GLenum internalformat,
                     GLenum format, GLenum type, HTMLCanvasElement canvas); // May throw DOMException
     [Throws]
     void texImage2D(GLenum target, GLint level, GLenum internalformat,
                     GLenum format, GLenum type, HTMLVideoElement video); // May throw DOMException
 
     void texParameterf(GLenum target, GLenum pname, GLfloat param);
     void texParameteri(GLenum target, GLenum pname, GLint param);
 
-    [Throws]
+    [Throws] // Can't actually throw.
     void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
-                       GLsizei width, GLsizei height,
-                       GLenum format, GLenum type, (ArrayBufferView or SharedArrayBufferView)? pixels);
-    [Throws]
+                       GLsizei width, GLsizei height, GLenum format, GLenum type,
+                       (ArrayBufferView or SharedArrayBufferView)? pixels);
+    [Throws] // Can't actually throw.
     void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
                        GLenum format, GLenum type, ImageData? pixels);
     [Throws]
     void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
                        GLenum format, GLenum type, HTMLImageElement image); // May throw DOMException
     [Throws]
     void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
                        GLenum format, GLenum type, HTMLCanvasElement canvas); // May throw DOMException
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -84,17 +84,17 @@ struct DrawOptions {
     , mCompositionOp(aCompositionOp)
     , mAntialiasMode(aAntialiasMode)
   {}
 
   Float mAlpha;                 /**< Alpha value by which the mask generated by this
                                      operation is multiplied. */
   CompositionOp mCompositionOp; /**< The operator that indicates how the source and
                                      destination patterns are blended. */
-  AntialiasMode mAntialiasMode; /**< The AntiAlias mode used for this drawing 
+  AntialiasMode mAntialiasMode; /**< The AntiAlias mode used for this drawing
                                      operation. */
 };
 
 /**
  * This structure is used to send stroke options that are used in stroking
  * operations.
  */
 struct StrokeOptions {
@@ -410,35 +410,35 @@ public:
 
     virtual ~ScopedMap()
     {
       if (mIsMapped) {
         mSurface->Unmap();
       }
     }
 
-    uint8_t* GetData()
+    uint8_t* GetData() const
     {
       MOZ_ASSERT(mIsMapped);
       return mMap.mData;
     }
 
-    int32_t GetStride()
+    int32_t GetStride() const
     {
       MOZ_ASSERT(mIsMapped);
       return mMap.mStride;
     }
 
-    MappedSurface* GetMappedSurface()
+    const MappedSurface* GetMappedSurface() const
     {
       MOZ_ASSERT(mIsMapped);
       return &mMap;
     }
 
-    bool IsMapped() { return mIsMapped; }
+    bool IsMapped() const { return mIsMapped; }
 
   private:
     RefPtr<DataSourceSurface> mSurface;
     MappedSurface mMap;
     bool mIsMapped;
   };
 
   virtual SurfaceType GetType() const override { return SurfaceType::DATA; }
@@ -522,17 +522,17 @@ class FlattenedPath;
 /** The path class is used to create (sets of) figures of any shape that can be
  * filled or stroked to a DrawTarget
  */
 class Path : public RefCounted<Path>
 {
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(Path)
   virtual ~Path();
-  
+
   virtual BackendType GetBackendType() const = 0;
 
   /** This returns a PathBuilder object that contains a copy of the contents of
    * this path and is still writable.
    */
   virtual already_AddRefed<PathBuilder> CopyToBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const = 0;
   virtual already_AddRefed<PathBuilder> TransformedCopyToBuilder(const Matrix &aTransform,
                                                              FillRule aFillRule = FillRule::FILL_WINDING) const = 0;
@@ -798,17 +798,17 @@ public:
    * @param aDest Destination point to copy the surface to
    */
   virtual void CopySurface(SourceSurface *aSurface,
                            const IntRect &aSourceRect,
                            const IntPoint &aDestination) = 0;
 
   /** @see CopySurface
    * Same as CopySurface, except uses itself as the source.
-   * 
+   *
    * Some backends may be able to optimize this better
    * than just taking a snapshot and using CopySurface.
    */
   virtual void CopyRect(const IntRect &aSourceRect,
                         const IntPoint &aDestination)
   {
     RefPtr<SourceSurface> source = Snapshot();
     CopySurface(source, aSourceRect, aDestination);
@@ -858,17 +858,17 @@ public:
    * @param aPattern Pattern that should be used for the stroke
    * @param aStrokeOptions Stroke options used for this operation
    * @param aOptions Draw options used for this operation
    */
   virtual void Stroke(const Path *aPath,
                       const Pattern &aPattern,
                       const StrokeOptions &aStrokeOptions = StrokeOptions(),
                       const DrawOptions &aOptions = DrawOptions()) = 0;
-  
+
   /**
    * Fill a path on the draw target with a certain source pattern.
    *
    * @param aPath Path that is to be filled
    * @param aPattern Pattern that should be used for the fill
    * @param aOptions Draw options used for this operation
    */
   virtual void Fill(const Path *aPath,
@@ -963,17 +963,17 @@ public:
    */
   virtual already_AddRefed<DrawTarget>
     CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const = 0;
 
   /**
    * Create a DrawTarget that captures the drawing commands and can be replayed
    * onto a compatible DrawTarget afterwards.
    *
-   * @param aSize Size of the area this DT will capture. 
+   * @param aSize Size of the area this DT will capture.
    */
   virtual already_AddRefed<DrawTargetCapture> CreateCaptureDT(const IntSize& aSize);
 
   /**
    * Create a draw target optimized for drawing a shadow.
    *
    * Note that aSigma is the blur radius that must be used when we draw the
    * shadow. Also note that this doesn't affect the size of the allocated
@@ -1165,17 +1165,17 @@ public:
 
   static already_AddRefed<DrawTarget> CreateDrawTargetForCairoSurface(cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat* aFormat = nullptr);
 
   static already_AddRefed<DrawTarget>
     CreateDrawTarget(BackendType aBackend, const IntSize &aSize, SurfaceFormat aFormat);
 
   static already_AddRefed<DrawTarget>
     CreateRecordingDrawTarget(DrawEventRecorder *aRecorder, DrawTarget *aDT);
-     
+
   static already_AddRefed<DrawTarget>
     CreateDrawTargetForData(BackendType aBackend, unsigned char* aData, const IntSize &aSize, int32_t aStride, SurfaceFormat aFormat);
 
   static already_AddRefed<ScaledFont>
     CreateScaledFontForNativeFont(const NativeFont &aNativeFont, Float aSize);
 
   /**
    * This creates a ScaledFont from TrueType data.
--- a/gfx/2d/DataSurfaceHelpers.cpp
+++ b/gfx/2d/DataSurfaceHelpers.cpp
@@ -12,17 +12,17 @@
 #include "mozilla/PodOperations.h"
 #include "Tools.h"
 
 namespace mozilla {
 namespace gfx {
 
 uint8_t*
 DataAtOffset(DataSourceSurface* aSurface,
-             DataSourceSurface::MappedSurface* aMap,
+             const DataSourceSurface::MappedSurface* aMap,
              IntPoint aPoint)
 {
   if (!SurfaceContainsPoint(aSurface, aPoint)) {
     MOZ_CRASH("sample position needs to be inside surface!");
   }
 
   MOZ_ASSERT(Factory::CheckSurfaceSize(aSurface->GetSize()),
              "surface size overflows - this should have been prevented when the surface was created");
--- a/gfx/2d/Factory.cpp
+++ b/gfx/2d/Factory.cpp
@@ -869,21 +869,18 @@ Factory::CreateWrappingDataSourceSurface
 {
   MOZ_ASSERT(aData);
   if (aSize.width <= 0 || aSize.height <= 0) {
     return nullptr;
   }
 
   RefPtr<SourceSurfaceRawData> newSurf = new SourceSurfaceRawData();
 
-  if (newSurf->InitWrappingData(aData, aSize, aStride, aFormat, false)) {
-    return newSurf.forget();
-  }
-
-  return nullptr;
+  newSurf->InitWrappingData(aData, aSize, aStride, aFormat, false);
+  return newSurf.forget();
 }
 
 already_AddRefed<DataSourceSurface>
 Factory::CreateDataSourceSurface(const IntSize &aSize,
                                  SurfaceFormat aFormat,
                                  bool aZero)
 {
   if (!AllowedSurfaceSize(aSize)) {
--- a/gfx/2d/SourceSurfaceRawData.cpp
+++ b/gfx/2d/SourceSurfaceRawData.cpp
@@ -7,30 +7,28 @@
 
 #include "DataSurfaceHelpers.h"
 #include "Logging.h"
 #include "mozilla/Types.h" // for decltype
 
 namespace mozilla {
 namespace gfx {
 
-bool
+void
 SourceSurfaceRawData::InitWrappingData(uint8_t *aData,
                                        const IntSize &aSize,
                                        int32_t aStride,
                                        SurfaceFormat aFormat,
                                        bool aOwnData)
 {
   mRawData = aData;
   mSize = aSize;
   mStride = aStride;
   mFormat = aFormat;
   mOwnData = aOwnData;
-
-  return true;
 }
 
 void
 SourceSurfaceRawData::GuaranteePersistance()
 {
   if (mOwnData) {
     return;
   }
--- a/gfx/2d/SourceSurfaceRawData.h
+++ b/gfx/2d/SourceSurfaceRawData.h
@@ -28,17 +28,17 @@ public:
 
   virtual uint8_t *GetData() override { return mRawData; }
   virtual int32_t Stride() override { return mStride; }
 
   virtual SurfaceType GetType() const override { return SurfaceType::DATA; }
   virtual IntSize GetSize() const override { return mSize; }
   virtual SurfaceFormat GetFormat() const override { return mFormat; }
 
-  bool InitWrappingData(unsigned char *aData,
+  void InitWrappingData(unsigned char *aData,
                         const IntSize &aSize,
                         int32_t aStride,
                         SurfaceFormat aFormat,
                         bool aOwnData);
 
   virtual void GuaranteePersistance() override;
 
   // Althought Map (and Moz2D in general) isn't normally threadsafe,
--- a/gfx/2d/moz.build
+++ b/gfx/2d/moz.build
@@ -37,16 +37,17 @@ EXPORTS.mozilla.gfx += [
     'PatternHelpers.h',
     'Point.h',
     'Quaternion.h',
     'Rect.h',
     'Scale.h',
     'ScaleFactor.h',
     'ScaleFactors2D.h',
     'SourceSurfaceCairo.h',
+    'SourceSurfaceRawData.h',
     'StackArray.h',
     'Tools.h',
     'Types.h',
     'UserData.h',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('cocoa', 'uikit'):
     EXPORTS.mozilla.gfx += [
--- a/gfx/gl/ScopedGLHelpers.cpp
+++ b/gfx/gl/ScopedGLHelpers.cpp
@@ -519,10 +519,41 @@ ScopedPackAlignment::UnwrapImpl() {
     // Check that we're not falling out of scope after the current context changed.
     MOZ_ASSERT(mGL->IsCurrent());
 
     if (mOldVal) {
         mGL->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, mOldVal);
     }
 }
 
+////////////////////////////////////////////////////////////////////////
+// ScopedUnpackAlignment
+
+ScopedUnpackAlignment::ScopedUnpackAlignment(GLContext* gl, GLint scopedVal)
+    : ScopedGLWrapper<ScopedUnpackAlignment>(gl)
+{
+    MOZ_ASSERT(scopedVal == 1 ||
+               scopedVal == 2 ||
+               scopedVal == 4 ||
+               scopedVal == 8);
+
+    gl->fGetIntegerv(LOCAL_GL_UNPACK_ALIGNMENT, &mOldVal);
+
+    if (scopedVal != mOldVal) {
+        gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, scopedVal);
+    } else {
+      // Don't try to re-set it during unwrap.
+        mOldVal = 0;
+    }
+}
+
+void
+ScopedUnpackAlignment::UnwrapImpl() {
+    // Check that we're not falling out of scope after the current context changed.
+    MOZ_ASSERT(mGL->IsCurrent());
+
+    if (mOldVal) {
+        mGL->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, mOldVal);
+    }
+}
+
 } /* namespace gl */
 } /* namespace mozilla */
--- a/gfx/gl/ScopedGLHelpers.h
+++ b/gfx/gl/ScopedGLHelpers.h
@@ -353,12 +353,29 @@ protected:
     GLint mOldVal;
 
 public:
     ScopedPackAlignment(GLContext* aGL, GLint scopedVal);
 
 protected:
     void UnwrapImpl();
 };
+
+
+struct ScopedUnpackAlignment
+    : public ScopedGLWrapper<ScopedUnpackAlignment>
+{
+    friend struct ScopedGLWrapper<ScopedUnpackAlignment>;
+
+protected:
+    GLint mOldVal;
+
+public:
+    ScopedUnpackAlignment(GLContext* gl, GLint scopedVal);
+
+protected:
+    void UnwrapImpl();
+};
+
 } /* namespace gl */
 } /* namespace mozilla */
 
 #endif /* SCOPEDGLHELPERS_H_ */