diff --git a/content/canvas/src/WebGLContext.h b/content/canvas/src/WebGLContext.h index d82f03841a0b..96189f36f055 100644 --- a/content/canvas/src/WebGLContext.h +++ b/content/canvas/src/WebGLContext.h @@ -306,6 +306,11 @@ public: return ErrorInvalidEnum("%s: invalid enum value 0x%x", info, enumvalue); } + WebGLTexture *activeBoundTextureForTarget(WebGLenum target) { + return target == LOCAL_GL_TEXTURE_2D ? mBound2DTextures[mActiveTexture] + : mBoundCubeMapTextures[mActiveTexture]; + } + already_AddRefed GetCanvasLayer(CanvasLayer *aOldLayer, LayerManager *aManager); void MarkContextClean() { } @@ -334,15 +339,15 @@ protected: PRBool mShaderValidation; // some GL constants - PRUint32 mGLMaxVertexAttribs; - PRUint32 mGLMaxTextureUnits; - PRUint32 mGLMaxTextureSize; - PRUint32 mGLMaxCubeMapTextureSize; - PRUint32 mGLMaxTextureImageUnits; - PRUint32 mGLMaxVertexTextureImageUnits; - PRUint32 mGLMaxVaryingVectors; - PRUint32 mGLMaxFragmentUniformVectors; - PRUint32 mGLMaxVertexUniformVectors; + PRInt32 mGLMaxVertexAttribs; + PRInt32 mGLMaxTextureUnits; + PRInt32 mGLMaxTextureSize; + PRInt32 mGLMaxCubeMapTextureSize; + PRInt32 mGLMaxTextureImageUnits; + PRInt32 mGLMaxVertexTextureImageUnits; + PRInt32 mGLMaxVaryingVectors; + PRInt32 mGLMaxFragmentUniformVectors; + PRInt32 mGLMaxVertexUniformVectors; PRBool SafeToCreateCanvas3DContext(nsHTMLCanvasElement *canvasElement); PRBool InitAndValidateGL(); diff --git a/content/canvas/src/WebGLContextGL.cpp b/content/canvas/src/WebGLContextGL.cpp index ef9b42ba4072..ccb4b060d252 100644 --- a/content/canvas/src/WebGLContextGL.cpp +++ b/content/canvas/src/WebGLContextGL.cpp @@ -63,6 +63,15 @@ using namespace mozilla; +template +bool is_power_of_two(T x) +{ + if (x <= 0) + return false; + else + return (x & (x-1)) == 0; +} + static PRBool BaseTypeAndSizeFromUniformType(WebGLenum uType, WebGLenum *baseType, WebGLint *unitSize); /* Helper macros for when we're just wrapping a gl method, so that @@ -572,6 +581,9 @@ WebGLContext::CopyTexImage2D(WebGLenum target, if (!CanvasUtils::CheckSaneSubrectSize(x,y,width, height, mWidth, mHeight)) return ErrorInvalidOperation("CopyTexImage2D: copied rectangle out of bounds"); + if (!activeBoundTextureForTarget(target)) + return ErrorInvalidOperation("copyTexImage2D: no texture bound to this target"); + MakeContextCurrent(); gl->fCopyTexImage2D(target, level, internalformat, x, y, width, height, border); @@ -1138,6 +1150,13 @@ WebGLContext::GenerateMipmap(WebGLenum target) if (!ValidateTextureTargetEnum(target, "generateMipmap")) return NS_OK; + WebGLTexture *tex = activeBoundTextureForTarget(target); + + if (!(is_power_of_two(tex->width()) || + is_power_of_two(tex->height()))) { + return ErrorInvalidOperation("generateMipmap: texture width and height must be powers of two"); + } + MakeContextCurrent(); gl->fGenerateMipmap(target); return NS_OK; @@ -3070,20 +3089,20 @@ WebGLContext::TexImage2D_base(WebGLenum target, WebGLint level, WebGLenum intern { 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) + return ErrorInvalidValue("texImage2D: with cube map targets, width and height must be equal"); break; default: return ErrorInvalidEnumInfo("texImage2D: target", target); } - if (level < 0) - return ErrorInvalidValue("TexImage2D: level must be >= 0"); - switch (internalformat) { case LOCAL_GL_RGB: case LOCAL_GL_RGBA: @@ -3092,11 +3111,30 @@ WebGLContext::TexImage2D_base(WebGLenum target, WebGLint level, WebGLenum intern case LOCAL_GL_LUMINANCE_ALPHA: break; default: - return ErrorInvalidEnumInfo("TexImage2D: internal format", internalformat); + return ErrorInvalidEnumInfo("texImage2D: internal format", internalformat); } + if (format != internalformat) + return ErrorInvalidOperation("texImage2D: format does not match internalformat"); + + WebGLsizei maxTextureSize = target == LOCAL_GL_TEXTURE_2D ? mGLMaxTextureSize : mGLMaxCubeMapTextureSize; + + if (level < 0) + return ErrorInvalidValue("texImage2D: level must be >= 0"); + + if ((1 << level) > maxTextureSize) + return ErrorInvalidValue("texImage2D: 2^level exceeds maximum texture size"); + if (width < 0 || height < 0) - return ErrorInvalidValue("TexImage2D: width and height must be >= 0"); + return ErrorInvalidValue("texImage2D: width and height must be >= 0"); + + if (width > maxTextureSize || height > maxTextureSize) + return ErrorInvalidValue("texImage2D: width or height exceeds maximum texture size"); + + if (level >= 1) { + if (!(is_power_of_two(width) && is_power_of_two(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"); @@ -3111,11 +3149,16 @@ WebGLContext::TexImage2D_base(WebGLenum target, WebGLint level, WebGLenum intern return ErrorInvalidOperation("texImage2D: integer overflow computing the needed buffer size"); PRUint32 bytesNeeded = checked_bytesNeeded.value(); - + if (byteLength && byteLength < bytesNeeded) return ErrorInvalidOperation("TexImage2D: not enough data for operation (need %d, have %d)", bytesNeeded, byteLength); + WebGLTexture *tex = activeBoundTextureForTarget(target); + + if (!tex) + return ErrorInvalidOperation("texImage2D: no texture is bound to this target"); + MakeContextCurrent(); if (byteLength) { @@ -3133,8 +3176,7 @@ WebGLContext::TexImage2D_base(WebGLenum target, WebGLint level, WebGLenum intern free(tempZeroData); } - if (mBound2DTextures[mActiveTexture]) - mBound2DTextures[mActiveTexture]->setDimensions(width, height); + tex->setDimensions(width, height); return NS_OK; } @@ -3208,11 +3250,24 @@ WebGLContext::TexSubImage2D_base(WebGLenum target, WebGLint level, return ErrorInvalidEnumInfo("texSubImage2D: target", target); } + WebGLsizei maxTextureSize = target == LOCAL_GL_TEXTURE_2D ? mGLMaxTextureSize : mGLMaxCubeMapTextureSize; + if (level < 0) - return ErrorInvalidValue("TexSubImage2D: level must be >= 0"); + return ErrorInvalidValue("texSubImage2D: level must be >= 0"); + + if ((1 << level) > maxTextureSize) + return ErrorInvalidValue("texSubImage2D: 2^level exceeds maximum texture size"); if (width < 0 || height < 0) - return ErrorInvalidValue("TexSubImage2D: width and height must be > 0!"); + return ErrorInvalidValue("texSubImage2D: width and height must be >= 0"); + + if (width > maxTextureSize || height > maxTextureSize) + return ErrorInvalidValue("texSubImage2D: width or height exceeds maximum texture size"); + + if (level >= 1) { + if (!(is_power_of_two(width) && is_power_of_two(height))) + return ErrorInvalidValue("texSubImage2D: with level > 0, width and height must be powers of two"); + } PRUint32 texelSize = 0; if (!ValidateTexFormatAndType(format, type, &texelSize, "texSubImage2D")) @@ -3229,7 +3284,15 @@ WebGLContext::TexSubImage2D_base(WebGLenum target, WebGLint level, PRUint32 bytesNeeded = checked_bytesNeeded.value(); if (byteLength < bytesNeeded) - return ErrorInvalidValue("TexSubImage2D: not enough data for operation (need %d, have %d)", bytesNeeded, byteLength); + return ErrorInvalidValue("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 (!CanvasUtils::CheckSaneSubrectSize(xoffset, yoffset, width, height, tex->width(), tex->height())) + return ErrorInvalidValue("texSubImage2D: subtexture rectangle out of bounds"); MakeContextCurrent();