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