Bug 1072680 - Untangle the mixup of formats vs internalformats in the WebGL implementation - r=jgilbert
authorBenoit Jacob <bjacob@mozilla.com>
Sat, 04 Oct 2014 22:24:24 -0400
changeset 208832 9f6ad16ffec652fc1ec5dc2cb403b6026c3ce90b
parent 208831 a7655a08c13ac3142ccaab3a0964bc436baec632
child 208833 916f2837201cfa240febe707f2ba73f0c6e33cd1
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersjgilbert
bugs1072680
milestone35.0a1
Bug 1072680 - Untangle the mixup of formats vs internalformats in the WebGL implementation - r=jgilbert
dom/canvas/WebGLContext.h
dom/canvas/WebGLContextGL.cpp
dom/canvas/WebGLContextUtils.cpp
dom/canvas/WebGLContextValidate.cpp
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -12,16 +12,17 @@
 #include "mozilla/LinkedList.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/WeakPtr.h"
 
 #include "GLDefs.h"
 #include "WebGLActiveInfo.h"
 #include "WebGLObjectModel.h"
 #include "WebGLRenderbuffer.h"
+#include "WebGLTexture.h"
 #include "WebGLStrongTypes.h"
 #include <stdarg.h>
 
 #include "nsTArray.h"
 #include "nsCycleCollectionNoteChild.h"
 
 #include "nsIDOMWebGLRenderingContext.h"
 #include "nsICanvasRenderingContextInternal.h"
@@ -216,16 +217,17 @@ public:
 
     /**
      * Return displayable name for GLenum.
      * This version is like gl::GLenumToStr but with out the GL_ prefix to
      * keep consistency with how errors are reported from WebGL.
      */
     static const char *EnumName(GLenum glenum);
 
+    bool IsCompressedTextureFormat(GLenum format);
     bool IsTextureFormatCompressed(TexInternalFormat format);
 
     void DummyFramebufferOperation(const char *info);
 
     WebGLTexture* activeBoundTextureForTarget(const TexTarget texTarget) const {
         return texTarget == LOCAL_GL_TEXTURE_2D ? mBound2DTextures[mActiveTexture]
                                                 : mBoundCubeMapTextures[mActiveTexture];
     }
@@ -538,19 +540,22 @@ public:
                        const Nullable<dom::ArrayBufferView> &pixels,
                        ErrorResult& rv);
     void TexSubImage2D(GLenum target, GLint level,
                        GLint xoffset, GLint yoffset, GLenum format,
                        GLenum type, dom::ImageData* pixels, ErrorResult& rv);
     // Allow whatever element types the bindings are willing to pass
     // us in TexSubImage2D
     template<class ElementType>
-    void TexSubImage2D(GLenum rawTexImageTarget, GLint level,
-                       GLint xoffset, GLint yoffset, GLenum format,
-                       GLenum type, ElementType& elt, ErrorResult& rv)
+    void TexSubImage2D(GLenum rawTexImageTarget,
+                       GLint level,
+                       GLint xoffset, GLint yoffset,
+                       GLenum format,
+                       GLenum type,
+                       ElementType& elt, ErrorResult& rv)
     {
         if (IsContextLost())
             return;
 
         if (!ValidateTexImageTarget(2, rawTexImageTarget, WebGLTexImageFunc::TexSubImage))
             return ErrorInvalidEnumInfo("texSubImage2D: target", rawTexImageTarget);
 
         const TexImageTarget texImageTarget(rawTexImageTarget);
@@ -560,32 +565,41 @@ public:
 
         if (level < 0)
             return ErrorInvalidValue("texSubImage2D: level is negative");
 
         const int32_t maxLevel = MaxTextureLevelForTexImageTarget(texImageTarget);
         if (level > maxLevel)
             return ErrorInvalidValue("texSubImage2D: level %d is too large, max is %d", level, maxLevel);
 
+        WebGLTexture* tex = activeBoundTextureForTexImageTarget(texImageTarget);
+        if (!tex) {
+            return ErrorInvalidOperation("texSubImage2D: no texture bound on active texture unit");
+        }
+        const WebGLTexture::ImageInfo &imageInfo = tex->ImageInfoAt(texImageTarget, level);
+        const TexInternalFormat internalformat = imageInfo.InternalFormat();
+
         // Trying to handle the video by GPU directly first
-        if (TexImageFromVideoElement(texImageTarget, level, format, format, type, elt)) {
+        if (TexImageFromVideoElement(texImageTarget, level,
+                                     internalformat.get(), format, type, elt))
+        {
             return;
         }
 
         RefPtr<gfx::DataSourceSurface> data;
         WebGLTexelFormat srcFormat;
         nsLayoutUtils::SurfaceFromElementResult res = SurfaceFromElement(elt);
         rv = SurfaceFromElementResultToImageSurface(res, data,
                                                     &srcFormat);
         if (rv.Failed() || !data)
             return;
 
         gfx::IntSize size = data->GetSize();
         uint32_t byteLength = data->Stride() * size.height;
-        return TexSubImage2D_base(texImageTarget, level, xoffset, yoffset,
+        return TexSubImage2D_base(texImageTarget.get(), level, xoffset, yoffset,
                                   size.width, size.height,
                                   data->Stride(), format, type,
                                   data->GetData(), byteLength,
                                   -1, srcFormat, mPixelStorePremultiplyAlpha);
 
     }
 
     void Uniform1i(WebGLUniformLocation* location, GLint x);
@@ -1068,17 +1082,17 @@ protected:
 
     virtual bool IsWebGL2() const = 0;
 
     bool InitWebGL2();
 
 
     // -------------------------------------------------------------------------
     // Validation functions (implemented in WebGLContextValidate.cpp)
-    TexFormat BaseTexFormat(TexInternalFormat internalFormat) const;
+    TexInternalFormat BaseTexFormat(TexInternalFormat internalFormat) const;
 
     bool CreateOffscreenGL(bool forceEnabled);
     bool InitAndValidateGL();
     bool ResizeBackbuffer(uint32_t width, uint32_t height);
     bool ValidateBlendEquationEnum(GLenum cap, const char *info);
     bool ValidateBlendFuncDstEnum(GLenum mode, const char *info);
     bool ValidateBlendFuncSrcEnum(GLenum mode, const char *info);
     bool ValidateBlendFuncEnumsCompatibility(GLenum sfactor, GLenum dfactor, const char *info);
@@ -1090,63 +1104,74 @@ protected:
     bool ValidateDrawModeEnum(GLenum mode, const char *info);
     bool ValidateAttribIndex(GLuint index, const char *info);
     bool ValidateStencilParamsForDrawCall();
 
     bool ValidateGLSLVariableName(const nsAString& name, const char *info);
     bool ValidateGLSLCharacter(char16_t c);
     bool ValidateGLSLString(const nsAString& string, const char *info);
 
-    bool ValidateCopyTexImage(GLenum format, WebGLTexImageFunc func);
+    bool ValidateCopyTexImage(GLenum internalformat,
+                              WebGLTexImageFunc func);
     bool ValidateTexImage(GLuint dims, 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);
     bool ValidateTexImageTarget(GLuint dims, GLenum target, WebGLTexImageFunc func);
-    bool ValidateTexImageFormat(GLenum format, WebGLTexImageFunc func);
+    bool ValidateTexImageFormat(GLenum internalformat,
+                                WebGLTexImageFunc func);
     bool ValidateTexImageType(GLenum type, WebGLTexImageFunc func);
     bool ValidateTexImageFormatAndType(GLenum format, GLenum type, WebGLTexImageFunc func);
+    bool ValidateCompTexImageInternalFormat(GLenum format,
+                                            WebGLTexImageFunc func);
+    bool ValidateCopyTexImageInternalFormat(GLenum format,
+                                            WebGLTexImageFunc func);
     bool ValidateTexImageSize(TexImageTarget target, GLint level,
                               GLint width, GLint height, GLint depth,
                               WebGLTexImageFunc func);
     bool ValidateTexSubImageSize(GLint x, GLint y, GLint z,
                                  GLsizei width, GLsizei height, GLsizei depth,
                                  GLsizei baseWidth, GLsizei baseHeight, GLsizei baseDepth,
                                  WebGLTexImageFunc func);
 
-    bool ValidateCompTexImageSize(GLint level, GLenum format,
+    bool ValidateCompTexImageSize(GLint level,
+                                  GLenum internalformat,
                                   GLint xoffset, GLint yoffset,
                                   GLsizei width, GLsizei height,
                                   GLsizei levelWidth, GLsizei levelHeight,
                                   WebGLTexImageFunc func);
-    bool ValidateCompTexImageDataSize(GLint level, GLenum format,
+    bool ValidateCompTexImageDataSize(GLint level,
+                                      GLenum internalformat,
                                       GLsizei width, GLsizei height,
                                       uint32_t byteLength, WebGLTexImageFunc func);
 
-
     static uint32_t GetBitsPerTexel(TexInternalFormat format, TexType type);
 
     void Invalidate();
     void DestroyResourcesAndContext();
 
     void MakeContextCurrent() const;
 
     // helpers
-    void TexImage2D_base(TexImageTarget target, GLint level, GLenum internalformat,
+    void TexImage2D_base(TexImageTarget target,
+                         GLint level,
+                         GLenum internalformat,
                          GLsizei width, GLsizei height, GLsizei srcStrideOrZero, GLint border,
-                         GLenum format, GLenum type,
+                         GLenum format,
+                         GLenum type,
                          void *data, uint32_t byteLength,
                          int jsArrayType,
                          WebGLTexelFormat srcFormat, bool srcPremultiplied);
     void TexSubImage2D_base(TexImageTarget target, GLint level,
                             GLint xoffset, GLint yoffset,
                             GLsizei width, GLsizei height, GLsizei srcStrideOrZero,
-                            GLenum format, GLenum type,
+                            GLenum format,
+                            GLenum type,
                             void *pixels, uint32_t byteLength,
                             int jsArrayType,
                             WebGLTexelFormat srcFormat, bool srcPremultiplied);
     void TexParameter_base(GLenum target, GLenum pname,
                            GLint *intParamPtr, GLfloat *floatParamPtr);
 
     void ConvertImage(size_t width, size_t height, size_t srcStride, size_t dstStride,
                       const uint8_t* src, uint8_t *dst,
@@ -1222,22 +1247,22 @@ protected:
     GLenum CheckedBufferData(GLenum target,
                              GLsizeiptr size,
                              const GLvoid *data,
                              GLenum usage);
     /** like glTexImage2D but if the call may change the texture size, checks any GL error generated
      * by this glTexImage2D call and returns it */
     GLenum CheckedTexImage2D(TexImageTarget texImageTarget,
                              GLint level,
-                             GLenum internalFormat,
+                             TexInternalFormat internalFormat,
                              GLsizei width,
                              GLsizei height,
                              GLint border,
-                             GLenum format,
-                             GLenum type,
+                             TexFormat format,
+                             TexType type,
                              const GLvoid *data);
 
     void ForceLoseContext(bool simulateLosing = false);
     void ForceRestoreContext();
 
     nsTArray<WebGLRefPtr<WebGLTexture> > mBound2DTextures;
     nsTArray<WebGLRefPtr<WebGLTexture> > mBoundCubeMapTextures;
 
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -370,17 +370,18 @@ WebGLContext::CopyTexSubImage2D_base(Tex
     const char* info = sub ? "copyTexSubImage2D" : "copyTexImage2D";
     WebGLTexImageFunc func = sub ? WebGLTexImageFunc::CopyTexSubImage : WebGLTexImageFunc::CopyTexImage;
 
     // TODO: This changes with color_buffer_float. Reassess when the
     // patch lands.
     if (!ValidateTexImage(2, texImageTarget, level, internalformat,
                           xoffset, yoffset, 0,
                           width, height, 0,
-                          0, internalformat, LOCAL_GL_UNSIGNED_BYTE,
+                          0,
+                          LOCAL_GL_NONE, LOCAL_GL_NONE,
                           func))
     {
         return;
     }
 
     if (!ValidateCopyTexImage(internalformat, func))
         return;
 
@@ -473,65 +474,65 @@ WebGLContext::CopyTexImage2D(GLenum rawT
                              GLsizei height,
                              GLint border)
 {
     if (IsContextLost())
         return;
 
     // copyTexImage2D only generates textures with type = UNSIGNED_BYTE
     const WebGLTexImageFunc func = WebGLTexImageFunc::CopyTexImage;
-    const GLenum format = internalformat; // WebGL/ES Format
-    const GLenum type = LOCAL_GL_UNSIGNED_BYTE; // WebGL/ES Format
 
     if (!ValidateTexImageTarget(2, rawTexImgTarget, WebGLTexImageFunc::CopyTexImage))
         return;
 
-    if (!ValidateTexImage(2, rawTexImgTarget, level, format,
+    if (!ValidateTexImage(2, rawTexImgTarget, level, internalformat,
                           0, 0, 0,
                           width, height, 0,
-                          border, format, type,
+                          border, LOCAL_GL_NONE, LOCAL_GL_NONE,
                           func))
     {
         return;
     }
 
-    if (!ValidateCopyTexImage(format, func))
+    if (!ValidateCopyTexImage(internalformat, func))
         return;
 
     if (!mBoundFramebuffer)
         ClearBackbufferIfNeeded();
 
     const TexImageTarget texImageTarget(rawTexImgTarget);
 
     // check if the memory size of this texture may change with this call
     bool sizeMayChange = true;
     WebGLTexture* tex = activeBoundTextureForTexImageTarget(texImageTarget);
     if (tex->HasImageInfoAt(texImageTarget, level)) {
         const WebGLTexture::ImageInfo& imageInfo = tex->ImageInfoAt(texImageTarget, level);
 
         sizeMayChange = width != imageInfo.Width() ||
                         height != imageInfo.Height() ||
-                        internalformat != imageInfo.InternalFormat() ||
-                        type != imageInfo.Type();
+                        internalformat != imageInfo.InternalFormat();
     }
 
     if (sizeMayChange)
         GetAndFlushUnderlyingGLErrors();
 
-    CopyTexSubImage2D_base(texImageTarget, level, format, 0, 0, x, y, width, height, false);
+    CopyTexSubImage2D_base(texImageTarget, level, internalformat, 0, 0, x, y, width, height, false);
 
     if (sizeMayChange) {
         GLenum error = GetAndFlushUnderlyingGLErrors();
         if (error) {
             GenerateWarning("copyTexImage2D generated error %s", ErrorName(error));
             return;
         }
     }
 
-    tex->SetImageInfo(texImageTarget, level, width, height, internalformat, type,
+    tex->SetImageInfo(texImageTarget, level, width, height,
+                      internalformat,
+                      LOCAL_GL_UNSIGNED_BYTE, /* dummy, artifact of us storing
+                                               the wrong data in ImageInfo */
                       WebGLImageDataStatus::InitializedImageData);
 }
 
 void
 WebGLContext::CopyTexSubImage2D(GLenum rawTexImgTarget,
                                 GLint level,
                                 GLint xoffset,
                                 GLint yoffset,
@@ -573,17 +574,17 @@ WebGLContext::CopyTexSubImage2D(GLenum r
 
     WebGLTexture *tex = activeBoundTextureForTexImageTarget(texImageTarget);
     if (!tex)
         return ErrorInvalidOperation("copyTexSubImage2D: no texture bound to this target");
 
     if (!tex->HasImageInfoAt(texImageTarget, level))
         return ErrorInvalidOperation("copyTexSubImage2D: no texture image previously defined for this level and face");
 
-    const WebGLTexture::ImageInfo &imageInfo = tex->ImageInfoAt(texImageTarget, level);
+    const WebGLTexture::ImageInfo& imageInfo = tex->ImageInfoAt(texImageTarget, level);
     GLsizei texWidth = imageInfo.Width();
     GLsizei texHeight = imageInfo.Height();
 
     if (xoffset + width > texWidth || xoffset + width < 0)
       return ErrorInvalidValue("copyTexSubImage2D: xoffset+width is too large");
 
     if (yoffset + height > texHeight || yoffset + height < 0)
       return ErrorInvalidValue("copyTexSubImage2D: yoffset+height is too large");
@@ -3293,31 +3294,34 @@ WebGLContext::CompileShader(WebGLShader 
 
     gl->fCompileShader(shadername);
     GLint ok;
     gl->fGetShaderiv(shadername, LOCAL_GL_COMPILE_STATUS, &ok);
     shader->SetCompileStatus(ok);
 }
 
 void
-WebGLContext::CompressedTexImage2D(GLenum rawTexImgTarget, GLint level, GLenum internalformat,
+WebGLContext::CompressedTexImage2D(GLenum rawTexImgTarget,
+                                   GLint level,
+                                   GLenum internalformat,
                                    GLsizei width, GLsizei height, GLint border,
                                    const ArrayBufferView& view)
 {
     if (IsContextLost())
         return;
 
     const WebGLTexImageFunc func = WebGLTexImageFunc::CompTexImage;
 
     if (!ValidateTexImageTarget(2, rawTexImgTarget, WebGLTexImageFunc::CompTexImage))
         return;
 
     if (!ValidateTexImage(2, rawTexImgTarget, level, internalformat,
                           0, 0, 0, width, height, 0,
-                          border, internalformat, LOCAL_GL_UNSIGNED_BYTE,
+                          border, LOCAL_GL_NONE,
+                          LOCAL_GL_NONE,
                           func))
     {
         return;
     }
 
     view.ComputeLengthAndData();
 
     uint32_t byteLength = view.Length();
@@ -3338,62 +3342,67 @@ WebGLContext::CompressedTexImage2D(GLenu
     MOZ_ASSERT(tex);
     tex->SetImageInfo(texImageTarget, level, width, height, internalformat, LOCAL_GL_UNSIGNED_BYTE,
                       WebGLImageDataStatus::InitializedImageData);
 }
 
 void
 WebGLContext::CompressedTexSubImage2D(GLenum rawTexImgTarget, GLint level, GLint xoffset,
                                       GLint yoffset, GLsizei width, GLsizei height,
-                                      GLenum format, const ArrayBufferView& view)
+                                      GLenum internalformat,
+                                      const ArrayBufferView& view)
 {
     if (IsContextLost())
         return;
 
     const WebGLTexImageFunc func = WebGLTexImageFunc::CompTexSubImage;
 
     if (!ValidateTexImageTarget(2, rawTexImgTarget, WebGLTexImageFunc::CompTexSubImage))
         return;
 
     if (!ValidateTexImage(2, rawTexImgTarget,
-                          level, format,
+                          level, internalformat,
                           xoffset, yoffset, 0,
                           width, height, 0,
-                          0, format, LOCAL_GL_UNSIGNED_BYTE,
+                          0, LOCAL_GL_NONE, LOCAL_GL_NONE,
                           func))
     {
         return;
     }
 
     const TexImageTarget texImageTarget(rawTexImgTarget);
 
     WebGLTexture *tex = activeBoundTextureForTexImageTarget(texImageTarget);
     MOZ_ASSERT(tex);
     WebGLTexture::ImageInfo& levelInfo = tex->ImageInfoAt(texImageTarget, level);
 
+    if (internalformat != levelInfo.InternalFormat()) {
+        return ErrorInvalidOperation("compressedTexImage2D: internalformat does not match the existing image");
+    }
+
     view.ComputeLengthAndData();
 
     uint32_t byteLength = view.Length();
-    if (!ValidateCompTexImageDataSize(level, format, width, height, byteLength, func))
+    if (!ValidateCompTexImageDataSize(level, internalformat, width, height, byteLength, func))
         return;
 
-    if (!ValidateCompTexImageSize(level, format,
+    if (!ValidateCompTexImageSize(level, internalformat,
                                   xoffset, yoffset,
                                   width, height,
                                   levelInfo.Width(), levelInfo.Height(),
                                   func))
     {
         return;
     }
 
     if (levelInfo.HasUninitializedImageData())
         tex->DoDeferredImageInitialization(texImageTarget, level);
 
     MakeContextCurrent();
-    gl->fCompressedTexSubImage2D(texImageTarget.get(), level, xoffset, yoffset, width, height, format, byteLength, view.Data());
+    gl->fCompressedTexSubImage2D(texImageTarget.get(), level, xoffset, yoffset, width, height, internalformat, byteLength, view.Data());
 }
 
 JS::Value
 WebGLContext::GetShaderParameter(WebGLShader *shader, GLenum pname)
 {
     if (IsContextLost())
         return JS::NullValue();
 
@@ -3572,63 +3581,63 @@ WebGLContext::GetShaderTranslatedSource(
     if (!ValidateObject("getShaderTranslatedSource: shader", shader))
         return;
 
     retval.Assign(shader->TranslatedSource());
 }
 
 GLenum WebGLContext::CheckedTexImage2D(TexImageTarget texImageTarget,
                                        GLint level,
-                                       GLenum internalFormat,
+                                       TexInternalFormat internalFormat,
                                        GLsizei width,
                                        GLsizei height,
                                        GLint border,
-                                       GLenum format,
-                                       GLenum type,
+                                       TexFormat format,
+                                       TexType type,
                                        const GLvoid *data)
 {
-    MOZ_ASSERT(internalFormat == format);
     WebGLTexture *tex = activeBoundTextureForTexImageTarget(texImageTarget);
     MOZ_ASSERT(tex != nullptr, "no texture bound");
 
     bool sizeMayChange = true;
 
     if (tex->HasImageInfoAt(texImageTarget, level)) {
         const WebGLTexture::ImageInfo& imageInfo = tex->ImageInfoAt(texImageTarget, level);
         sizeMayChange = width != imageInfo.Width() ||
                         height != imageInfo.Height() ||
-                        format != imageInfo.InternalFormat() ||
-                        type != imageInfo.Type();
+                        internalFormat != imageInfo.InternalFormat();
     }
 
     // Convert to format and type required by OpenGL 'driver'.
     GLenum driverType = DriverTypeFromType(gl, type);
     GLenum driverInternalFormat = LOCAL_GL_NONE;
     GLenum driverFormat = LOCAL_GL_NONE;
-    DriverFormatsFromFormatAndType(gl, format, type, &driverInternalFormat, &driverFormat);
+    DriverFormatsFromFormatAndType(gl, internalFormat, type, &driverInternalFormat, &driverFormat);
 
     if (sizeMayChange) {
         GetAndFlushUnderlyingGLErrors();
     }
 
     gl->fTexImage2D(texImageTarget.get(), level, driverInternalFormat, width, height, border, driverFormat, driverType, data);
 
     GLenum error = LOCAL_GL_NO_ERROR;
     if (sizeMayChange) {
         error = GetAndFlushUnderlyingGLErrors();
     }
 
     return error;
 }
 
 void
-WebGLContext::TexImage2D_base(TexImageTarget texImageTarget, GLint level, GLenum internalformat,
+WebGLContext::TexImage2D_base(TexImageTarget texImageTarget, GLint level,
+                              GLenum internalformat,
                               GLsizei width, GLsizei height, GLsizei srcStrideOrZero,
                               GLint border,
-                              GLenum format, GLenum type,
+                              GLenum format,
+                              GLenum type,
                               void* data, uint32_t byteLength,
                               int jsArrayType, // a TypedArray format enum, or -1 if not relevant
                               WebGLTexelFormat srcFormat, bool srcPremultiplied)
 {
     const WebGLTexImageFunc func = WebGLTexImageFunc::TexImage;
 
     if (!ValidateTexImage(2, texImageTarget, level, internalformat,
                           0, 0, 0,
@@ -3647,17 +3656,17 @@ WebGLContext::TexImage2D_base(TexImageTa
                                          "with format of DEPTH_COMPONENT or DEPTH_STENCIL, "
                                          "data must be nullptr, "
                                          "level must be zero");
     }
 
     if (!ValidateTexInputData(type, jsArrayType, func))
         return;
 
-    WebGLTexelFormat dstFormat = GetWebGLTexelFormat(format, type);
+    WebGLTexelFormat dstFormat = GetWebGLTexelFormat(internalformat, type);
     WebGLTexelFormat actualSrcFormat = srcFormat == WebGLTexelFormat::Auto ? dstFormat : srcFormat;
 
     uint32_t srcTexelSize = WebGLTexelConversions::TexelBytesForFormat(actualSrcFormat);
 
     CheckedUint32 checked_neededByteLength =
         GetImageSize(height, width, srcTexelSize, mPixelStoreUnpackAlignment);
 
     CheckedUint32 checked_plainRowSize = CheckedUint32(width) * srcTexelSize;
@@ -3681,17 +3690,17 @@ WebGLContext::TexImage2D_base(TexImageTa
     MakeContextCurrent();
 
     nsAutoArrayPtr<uint8_t> convertedData;
     void* pixels = nullptr;
     WebGLImageDataStatus imageInfoStatusIfSuccess = WebGLImageDataStatus::UninitializedImageData;
 
     if (byteLength) {
         size_t   srcStride = srcStrideOrZero ? srcStrideOrZero : checked_alignedRowSize.value();
-        uint32_t dstTexelSize = GetBitsPerTexel(format, type) / 8;
+        uint32_t dstTexelSize = GetBitsPerTexel(internalformat, type) / 8;
         size_t   dstPlainRowSize = dstTexelSize * width;
         size_t   unpackAlignment = mPixelStoreUnpackAlignment;
         size_t   dstStride = ((dstPlainRowSize + unpackAlignment-1) / unpackAlignment) * unpackAlignment;
 
         if (actualSrcFormat == dstFormat &&
             srcPremultiplied == mPixelStorePremultiplyAlpha &&
             srcStride == dstStride &&
             !mPixelStoreFlipY)
@@ -3798,28 +3807,39 @@ WebGLContext::TexSubImage2D_base(TexImag
                                  GLsizei width, GLsizei height, GLsizei srcStrideOrZero,
                                  GLenum format, GLenum type,
                                  void* data, uint32_t byteLength,
                                  int jsArrayType,
                                  WebGLTexelFormat srcFormat, bool srcPremultiplied)
 {
     const WebGLTexImageFunc func = WebGLTexImageFunc::TexSubImage;
 
-    if (!ValidateTexImage(2, texImageTarget, level, format,
+    WebGLTexture *tex = activeBoundTextureForTexImageTarget(texImageTarget);
+    if (!tex) {
+        return ErrorInvalidOperation("texSubImage2D: no texture bound on active texture unit");
+    }
+    const WebGLTexture::ImageInfo& imageInfo = tex->ImageInfoAt(texImageTarget, level);
+    const TexInternalFormat internalformat =  imageInfo.InternalFormat();
+
+    if (!ValidateTexImage(2, texImageTarget, level, internalformat.get(),
                           xoffset, yoffset, 0,
                           width, height, 0,
                           0, format, type, func))
     {
         return;
     }
 
     if (!ValidateTexInputData(type, jsArrayType, func))
         return;
 
-    WebGLTexelFormat dstFormat = GetWebGLTexelFormat(format, type);
+    if (imageInfo.Type() != type) {
+        return ErrorInvalidOperation("texSubImage2D: type parameter does not match the existing image");
+    }
+
+    WebGLTexelFormat dstFormat = GetWebGLTexelFormat(internalformat, type);
     WebGLTexelFormat actualSrcFormat = srcFormat == WebGLTexelFormat::Auto ? dstFormat : srcFormat;
 
     uint32_t srcTexelSize = WebGLTexelConversions::TexelBytesForFormat(actualSrcFormat);
 
     if (width == 0 || height == 0)
         return; // ES 2.0 says it has no effect, we better return right now
 
     CheckedUint32 checked_neededByteLength =
@@ -3833,26 +3853,23 @@ WebGLContext::TexSubImage2D_base(TexImag
     if (!checked_neededByteLength.isValid())
         return ErrorInvalidOperation("texSubImage2D: integer overflow computing the needed buffer size");
 
     uint32_t bytesNeeded = checked_neededByteLength.value();
 
     if (byteLength < bytesNeeded)
         return ErrorInvalidOperation("texSubImage2D: not enough data for operation (need %d, have %d)", bytesNeeded, byteLength);
 
-    WebGLTexture *tex = activeBoundTextureForTexImageTarget(texImageTarget);
-    const WebGLTexture::ImageInfo &imageInfo = tex->ImageInfoAt(texImageTarget, level);
-
     if (imageInfo.HasUninitializedImageData())
         tex->DoDeferredImageInitialization(texImageTarget, level);
 
     MakeContextCurrent();
 
     size_t   srcStride = srcStrideOrZero ? srcStrideOrZero : checked_alignedRowSize.value();
-    uint32_t dstTexelSize = GetBitsPerTexel(format, type) / 8;
+    uint32_t dstTexelSize = GetBitsPerTexel(internalformat, type) / 8;
     size_t   dstPlainRowSize = dstTexelSize * width;
     // There are checks above to ensure that this won't overflow.
     size_t   dstStride = RoundedToNextMultipleOf(dstPlainRowSize, mPixelStoreUnpackAlignment).value();
 
     void* pixels = data;
     nsAutoArrayPtr<uint8_t> convertedData;
 
     // no conversion, no flipping, so we avoid copying anything and just pass the source pointer
@@ -3869,17 +3886,17 @@ WebGLContext::TexSubImage2D_base(TexImag
                     actualSrcFormat, srcPremultiplied,
                     dstFormat, mPixelStorePremultiplyAlpha, dstTexelSize);
         pixels = reinterpret_cast<void*>(convertedData.get());
     }
 
     GLenum driverType = DriverTypeFromType(gl, type);
     GLenum driverInternalFormat = LOCAL_GL_NONE;
     GLenum driverFormat = LOCAL_GL_NONE;
-    DriverFormatsFromFormatAndType(gl, format, type, &driverInternalFormat, &driverFormat);
+    DriverFormatsFromFormatAndType(gl, internalformat, type, &driverInternalFormat, &driverFormat);
 
     gl->fTexSubImage2D(texImageTarget.get(), level, xoffset, yoffset, width, height, driverFormat, driverType, pixels);
 }
 
 void
 WebGLContext::TexSubImage2D(GLenum rawTarget, GLint level,
                             GLint xoffset, GLint yoffset,
                             GLsizei width, GLsizei height,
--- a/dom/canvas/WebGLContextUtils.cpp
+++ b/dom/canvas/WebGLContextUtils.cpp
@@ -485,21 +485,20 @@ WebGLContext::EnumName(GLenum glenum)
         XX(UNSIGNED_SHORT_5_5_5_1);
         XX(UNSIGNED_SHORT_5_6_5);
 #undef XX
     }
 
     return "[Unknown enum name]";
 }
 
-
 bool
-WebGLContext::IsTextureFormatCompressed(TexInternalFormat format)
+WebGLContext::IsCompressedTextureFormat(GLenum format)
 {
-    switch (format.get()) {
+    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_ATC_RGB:
         case LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA:
         case LOCAL_GL_ATC_RGBA_INTERPOLATED_ALPHA:
         case LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1:
@@ -508,16 +507,23 @@ WebGLContext::IsTextureFormatCompressed(
         case LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1:
         case LOCAL_GL_ETC1_RGB8_OES:
             return true;
         default:
             return false;
     }
 }
 
+
+bool
+WebGLContext::IsTextureFormatCompressed(TexInternalFormat format)
+{
+    return IsCompressedTextureFormat(format.get());
+}
+
 GLenum
 WebGLContext::GetAndFlushUnderlyingGLErrors()
 {
     // Get and clear GL error in ALL cases.
     GLenum error = gl->GetAndClearError();
 
     // Only store in mUnderlyingGLError if is hasn't already recorded an
     // error.
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -77,16 +77,23 @@ InfoFrom(WebGLTexImageFunc func)
     case WebGLTexImageFunc::CompTexImage:    return "compressedTexImage2D";
     case WebGLTexImageFunc::CompTexSubImage: return "compressedTexSubImage2D";
     default:
         MOZ_ASSERT(false, "Missing case for WebGLTexImageSource");
         return "(error)";
     }
 }
 
+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)
 {
     const char* name = WebGLContext::EnumName(glenum);
@@ -243,90 +250,90 @@ WebGLProgram::UpdateInfo()
 }
 
 /**
  * Return the simple base format for a given internal format.
  *
  * \return the corresponding \u base internal format (GL_ALPHA, GL_LUMINANCE,
  * GL_LUMINANCE_ALPHA, GL_RGB, GL_RGBA), or GL_NONE if invalid enum.
  */
-TexFormat
+TexInternalFormat
 WebGLContext::BaseTexFormat(TexInternalFormat internalFormat) const
 {
     if (internalFormat == LOCAL_GL_ALPHA ||
         internalFormat == LOCAL_GL_LUMINANCE ||
         internalFormat == LOCAL_GL_LUMINANCE_ALPHA ||
         internalFormat == LOCAL_GL_RGB ||
         internalFormat == LOCAL_GL_RGBA)
     {
-        return TexFormat(internalFormat.get());
+        return internalFormat;
     }
 
     if (IsExtensionEnabled(WebGLExtensionID::EXT_sRGB)) {
         if (internalFormat == LOCAL_GL_SRGB)
-            return TexFormat(LOCAL_GL_RGB);
+            return LOCAL_GL_RGB;
 
         if (internalFormat == LOCAL_GL_SRGB_ALPHA)
-            return TexFormat(LOCAL_GL_RGBA);
+            return LOCAL_GL_RGBA;
     }
 
     if (IsExtensionEnabled(WebGLExtensionID::WEBGL_compressed_texture_atc)) {
         if (internalFormat == LOCAL_GL_ATC_RGB)
-            return TexFormat(LOCAL_GL_RGB);
+            return LOCAL_GL_RGB;
 
         if (internalFormat == LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA ||
             internalFormat == LOCAL_GL_ATC_RGBA_INTERPOLATED_ALPHA)
         {
-            return TexFormat(LOCAL_GL_RGBA);
+            return LOCAL_GL_RGBA;
         }
     }
 
     if (IsExtensionEnabled(WebGLExtensionID::WEBGL_compressed_texture_etc1)) {
         if (internalFormat == LOCAL_GL_ETC1_RGB8_OES)
-            return TexFormat(LOCAL_GL_RGB);
+            return LOCAL_GL_RGB;
     }
 
     if (IsExtensionEnabled(WebGLExtensionID::WEBGL_compressed_texture_pvrtc)) {
         if (internalFormat == LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1 ||
             internalFormat == LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1)
         {
-            return TexFormat(LOCAL_GL_RGB);
+            return LOCAL_GL_RGB;
         }
 
         if (internalFormat == LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1 ||
             internalFormat == LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1)
         {
-            return TexFormat(LOCAL_GL_RGBA);
+            return LOCAL_GL_RGBA;
         }
     }
 
     if (IsExtensionEnabled(WebGLExtensionID::WEBGL_compressed_texture_s3tc)) {
         if (internalFormat == LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT)
-            return TexFormat(LOCAL_GL_RGB);
+            return LOCAL_GL_RGB;
 
         if (internalFormat == LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT ||
             internalFormat == LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT ||
             internalFormat == LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT)
         {
-            return TexFormat(LOCAL_GL_RGBA);
+            return LOCAL_GL_RGBA;
         }
     }
 
     if (IsExtensionEnabled(WebGLExtensionID::WEBGL_depth_texture)) {
         if (internalFormat == LOCAL_GL_DEPTH_COMPONENT ||
             internalFormat == LOCAL_GL_DEPTH_COMPONENT16 ||
             internalFormat == LOCAL_GL_DEPTH_COMPONENT32)
         {
-            return TexFormat(LOCAL_GL_DEPTH_COMPONENT);
+            return LOCAL_GL_DEPTH_COMPONENT;
         }
 
         if (internalFormat == LOCAL_GL_DEPTH_STENCIL ||
             internalFormat == LOCAL_GL_DEPTH24_STENCIL8)
         {
-            return TexFormat(LOCAL_GL_DEPTH_STENCIL);
+            return LOCAL_GL_DEPTH_STENCIL;
         }
     }
 
     MOZ_ASSERT(false, "Unhandled internalFormat");
     return LOCAL_GL_NONE;
 }
 
 bool WebGLContext::ValidateBlendEquationEnum(GLenum mode, const char *info)
@@ -603,73 +610,28 @@ WebGLContext::ValidateTexImageFormat(GLe
     {
         bool validFormat = IsExtensionEnabled(WebGLExtensionID::EXT_sRGB);
         if (!validFormat)
             ErrorInvalidEnum("%s: invalid format %s: need EXT_sRGB enabled",
                              InfoFrom(func), WebGLContext::EnumName(format));
         return validFormat;
     }
 
-    /* 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)
-    {
-        bool validFormat = IsExtensionEnabled(WebGLExtensionID::WEBGL_compressed_texture_atc);
-        if (!validFormat)
-            ErrorInvalidEnum("%s: invalid format %s: need WEBGL_compressed_texture_atc enabled",
-                             InfoFrom(func), WebGLContext::EnumName(format));
-        return validFormat;
-    }
-
-    // WEBGL_compressed_texture_etc1
-    if (format == LOCAL_GL_ETC1_RGB8_OES) {
-        bool validFormat = IsExtensionEnabled(WebGLExtensionID::WEBGL_compressed_texture_etc1);
-        if (!validFormat)
-            ErrorInvalidEnum("%s: invalid format %s: need WEBGL_compressed_texture_etc1 enabled",
-                             InfoFrom(func), WebGLContext::EnumName(format));
-        return validFormat;
-    }
-
-
-    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)
-    {
-        bool validFormat = IsExtensionEnabled(WebGLExtensionID::WEBGL_compressed_texture_pvrtc);
-        if (!validFormat)
-            ErrorInvalidEnum("%s: invalid format %s: need WEBGL_compressed_texture_pvrtc enabled",
-                             InfoFrom(func), WebGLContext::EnumName(format));
-        return validFormat;
-    }
-
-
-    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)
-    {
-        bool validFormat = IsExtensionEnabled(WebGLExtensionID::WEBGL_compressed_texture_s3tc);
-        if (!validFormat)
-            ErrorInvalidEnum("%s: invalid format %s: need WEBGL_compressed_texture_s3tc enabled",
-                             InfoFrom(func), WebGLContext::EnumName(format));
-        return validFormat;
-    }
-
     ErrorInvalidEnumWithName(this, "invalid format", format, func);
 
     return false;
 }
 
 /**
  * Check if the given texture target is valid for TexImage.
  */
 bool
-WebGLContext::ValidateTexImageTarget(GLuint dims, GLenum target, WebGLTexImageFunc func)
+WebGLContext::ValidateTexImageTarget(GLuint dims,
+                                     GLenum target,
+                                     WebGLTexImageFunc func)
 {
     switch (dims) {
     case 2:
         if (target == LOCAL_GL_TEXTURE_2D ||
             IsTexImageCubemapTarget(target))
         {
             return true;
         }
@@ -684,17 +646,18 @@ WebGLContext::ValidateTexImageTarget(GLu
     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)
+WebGLContext::ValidateTexImageType(GLenum type,
+                                   WebGLTexImageFunc func)
 {
     /* 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;
@@ -735,17 +698,18 @@ WebGLContext::ValidateTexImageType(GLenu
 }
 
 /**
  * Validate texture image sizing extra constraints for
  * CompressedTex(Sub)?Image.
  */
 // TODO: WebGL 2
 bool
-WebGLContext::ValidateCompTexImageSize(GLint level, GLenum format,
+WebGLContext::ValidateCompTexImageSize(GLint level,
+                                       GLenum format,
                                        GLint xoffset, GLint yoffset,
                                        GLsizei width, GLsizei height,
                                        GLsizei levelWidth, GLsizei levelHeight,
                                        WebGLTexImageFunc func)
 {
     // Negative parameters must already have been handled above
     MOZ_ASSERT(xoffset >= 0 && yoffset >= 0 &&
                width >= 0 && height >= 0);
@@ -1150,18 +1114,24 @@ WebGLContext::GetBitsPerTexel(TexInterna
     return 0;
 }
 
 /**
  * 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)
+WebGLContext::ValidateTexImageFormatAndType(GLenum format,
+                                            GLenum type, WebGLTexImageFunc func)
 {
+    if (IsCompressedFunc(func) || IsCopyFunc(func))
+    {
+        MOZ_ASSERT(type == LOCAL_GL_NONE && format == LOCAL_GL_NONE);
+        return true;
+    }
     if (!ValidateTexImageFormat(format, func) ||
         !ValidateTexImageType(type, func))
     {
         return false;
     }
 
     bool validCombo = false;
 
@@ -1198,46 +1168,114 @@ WebGLContext::ValidateTexImageFormatAndT
         validCombo = (type == LOCAL_GL_UNSIGNED_SHORT ||
                       type == LOCAL_GL_UNSIGNED_INT);
         break;
 
     case LOCAL_GL_DEPTH_STENCIL:
         validCombo = (type == LOCAL_GL_UNSIGNED_INT_24_8);
         break;
 
-    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:
-    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:
-    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:
-        validCombo = (type == LOCAL_GL_UNSIGNED_BYTE);
-        break;
-
     default:
         // Only valid formats should be passed to the switch stmt.
         MOZ_ASSERT(false, "Unexpected format and type combo. How'd this happen?");
         validCombo = false;
         // Fall through to return an InvalidOperations. This will alert us to the
         // unexpected case that needs fixing in builds without asserts.
     }
 
     if (!validCombo)
         ErrorInvalidOperation("%s: invalid combination of format %s and type %s",
                               InfoFrom(func), WebGLContext::EnumName(format), WebGLContext::EnumName(type));
 
     return validCombo;
 }
 
+bool
+WebGLContext::ValidateCompTexImageInternalFormat(GLenum format,
+                                                 WebGLTexImageFunc func)
+{
+    if (!IsCompressedTextureFormat(format)) {
+        ErrorInvalidEnum("%s: invalid compressed texture format: %s",
+                         InfoFrom(func), 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)
+    {
+        bool validFormat = IsExtensionEnabled(WebGLExtensionID::WEBGL_compressed_texture_atc);
+        if (!validFormat)
+            ErrorInvalidEnum("%s: invalid format %s: need WEBGL_compressed_texture_atc enabled",
+                             InfoFrom(func), WebGLContext::EnumName(format));
+        return validFormat;
+    }
+
+    // WEBGL_compressed_texture_etc1
+    if (format == LOCAL_GL_ETC1_RGB8_OES) {
+        bool validFormat = IsExtensionEnabled(WebGLExtensionID::WEBGL_compressed_texture_etc1);
+        if (!validFormat)
+            ErrorInvalidEnum("%s: invalid format %s: need WEBGL_compressed_texture_etc1 enabled",
+                             InfoFrom(func), WebGLContext::EnumName(format));
+        return validFormat;
+    }
+
+
+    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)
+    {
+        bool validFormat = IsExtensionEnabled(WebGLExtensionID::WEBGL_compressed_texture_pvrtc);
+        if (!validFormat)
+            ErrorInvalidEnum("%s: invalid format %s: need WEBGL_compressed_texture_pvrtc enabled",
+                             InfoFrom(func), WebGLContext::EnumName(format));
+        return validFormat;
+    }
+
+
+    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)
+    {
+        bool validFormat = IsExtensionEnabled(WebGLExtensionID::WEBGL_compressed_texture_s3tc);
+        if (!validFormat)
+            ErrorInvalidEnum("%s: invalid format %s: need WEBGL_compressed_texture_s3tc enabled",
+                             InfoFrom(func), WebGLContext::EnumName(format));
+        return validFormat;
+    }
+
+    return false;
+}
+
+bool
+WebGLContext::ValidateCopyTexImageInternalFormat(GLenum format,
+                                                 WebGLTexImageFunc func)
+{
+    bool valid = format == LOCAL_GL_RGBA ||
+                 format == LOCAL_GL_RGB ||
+                 format == LOCAL_GL_LUMINANCE_ALPHA ||
+                 format == LOCAL_GL_LUMINANCE ||
+                 format == LOCAL_GL_ALPHA;
+    if (!valid)
+    {
+        // in CopyTexImage, the internalformat is a function parameter,
+        // so a bad value is an INVALID_ENUM error.
+        // in CopyTexSubImage, the internalformat is part of existing state,
+        // so this is an INVALID_OPERATION error.
+        GenerateWarning("%s: invalid texture internal format: %s",
+                        InfoFrom(func), WebGLContext::EnumName(format));
+        SynthesizeGLError(func == WebGLTexImageFunc::CopyTexImage
+                          ? LOCAL_GL_INVALID_ENUM
+                          : LOCAL_GL_INVALID_OPERATION);
+    }
+    return valid;
+}
 /**
  * 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
@@ -1282,17 +1320,18 @@ WebGLContext::ValidateTexInputData(GLenu
 
 /**
  * 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)
+WebGLContext::ValidateCopyTexImage(GLenum format,
+                                   WebGLTexImageFunc func)
 {
     MOZ_ASSERT(IsCopyFunc(func));
 
     // Default framebuffer format
     GLenum fboFormat = bool(gl->GetPixelFormat().alpha > 0) ? LOCAL_GL_RGBA : LOCAL_GL_RGB;
 
     if (mBoundFramebuffer) {
         if (!mBoundFramebuffer->CheckAndInitializeAttachments()) {
@@ -1327,20 +1366,23 @@ WebGLContext::ValidateCopyTexImage(GLenu
 
 /**
  * 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(GLuint dims, TexImageTarget texImageTarget,
-                               GLint level, GLenum internalFormat,
+                               GLint level,
+                               GLenum internalFormat,
                                GLint xoffset, GLint yoffset, GLint zoffset,
                                GLint width, GLint height, GLint depth,
-                               GLint border, GLenum format, GLenum type,
+                               GLint border,
+                               GLenum format,
+                               GLenum type,
                                WebGLTexImageFunc func)
 {
     const char* info = InfoFrom(func);
 
     /* Check level */
     if (level < 0) {
         ErrorInvalidValue("%s: level must be >= 0", info);
         return false;
@@ -1351,23 +1393,31 @@ WebGLContext::ValidateTexImage(GLuint di
         ErrorInvalidValue("%s: border must be 0", info);
         return false;
     }
 
     /* Check incoming image format and type */
     if (!ValidateTexImageFormatAndType(format, type, func))
         return false;
 
-    /* WebGL and OpenGL ES 2.0 impose additional restrictions on the
-     * combinations of format, internalFormat, and type that can be
-     * used.  Formats and types that require additional extensions
-     * (e.g., GL_FLOAT requires GL_OES_texture_float) are filtered
-     * elsewhere.
-     */
-    if (format != internalFormat) {
+    if (IsCompressedFunc(func)) {
+        if (!ValidateCompTexImageInternalFormat(internalFormat, func)) {
+            return false;
+        }
+    } else if (IsCopyFunc(func)) {
+        if (!ValidateCopyTexImageInternalFormat(internalFormat, func)) {
+            return false;
+        }
+    } else if (format != internalFormat) {
+        /* WebGL and OpenGL ES 2.0 impose additional restrictions on the
+         * combinations of format, internalFormat, and type that can be
+         * used.  Formats and types that require additional extensions
+         * (e.g., GL_FLOAT requires GL_OES_texture_float) are filtered
+         * elsewhere.
+         */
         ErrorInvalidOperation("%s: format does not match internalformat", info);
         return false;
     }
 
     /* check internalFormat */
     // TODO: Not sure if this is a bit of over kill.
     if (BaseTexFormat(internalFormat) == LOCAL_GL_NONE) {
         MOZ_ASSERT(false);
@@ -1401,41 +1451,30 @@ WebGLContext::ValidateTexImage(GLuint di
         const WebGLTexture::ImageInfo& imageInfo = tex->ImageInfoAt(texImageTarget, level);
         if (!ValidateTexSubImageSize(xoffset, yoffset, zoffset,
                                      width, height, depth,
                                      imageInfo.Width(), imageInfo.Height(), 0,
                                      func))
         {
             return false;
         }
-
-        /* Require the format and type to match that of the existing
-         * texture as created
-         */
-        if (imageInfo.InternalFormat() != internalFormat ||
-            imageInfo.Type() != type)
-        {
-            ErrorInvalidOperation("%s: format or type doesn't match the existing texture",
-                                  info);
-            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(format, func)) {
+    if (!IsAllowedFromSource(internalFormat, func)) {
         ErrorInvalidOperation("%s: Invalid format %s for this operation",
                               info, WebGLContext::EnumName(format));
         return false;
     }
 
     /* Parameters are OK */
     return true;
 }