Bug 966624 - Refactor Tex Image checks. r=jgilbert
authorDan Glastonbury <dglastonbury@mozilla.com>
Thu, 20 Feb 2014 20:20:28 -0500
changeset 170142 063362390b6e4010d6a418edd6925ffe588d5f5f
parent 170141 128cbf1edc402482d096b0acc820549b0a7ebe68
child 170143 6deadaf3af446ef63e47be9711a86ad0cdd6f3f4
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewersjgilbert
bugs966624
milestone30.0a1
Bug 966624 - Refactor Tex Image checks. r=jgilbert
content/canvas/src/WebGLContext.cpp
content/canvas/src/WebGLContext.h
content/canvas/src/WebGLContextGL.cpp
content/canvas/src/WebGLContextValidate.cpp
content/canvas/src/WebGLTypes.h
content/canvas/test/webgl/conformance/more/functions/texImage2DBadArgs.html
--- a/content/canvas/src/WebGLContext.cpp
+++ b/content/canvas/src/WebGLContext.cpp
@@ -1215,20 +1215,18 @@ WebGLContext::PresentScreenBuffer()
 
     return true;
 }
 
 void
 WebGLContext::DummyFramebufferOperation(const char *info)
 {
     GLenum status = CheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
-    if (status == LOCAL_GL_FRAMEBUFFER_COMPLETE)
-        return;
-    else
-        return ErrorInvalidFramebufferOperation("%s: incomplete framebuffer", info);
+    if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE)
+        ErrorInvalidFramebufferOperation("%s: incomplete framebuffer", info);
 }
 
 // We use this timer for many things. Here are the things that it is activated for:
 // 1) If a script is using the MOZ_WEBGL_lose_context extension.
 // 2) If we are using EGL and _NOT ANGLE_, we query periodically to see if the
 //    CONTEXT_LOST_WEBGL error has been triggered.
 // 3) If we are using ANGLE, or anything that supports ARB_robustness, query the
 //    GPU periodically to see if the reset status bit has been set.
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -939,38 +939,63 @@ protected:
 
     virtual bool IsWebGL2() const = 0;
 
     bool InitWebGL2();
 
 
     // -------------------------------------------------------------------------
     // Validation functions (implemented in WebGLContextValidate.cpp)
+    GLenum BaseTexFormat(GLenum internalFormat) const;
+
     bool InitAndValidateGL();
     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);
     bool ValidateTextureTargetEnum(GLenum target, const char *info);
     bool ValidateComparisonEnum(GLenum target, const char *info);
     bool ValidateStencilOpEnum(GLenum action, const char *info);
     bool ValidateFaceEnum(GLenum face, const char *info);
-    bool ValidateTexFormatAndType(GLenum format, GLenum type, int jsArrayType,
-                                      uint32_t *texelSize, const char *info);
+    bool ValidateTexInputData(GLenum type, int jsArrayType, WebGLTexImageFunc func);
     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 ValidateTexImage2DFormat(GLenum format, const char* info);
-    bool ValidateTexImage2DTarget(GLenum target, GLsizei width, GLsizei height, const char* info);
-    bool ValidateCompressedTextureSize(GLenum target, GLint level, GLenum format, GLsizei width, GLsizei height, uint32_t byteLength, const char* info);
-    bool ValidateLevelWidthHeightForTarget(GLenum target, GLint level, GLsizei width, GLsizei height, const char* info);
+
+    bool ValidateTexImage(GLuint dims, GLenum target,
+                          GLint level, GLint 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 ValidateTexImageType(GLenum type, WebGLTexImageFunc func);
+    bool ValidateTexImageFormatAndType(GLenum format, GLenum type, WebGLTexImageFunc func);
+    bool ValidateTexImageSize(GLenum 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(GLenum target, GLint level, GLenum format,
+                                  GLint xoffset, GLint yoffset,
+                                  GLsizei width, GLsizei height,
+                                  GLsizei levelWidth, GLsizei levelHeight,
+                                  WebGLTexImageFunc func);
+    bool ValidateCompTexImageDataSize(GLint level, GLenum format,
+                                      GLsizei width, GLsizei height,
+                                      uint32_t byteLength, WebGLTexImageFunc func);
+
 
     static uint32_t GetBitsPerTexel(GLenum format, GLenum type);
 
     void Invalidate();
     void DestroyResourcesAndContext();
 
     void MakeContextCurrent() const;
 
@@ -1047,17 +1072,21 @@ protected:
 private:
     // Like ValidateObject, but only for cases when aObject is known
     // to not be null already.
     template<class ObjectType>
     bool ValidateObjectAssumeNonNull(const char* info, ObjectType *aObject);
 
 protected:
     int32_t MaxTextureSizeForTarget(GLenum target) const {
-        return target == LOCAL_GL_TEXTURE_2D ? mGLMaxTextureSize : mGLMaxCubeMapTextureSize;
+        MOZ_ASSERT(target == LOCAL_GL_TEXTURE_2D ||
+                   (target >= LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X &&
+                    target <= LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z),
+                   "Invalid target enum");
+        return (target == LOCAL_GL_TEXTURE_2D) ? mGLMaxTextureSize : mGLMaxCubeMapTextureSize;
     }
 
     /** 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);
--- a/content/canvas/src/WebGLContextGL.cpp
+++ b/content/canvas/src/WebGLContextGL.cpp
@@ -384,19 +384,27 @@ WebGLContext::CopyTexSubImage2D_base(GLe
                                      GLsizei width,
                                      GLsizei height,
                                      bool sub)
 {
     const WebGLRectangleObject* framebufferRect = CurValidFBRectObject();
     GLsizei framebufferWidth = framebufferRect ? framebufferRect->Width() : 0;
     GLsizei framebufferHeight = framebufferRect ? framebufferRect->Height() : 0;
 
-    const char *info = sub ? "copyTexSubImage2D" : "copyTexImage2D";
-
-    if (!ValidateLevelWidthHeightForTarget(target, level, width, height, info)) {
+    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, target, level, internalformat,
+                          xoffset, yoffset, 0,
+                          width, height, 0,
+                          0, internalformat, LOCAL_GL_UNSIGNED_BYTE,
+                          func))
+    {
         return;
     }
 
     MakeContextCurrent();
 
     WebGLTexture *tex = activeBoundTextureForTarget(target);
 
     if (!tex)
@@ -410,34 +418,35 @@ WebGLContext::CopyTexSubImage2D_base(GLe
     } else {
 
         // the rect doesn't fit in the framebuffer
 
         /*** first, we initialize the texture as black ***/
 
         // first, compute the size of the buffer we should allocate to initialize the texture as black
 
-        uint32_t texelSize = 0;
-        if (!ValidateTexFormatAndType(internalformat, LOCAL_GL_UNSIGNED_BYTE, -1, &texelSize, info))
+        if (!ValidateTexInputData(LOCAL_GL_UNSIGNED_BYTE, -1, func))
             return;
 
+        uint32_t texelSize = GetBitsPerTexel(internalformat, LOCAL_GL_UNSIGNED_BYTE) / 8;
+
         CheckedUint32 checked_neededByteLength =
             GetImageSize(height, width, texelSize, mPixelStoreUnpackAlignment);
 
         if (!checked_neededByteLength.isValid())
             return ErrorInvalidOperation("%s: integer overflow computing the needed buffer size", info);
 
         uint32_t bytesNeeded = checked_neededByteLength.value();
 
         // now that the size is known, create the buffer
 
         // We need some zero pages, because GL doesn't guarantee the
         // contents of a texture allocated with nullptr data.
         // Hopefully calloc will just mmap zero pages here.
-        void *tempZeroData = calloc(1, bytesNeeded);
+        void* tempZeroData = calloc(1, bytesNeeded);
         if (!tempZeroData)
             return ErrorOutOfMemory("%s: could not allocate %d bytes (for zero fill)", info, bytesNeeded);
 
         // now initialize the texture as black
 
         if (sub)
             gl->fTexSubImage2D(target, level, 0, 0, width, height,
                                internalformat, LOCAL_GL_UNSIGNED_BYTE, tempZeroData);
@@ -481,106 +490,65 @@ WebGLContext::CopyTexImage2D(GLenum targ
                              GLint y,
                              GLsizei width,
                              GLsizei height,
                              GLint border)
 {
     if (IsContextLost())
         return;
 
-    switch (target) {
-        case LOCAL_GL_TEXTURE_2D:
-        case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X:
-        case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
-        case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
-        case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
-        case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
-        case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
-            break;
-        default:
-            return ErrorInvalidEnumInfo("copyTexImage2D: target", target);
-    }
-
-
-    switch (internalformat) {
-        case LOCAL_GL_RGB:
-        case LOCAL_GL_LUMINANCE:
-        case LOCAL_GL_RGBA:
-        case LOCAL_GL_ALPHA:
-        case LOCAL_GL_LUMINANCE_ALPHA:
-            break;
-        default:
-            return ErrorInvalidEnumInfo("copyTexImage2D: internal format", internalformat);
+    // copyTexImage2D only generates textures with type = UNSIGNED_BYTE
+    const WebGLTexImageFunc func = WebGLTexImageFunc::CopyTexImage;
+    GLenum type = LOCAL_GL_UNSIGNED_BYTE;
+
+    if (!ValidateTexImage(2, target, level, internalformat,
+                          0, 0, 0,
+                          width, height, 0,
+                          border, internalformat, type,
+                          func))
+    {
+        return;
     }
 
-    if (border != 0)
-        return ErrorInvalidValue("copyTexImage2D: border must be 0");
-
-    if (width < 0 || height < 0)
-        return ErrorInvalidValue("copyTexImage2D: width and height may not be negative");
-
-    if (level < 0)
-        return ErrorInvalidValue("copyTexImage2D: level may not be negative");
-
-    GLsizei maxTextureSize = MaxTextureSizeForTarget(target);
-    if (!(maxTextureSize >> level))
-        return ErrorInvalidValue("copyTexImage2D: 2^level exceeds maximum texture size");
-
-    if (level >= 1) {
-        if (!(is_pot_assuming_nonnegative(width) &&
-              is_pot_assuming_nonnegative(height)))
-            return ErrorInvalidValue("copyTexImage2D: with level > 0, width and height must be powers of two");
-    }
-
-    if (internalformat == LOCAL_GL_DEPTH_COMPONENT ||
-        internalformat == LOCAL_GL_DEPTH_STENCIL)
-        return ErrorInvalidOperation("copyTexImage2D: a base internal format of DEPTH_COMPONENT or DEPTH_STENCIL isn't supported");
-
-    WebGLTexture *tex = activeBoundTextureForTarget(target);
-    if (!tex)
-        return ErrorInvalidOperation("copyTexImage2D: no texture bound to this target");
-    
-    if (mBoundFramebuffer)
-        if (!mBoundFramebuffer->CheckAndInitializeAttachments())
-            return ErrorInvalidFramebufferOperation("copyTexImage2D: incomplete framebuffer");
+    if (mBoundFramebuffer && !mBoundFramebuffer->CheckAndInitializeAttachments())
+        return ErrorInvalidFramebufferOperation("copyTexImage2D: incomplete framebuffer");
 
     bool texFormatRequiresAlpha = internalformat == LOCAL_GL_RGBA ||
-                                    internalformat == LOCAL_GL_ALPHA ||
-                                    internalformat == LOCAL_GL_LUMINANCE_ALPHA;
+                                  internalformat == LOCAL_GL_ALPHA ||
+                                  internalformat == LOCAL_GL_LUMINANCE_ALPHA;
     bool fboFormatHasAlpha = mBoundFramebuffer ? mBoundFramebuffer->ColorAttachment(0).HasAlpha()
                                                : bool(gl->GetPixelFormat().alpha > 0);
     if (texFormatRequiresAlpha && !fboFormatHasAlpha)
         return ErrorInvalidOperation("copyTexImage2D: texture format requires an alpha channel "
                                      "but the framebuffer doesn't have one");
 
-    // copyTexImage2D only generates textures with type = UNSIGNED_BYTE
-    GLenum type = LOCAL_GL_UNSIGNED_BYTE;
-
     // check if the memory size of this texture may change with this call
     bool sizeMayChange = true;
+    WebGLTexture* tex = activeBoundTextureForTarget(target);
     if (tex->HasImageInfoAt(target, level)) {
         const WebGLTexture::ImageInfo& imageInfo = tex->ImageInfoAt(target, level);
 
         sizeMayChange = width != imageInfo.Width() ||
                         height != imageInfo.Height() ||
                         internalformat != imageInfo.InternalFormat() ||
                         type != imageInfo.Type();
     }
 
-    if (sizeMayChange) {
+    if (sizeMayChange)
         UpdateWebGLErrorAndClearGLError();
-        CopyTexSubImage2D_base(target, level, internalformat, 0, 0, x, y, width, height, false);
+
+    CopyTexSubImage2D_base(target, level, internalformat, 0, 0, x, y, width, height, false);
+
+    if (sizeMayChange) {
         GLenum error = LOCAL_GL_NO_ERROR;
         UpdateWebGLErrorAndClearGLError(&error);
         if (error) {
             GenerateWarning("copyTexImage2D generated error %s", ErrorName(error));
             return;
         }
-    } else {
-        CopyTexSubImage2D_base(target, level, internalformat, 0, 0, x, y, width, height, false);
     }
 
     tex->SetImageInfo(target, level, width, height, internalformat, type,
                       WebGLImageDataStatus::InitializedImageData);
 }
 
 void
 WebGLContext::CopyTexSubImage2D(GLenum target,
@@ -3351,154 +3319,86 @@ WebGLContext::CompileShader(WebGLShader 
     }
 }
 
 void
 WebGLContext::CompressedTexImage2D(GLenum target, GLint level, GLenum internalformat,
                                    GLsizei width, GLsizei height, GLint border,
                                    const ArrayBufferView& view)
 {
-    if (IsContextLost()) {
-        return;
-    }
-
-    if (!ValidateTexImage2DTarget(target, width, height, "compressedTexImage2D")) {
+    if (IsContextLost())
         return;
-    }
-
-    WebGLTexture *tex = activeBoundTextureForTarget(target);
-    if (!tex) {
-        ErrorInvalidOperation("compressedTexImage2D: no texture is bound to this target");
-        return;
-    }
-
-    if (!mCompressedTextureFormats.Contains(internalformat)) {
-        ErrorInvalidEnum("compressedTexImage2D: compressed texture format 0x%x is not supported", internalformat);
-        return;
-    }
-
-    if (border) {
-        ErrorInvalidValue("compressedTexImage2D: border is not 0");
+
+    const WebGLTexImageFunc func = WebGLTexImageFunc::CompTexImage;
+
+    if (!ValidateTexImage(2, target, level, internalformat,
+                          0, 0, 0, width, height, 0,
+                          border, internalformat, LOCAL_GL_UNSIGNED_BYTE,
+                          func))
+    {
         return;
     }
 
     uint32_t byteLength = view.Length();
-    if (!ValidateCompressedTextureSize(target, level, internalformat, width, height, byteLength, "compressedTexImage2D")) {
+    if (!ValidateCompTexImageDataSize(target, internalformat, width, height, byteLength, func)) {
         return;
     }
 
     MakeContextCurrent();
     gl->fCompressedTexImage2D(target, level, internalformat, width, height, border, byteLength, view.Data());
+    WebGLTexture* tex = activeBoundTextureForTarget(target);
+    MOZ_ASSERT(tex);
     tex->SetImageInfo(target, level, width, height, internalformat, LOCAL_GL_UNSIGNED_BYTE,
                       WebGLImageDataStatus::InitializedImageData);
 
     ReattachTextureToAnyFramebufferToWorkAroundBugs(tex, level);
 }
 
 void
 WebGLContext::CompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset,
                                       GLint yoffset, GLsizei width, GLsizei height,
                                       GLenum format, const ArrayBufferView& view)
 {
-    if (IsContextLost()) {
+    if (IsContextLost())
         return;
-    }
-
-    switch (target) {
-        case LOCAL_GL_TEXTURE_2D:
-        case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X:
-        case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
-        case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
-        case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
-        case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
-        case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
-            break;
-        default:
-            return ErrorInvalidEnumInfo("texSubImage2D: target", target);
+
+    const WebGLTexImageFunc func = WebGLTexImageFunc::CompTexSubImage;
+
+    if (!ValidateTexImage(2, target,
+                          level, format,
+                          xoffset, yoffset, 0,
+                          width, height, 0,
+                          0, format, LOCAL_GL_UNSIGNED_BYTE,
+                          func))
+    {
+        return;
     }
 
     WebGLTexture *tex = activeBoundTextureForTarget(target);
-    if (!tex) {
-        ErrorInvalidOperation("compressedTexSubImage2D: no texture is bound to this target");
-        return;
-    }
-
-    if (!mCompressedTextureFormats.Contains(format)) {
-        ErrorInvalidEnum("compressedTexSubImage2D: compressed texture format 0x%x is not supported", format);
-        return;
-    }
-
-    if (!ValidateLevelWidthHeightForTarget(target, level, width, height, "compressedTexSubImage2D")) {
+    MOZ_ASSERT(tex);
+    WebGLTexture::ImageInfo& levelInfo = tex->ImageInfoAt(target, level);
+
+    if (!ValidateCompTexImageSize(target, level, format,
+                                  xoffset, yoffset,
+                                  width, height,
+                                  levelInfo.Width(), levelInfo.Height(),
+                                  func))
+    {
         return;
     }
 
     uint32_t byteLength = view.Length();
-    if (!ValidateCompressedTextureSize(target, level, format, width, height, byteLength, "compressedTexSubImage2D")) {
-        return;
-    }
-
-    if (!tex->HasImageInfoAt(target, level)) {
-        ErrorInvalidOperation("compressedTexSubImage2D: no texture image previously defined for this level and face");
-        return;
-    }
-
-    const WebGLTexture::ImageInfo &imageInfo = tex->ImageInfoAt(target, level);
-
-    if (!CanvasUtils::CheckSaneSubrectSize(xoffset, yoffset, width, height, imageInfo.Width(), imageInfo.Height())) {
-        ErrorInvalidValue("compressedTexSubImage2D: subtexture rectangle out of bounds");
+    if (!ValidateCompTexImageDataSize(target, format, width, height, byteLength, func))
         return;
-    }
-
-    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:
-        {
-            if (xoffset < 0 || xoffset % 4 != 0) {
-                ErrorInvalidOperation("compressedTexSubImage2D: xoffset is not a multiple of 4");
-                return;
-            }
-            if (yoffset < 0 || yoffset % 4 != 0) {
-                ErrorInvalidOperation("compressedTexSubImage2D: yoffset is not a multiple of 4");
-                return;
-            }
-            if (width % 4 != 0 && width != imageInfo.Width()) {
-                ErrorInvalidOperation("compressedTexSubImage2D: width is not a multiple of 4 or equal to texture width");
-                return;
-            }
-            if (height % 4 != 0 && height != imageInfo.Height()) {
-                ErrorInvalidOperation("compressedTexSubImage2D: height is not a multiple of 4 or equal to texture height");
-                return;
-            }
-            break;
-        }
-        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 (xoffset || yoffset ||
-                width != imageInfo.Width() ||
-                height != imageInfo.Height())
-            {
-                ErrorInvalidValue("compressedTexSubImage2D: the update rectangle doesn't match the existing image");
-                return;
-            }
-        }
-    }
-
-    if (imageInfo.HasUninitializedImageData()) {
+
+    if (levelInfo.HasUninitializedImageData())
         tex->DoDeferredImageInitialization(target, level);
-    }
 
     MakeContextCurrent();
     gl->fCompressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, byteLength, view.Data());
-
-    return;
 }
 
 JS::Value
 WebGLContext::GetShaderParameter(WebGLShader *shader, GLenum pname)
 {
     if (IsContextLost())
         return JS::NullValue();
 
@@ -3728,58 +3628,38 @@ void
 WebGLContext::TexImage2D_base(GLenum target, GLint level, GLenum internalformat,
                               GLsizei width, GLsizei height, GLsizei srcStrideOrZero,
                               GLint border,
                               GLenum format, GLenum type,
                               void *data, uint32_t byteLength,
                               int jsArrayType, // a TypedArray format enum, or -1 if not relevant
                               WebGLTexelFormat srcFormat, bool srcPremultiplied)
 {
-    if (!ValidateTexImage2DTarget(target, width, height, "texImage2D")) {
+    const WebGLTexImageFunc func = WebGLTexImageFunc::TexImage;
+
+    if (!ValidateTexImage(2, target, level, internalformat,
+                          0, 0, 0,
+                          width, height, 0,
+                          border, format, type, func))
+    {
         return;
     }
 
-    if (!ValidateTexImage2DFormat(format, "texImage2D: format"))
-        return;
-
-    if (format != internalformat)
-        return ErrorInvalidOperation("texImage2D: format does not match internalformat");
-
-    if (!ValidateLevelWidthHeightForTarget(target, level, width, height, "texImage2D")) {
-        return;
-    }
-
-    if (level >= 1) {
-        if (!(is_pot_assuming_nonnegative(width) &&
-              is_pot_assuming_nonnegative(height)))
-            return ErrorInvalidValue("texImage2D: with level > 0, width and height must be powers of two");
-    }
-
-    if (border != 0)
-        return ErrorInvalidValue("texImage2D: border must be 0");
-
     const bool isDepthTexture = format == LOCAL_GL_DEPTH_COMPONENT ||
                                 format == LOCAL_GL_DEPTH_STENCIL;
 
     if (isDepthTexture) {
-        if (IsExtensionEnabled(WEBGL_depth_texture)) {
-            if (target != LOCAL_GL_TEXTURE_2D || data != nullptr || level != 0)
-                return ErrorInvalidOperation("texImage2D: "
-                                             "with format of DEPTH_COMPONENT or DEPTH_STENCIL, "
-                                             "target must be TEXTURE_2D, "
-                                             "data must be nullptr, "
-                                             "level must be zero"); // Haiku by unknown Zen master
-        } else {
-            return ErrorInvalidEnum("texImage2D: attempt to create a depth texture "
-                                    "without having enabled the WEBGL_depth_texture extension.");
-        }
+        if (data != nullptr || level != 0)
+            return ErrorInvalidOperation("texImage2D: "
+                                         "with format of DEPTH_COMPONENT or DEPTH_STENCIL, "
+                                         "data must be nullptr, "
+                                         "level must be zero");
     }
 
-    uint32_t dstTexelSize = 0;
-    if (!ValidateTexFormatAndType(format, type, jsArrayType, &dstTexelSize, "texImage2D"))
+    if (!ValidateTexInputData(type, jsArrayType, func))
         return;
 
     WebGLTexelFormat dstFormat = GetWebGLTexelFormat(format, type);
     WebGLTexelFormat actualSrcFormat = srcFormat == WebGLTexelFormat::Auto ? dstFormat : srcFormat;
 
     uint32_t srcTexelSize = WebGLTexelConversions::TexelBytesForFormat(actualSrcFormat);
 
     CheckedUint32 checked_neededByteLength =
@@ -3829,16 +3709,17 @@ WebGLContext::TexImage2D_base(GLenum tar
 
     GLenum error = LOCAL_GL_NO_ERROR;
 
     WebGLImageDataStatus imageInfoStatusIfSuccess = WebGLImageDataStatus::NoImageData;
 
     if (byteLength) {
         size_t srcStride = srcStrideOrZero ? srcStrideOrZero : checked_alignedRowSize.value();
 
+        uint32_t dstTexelSize = GetBitsPerTexel(format, 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)
@@ -3921,46 +3802,27 @@ void
 WebGLContext::TexSubImage2D_base(GLenum target, GLint level,
                                  GLint xoffset, GLint yoffset,
                                  GLsizei width, GLsizei height, GLsizei srcStrideOrZero,
                                  GLenum format, GLenum type,
                                  void *pixels, uint32_t byteLength,
                                  int jsArrayType,
                                  WebGLTexelFormat srcFormat, bool srcPremultiplied)
 {
-    switch (target) {
-        case LOCAL_GL_TEXTURE_2D:
-        case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X:
-        case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
-        case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
-        case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
-        case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
-        case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
-            break;
-        default:
-            return ErrorInvalidEnumInfo("texSubImage2D: target", target);
-    }
-
-    if (!ValidateLevelWidthHeightForTarget(target, level, width, height, "texSubImage2D")) {
+    const WebGLTexImageFunc func = WebGLTexImageFunc::TexSubImage;
+
+    if (!ValidateTexImage(2, target, level, format,
+                          xoffset, yoffset, 0,
+                          width, height, 0,
+                          0, format, type, func))
+    {
         return;
     }
 
-    if (level >= 1) {
-        if (!(is_pot_assuming_nonnegative(width) &&
-              is_pot_assuming_nonnegative(height)))
-            return ErrorInvalidValue("texSubImage2D: with level > 0, width and height must be powers of two");
-    }
-
-    if (IsExtensionEnabled(WEBGL_depth_texture) &&
-        (format == LOCAL_GL_DEPTH_COMPONENT || format == LOCAL_GL_DEPTH_STENCIL)) {
-        return ErrorInvalidOperation("texSubImage2D: format");
-    }
-
-    uint32_t dstTexelSize = 0;
-    if (!ValidateTexFormatAndType(format, type, jsArrayType, &dstTexelSize, "texSubImage2D"))
+    if (!ValidateTexInputData(type, jsArrayType, func))
         return;
 
     WebGLTexelFormat dstFormat = GetWebGLTexelFormat(format, type);
     WebGLTexelFormat actualSrcFormat = srcFormat == WebGLTexelFormat::Auto ? dstFormat : srcFormat;
 
     uint32_t srcTexelSize = WebGLTexelConversions::TexelBytesForFormat(actualSrcFormat);
 
     if (width == 0 || height == 0)
@@ -3978,48 +3840,34 @@ WebGLContext::TexSubImage2D_base(GLenum 
         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 = activeBoundTextureForTarget(target);
-
-    if (!tex)
-        return ErrorInvalidOperation("texSubImage2D: no texture is bound to this target");
-
-    if (!tex->HasImageInfoAt(target, level))
-        return ErrorInvalidOperation("texSubImage2D: no texture image previously defined for this level and face");
-
     const WebGLTexture::ImageInfo &imageInfo = tex->ImageInfoAt(target, level);
-    if (!CanvasUtils::CheckSaneSubrectSize(xoffset, yoffset, width, height, imageInfo.Width(), imageInfo.Height()))
-        return ErrorInvalidValue("texSubImage2D: subtexture rectangle out of bounds");
-
-    // Require the format and type in texSubImage2D to match that of the existing texture as created by texImage2D
-    if (imageInfo.InternalFormat() != format || imageInfo.Type() != type)
-        return ErrorInvalidOperation("texSubImage2D: format or type doesn't match the existing texture");
-
-    if (imageInfo.HasUninitializedImageData()) {
+
+    if (imageInfo.HasUninitializedImageData())
         tex->DoDeferredImageInitialization(target, level);
-    }
 
     MakeContextCurrent();
 
     size_t srcStride = srcStrideOrZero ? srcStrideOrZero : checked_alignedRowSize.value();
 
+    uint32_t dstTexelSize = GetBitsPerTexel(format, 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();
 
     // convert type for half float if not on GLES2
     GLenum realType = type;
-    if (realType == LOCAL_GL_HALF_FLOAT_OES && !gl->IsGLES2()) {
+    if (realType == LOCAL_GL_HALF_FLOAT_OES && !gl->IsGLES2())
         realType = LOCAL_GL_HALF_FLOAT;
-    }
 
     if (actualSrcFormat == dstFormat &&
         srcPremultiplied == mPixelStorePremultiplyAlpha &&
         srcStride == dstStride &&
         !mPixelStoreFlipY)
     {
         // no conversion, no flipping, so we avoid copying anything and just pass the source pointer
         gl->fTexSubImage2D(target, level, xoffset, yoffset, width, height, format, realType, pixels);
--- a/content/canvas/src/WebGLContextValidate.cpp
+++ b/content/canvas/src/WebGLContextValidate.cpp
@@ -9,32 +9,213 @@
 #include "WebGLShader.h"
 #include "WebGLProgram.h"
 #include "WebGLUniformLocation.h"
 #include "WebGLFramebuffer.h"
 #include "WebGLRenderbuffer.h"
 #include "WebGLTexture.h"
 #include "WebGLVertexArray.h"
 #include "GLContext.h"
+#include "CanvasUtils.h"
 
 #include "mozilla/CheckedInt.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 
 #include "jsfriendapi.h"
 
 #include "angle/ShaderLang.h"
 
 #include <algorithm>
 
 #include "mozilla/Services.h"
 #include "nsIObserverService.h"
 
 using namespace mozilla;
 
+/**
+ * Return the block size for format.
+ */
+static void
+BlockSizeFor(GLenum format, GLint* blockWidth, GLint* blockHeight)
+{
+    MOZ_ASSERT(blockWidth && 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:
+        if (blockWidth)
+            *blockWidth = 4;
+        if (blockHeight)
+            *blockHeight = 4;
+        break;
+    default:
+        break;
+    }
+}
+
+/**
+ * Return the displayable name for the texture function that is the
+ * source for validation.
+ */
+static const char*
+InfoFrom(WebGLTexImageFunc func)
+{
+    // TODO: Account for dimensions (WebGL 2)
+    switch (func) {
+    case WebGLTexImageFunc::TexImage:        return "texImage2D";
+    case WebGLTexImageFunc::TexSubImage:     return "texSubImage2D";
+    case WebGLTexImageFunc::CopyTexImage:    return "copyTexImage2D";
+    case WebGLTexImageFunc::CopyTexSubImage: return "copyTexSubImage2D";
+    case WebGLTexImageFunc::CompTexImage:    return "compressedTexImage2D";
+    case WebGLTexImageFunc::CompTexSubImage: return "compressedTexSubImage2D";
+    default:
+        MOZ_ASSERT(false, "Missing case for WebGLTexImageSource");
+        return "(error)";
+    }
+}
+
+/**
+ * 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*
+NameFrom(GLenum glenum)
+{
+    switch (glenum) {
+#define XX(x) case LOCAL_GL_##x: return #x
+        XX(ALPHA);
+        XX(ATC_RGB);
+        XX(ATC_RGBA_EXPLICIT_ALPHA);
+        XX(ATC_RGBA_INTERPOLATED_ALPHA);
+        XX(COMPRESSED_RGBA_PVRTC_2BPPV1);
+        XX(COMPRESSED_RGBA_PVRTC_4BPPV1);
+        XX(COMPRESSED_RGBA_S3TC_DXT1_EXT);
+        XX(COMPRESSED_RGBA_S3TC_DXT3_EXT);
+        XX(COMPRESSED_RGBA_S3TC_DXT5_EXT);
+        XX(COMPRESSED_RGB_PVRTC_2BPPV1);
+        XX(COMPRESSED_RGB_PVRTC_4BPPV1);
+        XX(COMPRESSED_RGB_S3TC_DXT1_EXT);
+        XX(DEPTH_COMPONENT);
+        XX(DEPTH_COMPONENT16);
+        XX(DEPTH_COMPONENT32);
+        XX(DEPTH_STENCIL);
+        XX(DEPTH24_STENCIL8);
+        XX(FLOAT);
+        XX(HALF_FLOAT);
+        XX(LUMINANCE);
+        XX(LUMINANCE_ALPHA);
+        XX(RGB);
+        XX(RGB16F);
+        XX(RGB32F);
+        XX(RGBA);
+        XX(RGBA16F);
+        XX(RGBA32F);
+        XX(SRGB);
+        XX(SRGB_ALPHA);
+        XX(TEXTURE_2D);
+        XX(TEXTURE_3D);
+        XX(TEXTURE_CUBE_MAP);
+        XX(TEXTURE_CUBE_MAP_NEGATIVE_X);
+        XX(TEXTURE_CUBE_MAP_NEGATIVE_Y);
+        XX(TEXTURE_CUBE_MAP_NEGATIVE_Z);
+        XX(TEXTURE_CUBE_MAP_POSITIVE_X);
+        XX(TEXTURE_CUBE_MAP_POSITIVE_Y);
+        XX(TEXTURE_CUBE_MAP_POSITIVE_Z);
+        XX(UNSIGNED_BYTE);
+        XX(UNSIGNED_INT);
+        XX(UNSIGNED_INT_24_8);
+        XX(UNSIGNED_SHORT);
+        XX(UNSIGNED_SHORT_4_4_4_4);
+        XX(UNSIGNED_SHORT_5_5_5_1);
+        XX(UNSIGNED_SHORT_5_6_5);
+#undef XX
+    }
+
+    return nullptr;
+}
+
+/**
+ * Same as ErrorInvalidEnum but uses NameFrom to print displayable
+ * name for \a glenum.
+ */
+static void
+ErrorInvalidEnumWithName(WebGLContext* ctx, const char* msg, GLenum glenum, WebGLTexImageFunc func)
+{
+    const char* name = NameFrom(glenum);
+    if (name)
+        ctx->ErrorInvalidEnum("%s: %s %s", InfoFrom(func), msg, name);
+    else
+        ctx->ErrorInvalidEnum("%s: %s 0x%04X", InfoFrom(func), 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:
+        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 target)
+{
+    return (target >= LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X &&
+            target <= LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z);
+}
+
 /*
  * Pull data out of the program, post-linking
  */
 bool
 WebGLProgram::UpdateInfo()
 {
     mIdentifierMap = nullptr;
     mIdentifierReverseMap = nullptr;
@@ -74,19 +255,19 @@ WebGLProgram::UpdateInfo()
             }
         }
     }
 
     if (!mUniformInfoMap) {
         mUniformInfoMap = new CStringToUniformInfoMap;
         for (size_t i = 0; i < mAttachedShaders.Length(); i++) {
             for (size_t j = 0; j < mAttachedShaders[i]->mUniforms.Length(); j++) {
-	        const WebGLMappedIdentifier& uniform = mAttachedShaders[i]->mUniforms[j];
-	        const WebGLUniformInfo& info = mAttachedShaders[i]->mUniformInfos[j];
-	        mUniformInfoMap->Put(uniform.mapped, info);
+                const WebGLMappedIdentifier& uniform = mAttachedShaders[i]->mUniforms[j];
+                const WebGLUniformInfo& info = mAttachedShaders[i]->mUniformInfos[j];
+                mUniformInfoMap->Put(uniform.mapped, info);
             }
         }
     }
 
     mActiveAttribMap.clear();
 
     GLint numActiveAttrs = 0;
     mContext->gl->fGetProgramiv(mGLName, LOCAL_GL_ACTIVE_ATTRIBUTES, &numActiveAttrs);
@@ -103,16 +284,98 @@ WebGLProgram::UpdateInfo()
         GLint attrLoc = mContext->gl->fGetAttribLocation(mGLName, attrName);
         MOZ_ASSERT(attrLoc >= 0);
         mActiveAttribMap.insert(std::make_pair(attrLoc, nsCString(attrName)));
     }
 
     return true;
 }
 
+/**
+ * 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.
+ */
+GLenum
+WebGLContext::BaseTexFormat(GLenum 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 internalFormat;
+    }
+
+    if (IsExtensionEnabled(EXT_sRGB)) {
+        if (internalFormat == LOCAL_GL_SRGB)
+            return LOCAL_GL_RGB;
+
+        if (internalFormat == LOCAL_GL_SRGB_ALPHA)
+            return LOCAL_GL_RGBA;
+    }
+
+    if (IsExtensionEnabled(WEBGL_compressed_texture_atc)) {
+        if (internalFormat == LOCAL_GL_ATC_RGB)
+            return LOCAL_GL_RGB;
+
+        if (internalFormat == LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA ||
+            internalFormat == LOCAL_GL_ATC_RGBA_INTERPOLATED_ALPHA)
+        {
+            return LOCAL_GL_RGBA;
+        }
+    }
+
+    if (IsExtensionEnabled(WEBGL_compressed_texture_pvrtc)) {
+        if (internalFormat == LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1 ||
+            internalFormat == LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1)
+        {
+            return LOCAL_GL_RGB;
+        }
+
+        if (internalFormat == LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1 ||
+            internalFormat == LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1)
+        {
+            return LOCAL_GL_RGBA;
+        }
+    }
+
+    if (IsExtensionEnabled(WEBGL_compressed_texture_s3tc)) {
+        if (internalFormat == LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT)
+            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 LOCAL_GL_RGBA;
+        }
+    }
+
+    if (IsExtensionEnabled(WEBGL_depth_texture)) {
+        if (internalFormat == LOCAL_GL_DEPTH_COMPONENT ||
+            internalFormat == LOCAL_GL_DEPTH_COMPONENT16 ||
+            internalFormat == LOCAL_GL_DEPTH_COMPONENT32)
+        {
+            return LOCAL_GL_DEPTH_COMPONENT;
+        }
+
+        if (internalFormat == LOCAL_GL_DEPTH_STENCIL ||
+            internalFormat == LOCAL_GL_DEPTH24_STENCIL8)
+        {
+            return LOCAL_GL_DEPTH_STENCIL;
+        }
+    }
+
+    MOZ_ASSERT(false, "Unhandled internalFormat");
+    return LOCAL_GL_NONE;
+}
+
 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:
@@ -295,75 +558,278 @@ bool WebGLContext::ValidateGLSLString(co
              ErrorInvalidValue("%s: string contains the illegal character '%d'", info, string.CharAt(i));
              return false;
         }
     }
 
     return true;
 }
 
-bool WebGLContext::ValidateTexImage2DFormat(GLenum format, const char* info)
+/**
+ * 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)
+{
+    /* 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;
+    }
+
+    /* 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);
+        return false;
+    }
+
+    /* WEBGL_depth_texture added formats */
+    if (format == LOCAL_GL_DEPTH_COMPONENT ||
+        format == LOCAL_GL_DEPTH_STENCIL)
+    {
+        bool validFormat = IsExtensionEnabled(WEBGL_depth_texture);
+        if (!validFormat)
+            ErrorInvalidEnum("%s: invalid format %s: need WEBGL_depth_texture enabled",
+                             InfoFrom(func), NameFrom(format));
+        return validFormat;
+    }
+
+    /* EXT_sRGB added formats */
+    if (format == LOCAL_GL_SRGB ||
+        format == LOCAL_GL_SRGB_ALPHA)
+    {
+        bool validFormat = IsExtensionEnabled(EXT_sRGB);
+        if (!validFormat)
+            ErrorInvalidEnum("%s: invalid format %s: need EXT_sRGB enabled",
+                             InfoFrom(func), NameFrom(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(WEBGL_compressed_texture_atc);
+        if (!validFormat)
+            ErrorInvalidEnum("%s: invalid format %s: need WEBGL_compressed_texture_atc enabled",
+                             InfoFrom(func), NameFrom(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(WEBGL_compressed_texture_pvrtc);
+        if (!validFormat)
+            ErrorInvalidEnum("%s: invalid format %s: need WEBGL_compressed_texture_pvrtc enabled",
+                             InfoFrom(func), NameFrom(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(WEBGL_compressed_texture_s3tc);
+        if (!validFormat)
+            ErrorInvalidEnum("%s: invalid format %s: need WEBGL_compressed_texture_s3tc enabled",
+                             InfoFrom(func), NameFrom(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)
 {
-    if (IsExtensionEnabled(EXT_sRGB)) {
-        switch (format) {
-            case LOCAL_GL_SRGB_EXT:
-            case LOCAL_GL_SRGB_ALPHA_EXT:
-                return true;
+    switch (dims) {
+    case 2:
+        if (target == LOCAL_GL_TEXTURE_2D ||
+            IsTexImageCubemapTarget(target))
+        {
+            return true;
+        }
+
+        ErrorInvalidEnumWithName(this, "invalid target", target, func);
+        return false;
+
+    default:
+        MOZ_ASSERT(false, "ValidateTexImageTarget: Invalid 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)
+{
+    /* 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;
+    }
+
+    /* OES_texture_float added types */
+    if (type == LOCAL_GL_FLOAT) {
+        bool validType = IsExtensionEnabled(OES_texture_float);
+        if (!validType)
+            ErrorInvalidEnum("%s: invalid type %s: need OES_texture_float enabled",
+                             InfoFrom(func), NameFrom(type));
+        return validType;
+    }
+
+    /* OES_texture_half_float add types */
+    if (type == LOCAL_GL_HALF_FLOAT_OES) {
+        bool validType = IsExtensionEnabled(OES_texture_half_float);
+        if (!validType)
+            ErrorInvalidEnum("%s: invalid type %s: need OES_texture_half_float enabled",
+                             InfoFrom(func), NameFrom(type));
+        return validType;
+    }
+
+    /* WEBGL_depth_texture added types */
+    if (type == LOCAL_GL_UNSIGNED_SHORT ||
+        type == LOCAL_GL_UNSIGNED_INT ||
+        type == LOCAL_GL_UNSIGNED_INT_24_8)
+    {
+        bool validType = IsExtensionEnabled(WEBGL_depth_texture);
+        if (!validType)
+            ErrorInvalidEnum("%s: invalid type %s: need WEBGL_depth_texture enabled",
+                             InfoFrom(func), NameFrom(type));
+        return validType;
+    }
+
+    ErrorInvalidEnumWithName(this, "invalid type", type, func);
+    return false;
+}
+
+/**
+ * Validate texture image sizing extra constraints for
+ * CompressedTex(Sub)?Image.
+ */
+// TODO: WebGL 2
+bool
+WebGLContext::ValidateCompTexImageSize(GLenum target, 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);
+
+    if (xoffset + width > (GLint) levelWidth) {
+        ErrorInvalidValue("%s: xoffset + width must be <= levelWidth", InfoFrom(func));
+        return false;
+    }
+
+    if (yoffset + height > (GLint) levelHeight) {
+        ErrorInvalidValue("%s: yoffset + height must be <= levelHeight", InfoFrom(func));
+        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), blockWidth);
+            return false;
+        }
+
+        if (yoffset % blockHeight != 0) {
+            ErrorInvalidOperation("%s: yoffset must be multiple of %d",
+                                  InfoFrom(func), 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. (s3tc extension appears
+         * to have changed and old code that checks 1x1, 2x2 doesn't
+         * appear necessary anymore)
+         */
+        if ((width % blockWidth != 0) &&
+            (xoffset + width != (GLint) levelWidth))
+        {
+            ErrorInvalidOperation("%s: width must be multiple of %d or "
+                                  "xoffset + width must be %d",
+                                  InfoFrom(func), blockWidth, levelWidth);
+            return false;
+        }
+
+        if ((height % blockHeight != 0) &&
+            (yoffset + height != (GLint) levelHeight))
+        {
+            ErrorInvalidOperation("%s: height must be multiple of %d or "
+                                  "yoffset + height must be %d",
+                                  InfoFrom(func), blockHeight, levelHeight);
+            return false;
         }
     }
 
     switch (format) {
-        case LOCAL_GL_RGB:
-        case LOCAL_GL_RGBA:
-        case LOCAL_GL_ALPHA:
-        case LOCAL_GL_LUMINANCE:
-        case LOCAL_GL_LUMINANCE_ALPHA:
-        case LOCAL_GL_DEPTH_COMPONENT:
-        case LOCAL_GL_DEPTH_STENCIL:
-            return true;
-            break;
-    }
-
-    ErrorInvalidEnumInfo(info, format);
-    return false;
-}
-
-bool WebGLContext::ValidateTexImage2DTarget(GLenum target, GLsizei width, GLsizei height,
-                                            const char* info)
-{
-    switch (target) {
-        case LOCAL_GL_TEXTURE_2D:
-            break;
-        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:
-            if (width != height) {
-                ErrorInvalidValue("%s: with cube map targets, width and height must be equal", info);
-                return false;
-            }
-            break;
-        default:
-            ErrorInvalidEnum("%s: invalid target enum 0x%x", info, target);
+    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 (!is_pot_assuming_nonnegative(width) ||
+            !is_pot_assuming_nonnegative(height))
+        {
+            ErrorInvalidValue("%s: width and height must be powers of two",
+                              InfoFrom(func));
             return false;
+        }
     }
 
     return true;
 }
 
-bool WebGLContext::ValidateCompressedTextureSize(GLenum target, GLint level,
-                                                 GLenum format,
-                                                 GLsizei width, GLsizei height, uint32_t byteLength, const char* info)
+/**
+ * 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)
 {
-    if (!ValidateLevelWidthHeightForTarget(target, level, width, height, info)) {
-        return false;
-    }
-
     // 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:
@@ -390,287 +856,486 @@ bool WebGLContext::ValidateCompressedTex
         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", info);
+        ErrorInvalidValue("%s: data size does not match dimensions", InfoFrom(func));
+        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(GLenum target, GLint level,
+                                   GLint width, GLint height, GLint depth,
+                                   WebGLTexImageFunc func)
+{
+    MOZ_ASSERT(level >= 0, "level should already be validated");
+
+    const GLuint maxTexImageSize = MaxTextureSizeForTarget(target) >> level;
+    const bool isCubemapTarget = IsTexImageCubemapTarget(target);
+
+    if (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 map, width must equal height", InfoFrom(func));
         return false;
     }
 
-    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:
-        {
-            if (level == 0 && width % 4 == 0 && height % 4 == 0) {
-                break;
-            }
-            if (level > 0
-                && (width == 0 || width == 1 || width == 2 || width % 4 == 0)
-                && (height == 0 || height == 1 || height == 2 || height % 4 == 0))
-            {
-                break;
-            }
-            ErrorInvalidOperation("%s: level parameter does not match width and height", info);
+    if (target == 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));
+            return false;
+        }
+
+        if (height < 0) {
+            ErrorInvalidValue("%s: height must be >= 0", InfoFrom(func));
+            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), level, maxTexImageSize);
             return false;
         }
-        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 (!is_pot_assuming_nonnegative(width) ||
-                !is_pot_assuming_nonnegative(height))
-            {
-                ErrorInvalidValue("%s: width and height must be powers of two", info);
+
+        if (height > (int) maxTexImageSize) {
+            ErrorInvalidValue("%s: tex maximum height for level %d is %u",
+                              InfoFrom(func), 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."
+         */
+        if (level > 0) {
+            if (!is_pot_assuming_nonnegative(width)) {
+                ErrorInvalidValue("%s: level >= 0, width of %d must be a power of two.",
+                                  InfoFrom(func), width);
                 return false;
             }
+
+            if (!is_pot_assuming_nonnegative(height)) {
+                ErrorInvalidValue("%s: level >= 0, height of %d must be a power of two.",
+                                  InfoFrom(func), height);
+                return false;
+            }
+        }
+    }
+
+    // TODO: WebGL 2
+    if (target == LOCAL_GL_TEXTURE_3D) {
+        if (depth < 0) {
+            ErrorInvalidValue("%s: depth must be >= 0", InfoFrom(func));
+            return false;
+        }
+
+        if (!is_pot_assuming_nonnegative(depth)) {
+            ErrorInvalidValue("%s: level >= 0, depth of %d must be a power of two.",
+                              InfoFrom(func), depth);
+            return false;
         }
     }
 
     return true;
 }
 
-bool WebGLContext::ValidateLevelWidthHeightForTarget(GLenum target, GLint level, GLsizei width,
-                                                     GLsizei height, const char* info)
+/**
+ * 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)
 {
-    GLsizei maxTextureSize = MaxTextureSizeForTarget(target);
+    /* 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 (level < 0) {
-        ErrorInvalidValue("%s: level must be >= 0", info);
+    if (xoffset < 0) {
+        ErrorInvalidValue("%s: xoffset must be >= 0", InfoFrom(func));
         return false;
     }
 
-    GLsizei maxAllowedSize = maxTextureSize >> level;
-
-    if (!maxAllowedSize) {
-        ErrorInvalidValue("%s: 2^level exceeds maximum texture size", info);
+    if (yoffset < 0) {
+        ErrorInvalidValue("%s: yoffset must be >= 0", InfoFrom(func));
         return false;
     }
 
-    if (width < 0 || height < 0) {
-        ErrorInvalidValue("%s: width and height must be >= 0", info);
-        return false;
-    }
-
-    if (width > maxAllowedSize || height > maxAllowedSize) {
-        ErrorInvalidValue("%s: the maximum texture size for level %d is %d", info, level, maxAllowedSize);
+    if (!CanvasUtils::CheckSaneSubrectSize(xoffset, yoffset, width, height, baseWidth, baseHeight)) {
+        ErrorInvalidValue("%s: subtexture rectangle out-of-bounds", InfoFrom(func));
         return false;
     }
 
     return true;
 }
 
-uint32_t WebGLContext::GetBitsPerTexel(GLenum format, GLenum type)
+/**
+ * Return the bits per texel for format & type combination.
+ * Assumes that format & type are a valid combination as checked with
+ * ValidateTexImageFormatAndType().
+ */
+uint32_t
+WebGLContext::GetBitsPerTexel(GLenum format, GLenum type)
 {
     // If there is no defined format or type, we're not taking up any memory
     if (!format || !type) {
         return 0;
     }
 
-    if (format == LOCAL_GL_DEPTH_COMPONENT) {
-        if (type == LOCAL_GL_UNSIGNED_SHORT)
-            return 2;
-        else if (type == LOCAL_GL_UNSIGNED_INT)
-            return 4;
-    } else if (format == LOCAL_GL_DEPTH_STENCIL) {
-        if (type == LOCAL_GL_UNSIGNED_INT_24_8_EXT)
-            return 4;
+    /* Known fixed-sized types */
+    if (type == LOCAL_GL_UNSIGNED_SHORT_4_4_4_4 ||
+        type == LOCAL_GL_UNSIGNED_SHORT_5_5_5_1 ||
+        type == LOCAL_GL_UNSIGNED_SHORT_5_6_5)
+    {
+        return 16;
+    }
+
+    if (type == LOCAL_GL_UNSIGNED_INT_24_8)
+        return 32;
+
+    int bitsPerComponent = 0;
+    switch (type) {
+    case LOCAL_GL_UNSIGNED_BYTE:
+        bitsPerComponent = 8;
+        break;
+
+    case LOCAL_GL_HALF_FLOAT:
+    case LOCAL_GL_HALF_FLOAT_OES:
+    case LOCAL_GL_UNSIGNED_SHORT:
+        bitsPerComponent = 16;
+        break;
+
+    case LOCAL_GL_FLOAT:
+    case LOCAL_GL_UNSIGNED_INT:
+        bitsPerComponent = 32;
+        break;
+
+    default:
+        MOZ_ASSERT(false, "Unhandled type.");
+        break;
     }
 
-    if (type == LOCAL_GL_UNSIGNED_BYTE || type == LOCAL_GL_FLOAT) {
-        int multiplier = type == LOCAL_GL_FLOAT ? 32 : 8;
-        switch (format) {
-            case LOCAL_GL_ALPHA:
-            case LOCAL_GL_LUMINANCE:
-                return 1 * multiplier;
-            case LOCAL_GL_LUMINANCE_ALPHA:
-                return 2 * multiplier;
-            case LOCAL_GL_RGB:
-            case LOCAL_GL_RGB32F:
-            case LOCAL_GL_SRGB_EXT:
-                return 3 * multiplier;
-            case LOCAL_GL_RGBA:
-            case LOCAL_GL_RGBA32F:
-            case LOCAL_GL_SRGB_ALPHA_EXT:
-                return 4 * multiplier;
-            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:
-                return 4;
-            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;
-            default:
-                break;
-        }
-    } else if (type == LOCAL_GL_UNSIGNED_SHORT_4_4_4_4 ||
-               type == LOCAL_GL_UNSIGNED_SHORT_5_5_5_1 ||
-               type == LOCAL_GL_UNSIGNED_SHORT_5_6_5)
-    {
-        return 16;
+    switch (format) {
+        // Uncompressed formats
+    case LOCAL_GL_ALPHA:
+    case LOCAL_GL_LUMINANCE:
+    case LOCAL_GL_DEPTH_COMPONENT:
+    case LOCAL_GL_DEPTH_STENCIL:
+        return 1 * bitsPerComponent;
+
+    case LOCAL_GL_LUMINANCE_ALPHA:
+        return 2 * bitsPerComponent;
+
+    case LOCAL_GL_RGB:
+    case LOCAL_GL_RGB32F:
+    case LOCAL_GL_SRGB_EXT:
+        return 3 * bitsPerComponent;
+
+    case LOCAL_GL_RGBA:
+    case LOCAL_GL_RGBA32F:
+    case LOCAL_GL_SRGB_ALPHA_EXT:
+        return 4 * bitsPerComponent;
+
+        // Compressed formats
+    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:
+        return 4;
+
+    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;
+
+    default:
+        break;
     }
 
     MOZ_ASSERT(false, "Unhandled format+type combo.");
     return 0;
 }
 
-bool WebGLContext::ValidateTexFormatAndType(GLenum format, GLenum type, int jsArrayType,
-                                              uint32_t *texelSize, const char *info)
+/**
+ * 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)
 {
-    if (IsExtensionEnabled(WEBGL_depth_texture)) {
-        if (format == LOCAL_GL_DEPTH_COMPONENT) {
-            if (jsArrayType != -1) {
-                if ((type == LOCAL_GL_UNSIGNED_SHORT && jsArrayType != js::ArrayBufferView::TYPE_UINT16) ||
-                    (type == LOCAL_GL_UNSIGNED_INT && jsArrayType != js::ArrayBufferView::TYPE_UINT32)) {
-                    ErrorInvalidOperation("%s: invalid typed array type for given texture data type", info);
-                    return false;
-                }
-            }
-
-            switch(type) {
-                case LOCAL_GL_UNSIGNED_SHORT:
-                    *texelSize = 2;
-                    break;
-                case LOCAL_GL_UNSIGNED_INT:
-                    *texelSize = 4;
-                    break;
-                default:
-                    ErrorInvalidOperation("%s: invalid type 0x%x", info, type);
-                    return false;
-            }
-
-            return true;
-
-        } else if (format == LOCAL_GL_DEPTH_STENCIL) {
-            if (type != LOCAL_GL_UNSIGNED_INT_24_8_EXT) {
-                ErrorInvalidOperation("%s: invalid format 0x%x", info, format);
-                return false;
-            }
-            if (jsArrayType != -1) {
-                if (jsArrayType != js::ArrayBufferView::TYPE_UINT32) {
-                    ErrorInvalidOperation("%s: invalid typed array type for given texture data type", info);
-                    return false;
-                }
-            }
-
-            *texelSize = 4;
-            return true;
-        }
+    if (!ValidateTexImageFormat(format, func) ||
+        !ValidateTexImageType(type, func))
+    {
+        return false;
     }
 
+    bool validCombo = false;
+
+    switch (format) {
+    case LOCAL_GL_ALPHA:
+    case LOCAL_GL_LUMINANCE:
+    case LOCAL_GL_LUMINANCE_ALPHA:
+        validCombo = (type == LOCAL_GL_UNSIGNED_BYTE ||
+                      type == LOCAL_GL_HALF_FLOAT ||
+                      type == LOCAL_GL_FLOAT);
+        break;
+
+    case LOCAL_GL_RGB:
+    case LOCAL_GL_SRGB:
+        validCombo = (type == LOCAL_GL_UNSIGNED_BYTE ||
+                      type == LOCAL_GL_UNSIGNED_SHORT_5_6_5 ||
+                      type == LOCAL_GL_HALF_FLOAT ||
+                      type == LOCAL_GL_FLOAT);
+        break;
+
+    case LOCAL_GL_RGBA:
+    case LOCAL_GL_SRGB_ALPHA:
+        validCombo = (type == LOCAL_GL_UNSIGNED_BYTE ||
+                      type == LOCAL_GL_UNSIGNED_SHORT_4_4_4_4 ||
+                      type == LOCAL_GL_UNSIGNED_SHORT_5_5_5_1 ||
+                      type == LOCAL_GL_HALF_FLOAT ||
+                      type == LOCAL_GL_FLOAT);
+        break;
+
+    case LOCAL_GL_DEPTH_COMPONENT:
+        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;
+
+    default:
+        // Only valid formats should be passed to the switch stmt.
+        MOZ_ASSERT("Invalid format");
+        return false;
+    }
+
+    if (!validCombo)
+        ErrorInvalidOperation("%s: invalid combination of format %s and type %s",
+                              InfoFrom(func), NameFrom(format), NameFrom(type));
+
+    return validCombo;
+}
+
+/**
+ * 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, int jsArrayType, WebGLTexImageFunc func)
+{
+    bool validInput = false;
     const char invalidTypedArray[] = "%s: invalid typed array type for given texture data type";
 
     // First, we check for packed types
     switch (type) {
-        case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
-        case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
-            if (jsArrayType != -1 && jsArrayType != js::ArrayBufferView::TYPE_UINT16) {
-                ErrorInvalidOperation("%s: invalid typed array type for given texture data type", info);
-                return false;
-            }
+    case LOCAL_GL_UNSIGNED_BYTE:
+        validInput = (jsArrayType == -1 || jsArrayType == js::ArrayBufferView::TYPE_UINT8);
+        break;
+
+        // TODO: WebGL spec doesn't allow half floats to specified as UInt16.
+    // case LOCAL_GL_HALF_FLOAT:
+    // case LOCAL_GL_HALF_FLOAT_OES:
+    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 == -1 || jsArrayType == js::ArrayBufferView::TYPE_UINT16);
+        break;
+
+    case LOCAL_GL_UNSIGNED_INT:
+    case LOCAL_GL_UNSIGNED_INT_24_8:
+        validInput = (jsArrayType == -1 || jsArrayType == js::ArrayBufferView::TYPE_UINT32);
+        break;
+
+    case LOCAL_GL_FLOAT:
+        validInput = (jsArrayType == -1 || jsArrayType == js::ArrayBufferView::TYPE_FLOAT32);
+        break;
 
-            if (format == LOCAL_GL_RGBA) {
-                *texelSize = 2;
-                return true;
-            }
-            ErrorInvalidOperation("%s: mutually incompatible format and type", info);
-            return false;
+    default:
+        break;
+    }
+
+    if (!validInput)
+        ErrorInvalidOperation(invalidTypedArray, InfoFrom(func));
+
+    return validInput;
+}
 
-        case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
-            if (jsArrayType != -1 && jsArrayType != js::ArrayBufferView::TYPE_UINT16) {
-                ErrorInvalidOperation("%s: invalid typed array type for given texture data type", info);
-                return false;
-            }
+/**
+ * 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, GLenum target,
+                               GLint level, GLint internalFormat,
+                               GLint xoffset, GLint yoffset, GLint zoffset,
+                               GLint width, GLint height, GLint depth,
+                               GLint border, GLenum format, GLenum type,
+                               WebGLTexImageFunc func)
+{
+    const char* info = InfoFrom(func);
+
+    /* Check target */
+    if (!ValidateTexImageTarget(dims, target, func))
+        return false;
 
-            if (format == LOCAL_GL_RGB) {
-                *texelSize = 2;
-                return true;
-            }
-            ErrorInvalidOperation("%s: mutually incompatible format and type", info);
-            return false;
+    /* 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;
+    }
 
-        default:
-            break;
-        }
+    /* 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 ((GLint) format != internalFormat) {
+        ErrorInvalidOperation("%s: format does not match internalformat", info);
+        return false;
+    }
 
-    int texMultiplier = 1;
+    /* check internalFormat */
+    // TODO: Not sure if this is a bit of over kill.
+    if (BaseTexFormat(internalFormat) == LOCAL_GL_NONE) {
+        MOZ_ASSERT(false);
+        ErrorInvalidValue("%s:", info);
+        return false;
+    }
+
+    /* Check texture image size */
+    if (!ValidateTexImageSize(target, level, width, height, 0, func))
+        return false;
 
-    // If not a packed types, then it's might be a standard type.
-    if (type == LOCAL_GL_UNSIGNED_BYTE) {
-        if (jsArrayType != -1 && jsArrayType != js::ArrayBufferView::TYPE_UINT8) {
-            ErrorInvalidEnum(invalidTypedArray, info);
-            return false;
-        }
-    } else if (type == LOCAL_GL_FLOAT) {
-        if (!IsExtensionEnabled(OES_texture_float)) {
-            ErrorInvalidEnum("%s: invalid format FLOAT: need OES_texture_float enabled", info);
+    /* 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 = activeBoundTextureForTarget(target);
+    if (!tex) {
+        ErrorInvalidOperation("%s: no texture is bound to target %s",
+                              info, NameFrom(target));
+        return false;
+    }
+
+    if (IsSubFunc(func)) {
+        if (!tex->HasImageInfoAt(target, level)) {
+            ErrorInvalidOperation("%s: no texture image previously defined for target %s at level %d",
+                                  info, NameFrom(target), level);
             return false;
         }
 
-        if (jsArrayType != -1 && jsArrayType != js::ArrayBufferView::TYPE_FLOAT32) {
-            ErrorInvalidOperation(invalidTypedArray, info);
-            return false;
-        }
-
-        texMultiplier = 4;
-    } else if (type == LOCAL_GL_HALF_FLOAT_OES) {
-        if (!IsExtensionEnabled(OES_texture_half_float)) {
-            ErrorInvalidEnum("%s: invalid format HALF_FLOAT_OES: need OES_texture_half_float enabled", info);
-            return false;
-        }
-
-        if (jsArrayType != -1)
+        const WebGLTexture::ImageInfo& imageInfo = tex->ImageInfoAt(target, level);
+        if (!ValidateTexSubImageSize(xoffset, yoffset, zoffset,
+                                     width, height, depth,
+                                     imageInfo.Width(), imageInfo.Height(), 0,
+                                     func))
         {
-            ErrorInvalidOperation(invalidTypedArray, info);
             return false;
         }
 
-        texMultiplier = 2;
-    } else {
-        // We don't know the type
-        ErrorInvalidEnum("%s: invalid type 0x%x", info, type);
+        /* Require the format and type to match that of the existing
+         * texture as created
+         */
+        if (imageInfo.InternalFormat() != format ||
+            imageInfo.Type() != type)
+        {
+            ErrorInvalidOperation("%s: format or type doesn't match the existing texture",
+                                  info);
+            return false;
+        }
+    }
+
+    /* Additional checks for depth textures */
+    if (target != 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, NameFrom(format));
         return false;
     }
 
-    // Ok we know that is a standard type.
-    switch (format) {
-        case LOCAL_GL_ALPHA:
-        case LOCAL_GL_LUMINANCE:
-            *texelSize = 1 * texMultiplier;
-            return true;
-        case LOCAL_GL_LUMINANCE_ALPHA:
-            *texelSize = 2 * texMultiplier;
-            return true;
-        case LOCAL_GL_RGB:
-        case LOCAL_GL_SRGB_EXT:
-            *texelSize = 3 * texMultiplier;
-            return true;
-        case LOCAL_GL_RGBA:
-        case LOCAL_GL_SRGB_ALPHA_EXT:
-            *texelSize = 4 * texMultiplier;
-            return true;
-        default:
-            break;
+    /* Additional checks for compressed textures */
+    if (!IsAllowedFromSource(format, func)) {
+        ErrorInvalidOperation("%s: Invalid format %s for this operation",
+                              info, NameFrom(format));
+        return false;
     }
 
-    ErrorInvalidEnum("%s: invalid format 0x%x", info, format);
-    return false;
+    /* Parameters are OK */
+    return true;
 }
 
 bool
 WebGLContext::ValidateUniformLocation(const char* info, WebGLUniformLocation *location_object)
 {
     if (!ValidateObjectAllowNull(info, location_object))
         return false;
     if (!location_object)
--- a/content/canvas/src/WebGLTypes.h
+++ b/content/canvas/src/WebGLTypes.h
@@ -129,11 +129,20 @@ MOZ_BEGIN_ENUM_CLASS(WebGLTexelFormat, i
     RGBA8,
     BGRA8, // used for DOM elements
     RGBA5551,
     RGBA4444,
     RGBA16F, // OES_texture_half_float
     RGBA32F // OES_texture_float
 MOZ_END_ENUM_CLASS(WebGLTexelFormat)
 
+MOZ_BEGIN_ENUM_CLASS(WebGLTexImageFunc, int)
+    TexImage,
+    TexSubImage,
+    CopyTexImage,
+    CopyTexSubImage,
+    CompTexImage,
+    CompTexSubImage,
+MOZ_END_ENUM_CLASS(WebGLTexImageFunc)
+
 } // namespace mozilla
 
 #endif
--- a/content/canvas/test/webgl/conformance/more/functions/texImage2DBadArgs.html
+++ b/content/canvas/test/webgl/conformance/more/functions/texImage2DBadArgs.html
@@ -1,11 +1,12 @@
 <!DOCTYPE html>
-<html><head>
-<meta charset="utf-8">
+<html>
+<head>
+<meta charset="utf-8"/>
 <!--
 Tests for the OpenGL ES 2.0 HTML Canvas context
 
 Copyright (C) 2011  Ilmari Heikkinen <ilmari.heikkinen@gmail.com>
 
 Permission is hereby granted, free of charge, to any person
 obtaining a copy of this software and associated documentation
 files (the "Software"), to deal in the Software without
@@ -23,84 +24,93 @@ EXPRESS OR IMPLIED, INCLUDING BUT NOT LI
 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 OTHER DEALINGS IN THE SOFTWARE.
 
 -->
-<link rel="stylesheet" type="text/css" href="../unit.css" />
-<script type="application/x-javascript" src="../unit.js"></script>
-<script type="application/x-javascript" src="../util.js"></script>
-<script type="application/x-javascript">
+<link rel="stylesheet" type="text/css" href="../../../resources/js-test-style.css" />
+<script src="../../../resources/js-test-pre.js"></script>
+<script src="../../resources/webgl-test.js"></script>
+<script src="../../resources/webgl-test-utils.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<canvas id="canvas" width="16" height="16" style="width: 50px; height: 50px; border: 1px solid black;"></canvas>
+<div id="console"></div>
 
-Tests.startUnit = function () {
-    var canvas = document.getElementById('gl');
-    var gl = wrapGLContext(canvas.getContext(GL_CONTEXT_ID));
-    return [gl];
+<script>
+var wtu = WebGLTestUtils;
+
+description("Test bad arguments to texImage2D");
+
+var canvas = document.getElementById('canvas');
+var gl = wtu.create3DContext(canvas);
+
+function setup(gl) {
+    var tex = gl.createTexture();
+    gl.bindTexture(gl.TEXTURE_2D, tex);
+    return tex;
 }
 
-Tests.setup = function(gl) {
-    var tex = gl.createTexture();
-    gl.bindTexture(gl.TEXTURE_2D, tex);
-    return [gl]
-}
-
-Tests.teardown = function(gl,tex) {
+function teardown(gl, tex) {
     gl.bindTexture(gl.TEXTURE_2D, null);
     gl.deleteTexture(tex);
 }
 
-Tests.testTexImage2D = function(gl) {
-    var data = new Uint8Array(4);
-    assertGLError(gl, gl.INVALID_OPERATION, "not enough data", function(){
-        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 2,1,0,gl.RGBA,gl.UNSIGNED_BYTE, data);
-    });
-    assertGLError(gl, gl.INVALID_OPERATION, "not enough data", function(){
-        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1,2,0,gl.RGBA,gl.UNSIGNED_BYTE, data);
-    });
-    assertGLError(gl, gl.INVALID_ENUM, "bad target", function(){
-        gl.texImage2D(gl.FLOAT, 0, gl.RGBA, 1,1,0,gl.RGBA,gl.UNSIGNED_BYTE, null);
-    });
-    assertGLError(gl, gl.INVALID_ENUM, "bad internal format/format", function(){
-        gl.texImage2D(gl.TEXTURE_2D, 0, gl.FLOAT, 1,1,0,gl.FLOAT,gl.UNSIGNED_BYTE, null);
-    });
-    assertGLError(gl, gl.INVALID_VALUE, "border > 0", function(){
-        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1,1,48,gl.RGBA,gl.UNSIGNED_BYTE, null);
-    });
-    // The spec says zero size is OK. If you disagree please list the section
-    // in the spec that details this issue.
-    assertOk("zero size", function(){
-        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 0,0,0,gl.RGBA,gl.UNSIGNED_BYTE, null);
-    });
-    assertGLError(gl, gl.INVALID_VALUE, "negative width", function(){
-        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, -1,1,0,gl.RGBA,gl.UNSIGNED_BYTE, null);
-    });
-    assertGLError(gl, gl.INVALID_VALUE, "negative height", function(){
-        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1,-1,0,gl.RGBA,gl.UNSIGNED_BYTE, null);
-    });
-    assertGLError(gl, gl.INVALID_ENUM, "bad format", function(){
-        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1,1,0,gl.FLOAT,gl.UNSIGNED_BYTE, null);
-    });
-    assertGLError(gl, gl.INVALID_ENUM, "bad type", function(){
-        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1,1,0,gl.RGBA,gl.TEXTURE_2D, null);
-    });
-    assertGLError(gl, gl.INVALID_OPERATION, "not enough data", function(){
-        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1,1,0,gl.RGBA,gl.UNSIGNED_BYTE, new Uint8Array(3));
-    });
-    assertGLError(gl, gl.INVALID_OPERATION, "format and type incompatible",function(){
-        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1,1,0,gl.RGBA,gl.UNSIGNED_SHORT_5_6_5, null);
-    });
-    assertGLError(gl, gl.INVALID_OPERATION, "format and type incompatible",function(){
-        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, 1,1,0,gl.RGB,gl.UNSIGNED_SHORT_4_4_4_4, null);
-    });
+function testrunner(gl, expected, desc, fn) {
+   var tex = setup(gl);
+   fn();
+   glErrorShouldBe(gl, expected, desc);
+   teardown(gl, tex);
 }
 
+ var data = new Uint8Array(4);
+ testrunner(gl, gl.INVALID_OPERATION, "not enough data", function(){
+     gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 2,1,0,gl.RGBA,gl.UNSIGNED_BYTE, data);
+ });
+ testrunner(gl, gl.INVALID_OPERATION, "not enough data", function(){
+     gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1,2,0,gl.RGBA,gl.UNSIGNED_BYTE, data);
+ });
+ testrunner(gl, gl.INVALID_ENUM, "bad target", function(){
+     gl.texImage2D(gl.FLOAT, 0, gl.RGBA, 1,1,0,gl.RGBA,gl.UNSIGNED_BYTE, null);
+ });
+ testrunner(gl, gl.INVALID_ENUM, "bad internal format/format", function(){
+     gl.texImage2D(gl.TEXTURE_2D, 0, gl.FLOAT, 1,1,0,gl.FLOAT,gl.UNSIGNED_BYTE, null);
+ });
+ testrunner(gl, gl.INVALID_VALUE, "border > 0", function(){
+     gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1,1,48,gl.RGBA,gl.UNSIGNED_BYTE, null);
+ });
+ // The spec says zero size is OK. If you disagree please list the section
+ // in the spec that details this issue.
+ testrunner(gl, gl.NO_ERROR, "zero size", function(){
+     gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 0,0,0,gl.RGBA,gl.UNSIGNED_BYTE, null);
+ });
+ testrunner(gl, gl.INVALID_VALUE, "negative width", function(){
+     gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, -1,1,0,gl.RGBA,gl.UNSIGNED_BYTE, null);
+ });
+ testrunner(gl, gl.INVALID_VALUE, "negative height", function(){
+     gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1,-1,0,gl.RGBA,gl.UNSIGNED_BYTE, null);
+ });
+ testrunner(gl, gl.INVALID_ENUM, "bad format", function(){
+     gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1,1,0,gl.FLOAT,gl.UNSIGNED_BYTE, null);
+ });
+ testrunner(gl, gl.INVALID_ENUM, "bad type", function(){
+     gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1,1,0,gl.RGBA,gl.TEXTURE_2D, null);
+ });
+ testrunner(gl, gl.INVALID_OPERATION, "not enough data", function(){
+     gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1,1,0,gl.RGBA,gl.UNSIGNED_BYTE, new Uint8Array(3));
+ });
+ testrunner(gl, gl.INVALID_OPERATION, "format and type incompatible",function(){
+     gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1,1,0,gl.RGBA,gl.UNSIGNED_SHORT_5_6_5, null);
+ });
+ testrunner(gl, gl.INVALID_OPERATION, "format and type incompatible",function(){
+     gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, 1,1,0,gl.RGB,gl.UNSIGNED_SHORT_4_4_4_4, null);
+ });
 
-Tests.endUnit = function(gl) {
-}
-
+debug("");
+var successfullyParsed = true;
 </script>
-<style>canvas{ position:absolute; }</style>
-</head><body>
-<canvas id="gl" width="16" height="16"></canvas>
-</body></html>
+<script>finishTest();</script>
+</body>
+</html>