b=570538; [webgl] fix up buffer validation with interleaved data; r=bjacob
authorVladimir Vukicevic <vladimir@pobox.com>
Tue, 08 Jun 2010 15:14:43 -0700
changeset 43333 f6f0a6bba978c4c3ea2b70d4f123cfa421758748
parent 43332 a02a4c380d0be7af3cc6994a80e3074c883a5e37
child 43334 c448572f61cfe18328f80c720589b038ade06dc4
push id13657
push uservladimir@mozilla.com
push dateTue, 08 Jun 2010 22:16:34 +0000
treeherdermozilla-central@f6f0a6bba978 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbjacob
bugs570538
milestone1.9.3a5pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
b=570538; [webgl] fix up buffer validation with interleaved data; r=bjacob
content/canvas/src/WebGLContext.h
content/canvas/src/WebGLContextGL.cpp
content/canvas/src/WebGLContextValidate.cpp
content/canvas/test/webgl/conformance/draw-arrays-out-of-bounds.html
content/canvas/test/webgl/conformance/draw-elements-out-of-bounds.html
dom/interfaces/canvas/nsICanvasRenderingContextWebGL.idl
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -216,38 +216,43 @@ struct WebGLVertexAttribData {
 
     WebGLObjectRefPtr<WebGLBuffer> buf;
     WebGLuint stride;
     WebGLuint size;
     GLuint byteOffset;
     GLenum type;
     PRBool enabled;
 
-    GLuint actualStride() const {
-        if (stride) return stride;
-        GLuint componentSize = 0;
+    GLuint componentSize() const {
         switch(type) {
             case LOCAL_GL_BYTE:
-                componentSize = sizeof(GLbyte);
+                return sizeof(GLbyte);
                 break;
             case LOCAL_GL_UNSIGNED_BYTE:
-                componentSize = sizeof(GLubyte);
+                return sizeof(GLubyte);
                 break;
             case LOCAL_GL_SHORT:
-                componentSize = sizeof(GLshort);
+                return sizeof(GLshort);
                 break;
             case LOCAL_GL_UNSIGNED_SHORT:
-                componentSize = sizeof(GLushort);
+                return sizeof(GLushort);
                 break;
             // XXX case LOCAL_GL_FIXED:
             case LOCAL_GL_FLOAT:
-                componentSize = sizeof(GLfloat);
+                return sizeof(GLfloat);
                 break;
+            default:
+                NS_ERROR("Should never get here!");
+                return 0;
         }
-        return size * componentSize;
+    }
+
+    GLuint actualStride() const {
+        if (stride) return stride;
+        return size * componentSize();
     }
 };
 
 class WebGLContext :
     public nsICanvasRenderingContextWebGL,
     public nsICanvasRenderingContextInternal,
     public nsSupportsWeakReference
 {
--- a/content/canvas/src/WebGLContextGL.cpp
+++ b/content/canvas/src/WebGLContextGL.cpp
@@ -798,91 +798,100 @@ WebGLContext::DrawArrays(GLenum mode, We
         case LOCAL_GL_LINE_STRIP:
         case LOCAL_GL_LINE_LOOP:
         case LOCAL_GL_LINES:
             break;
         default:
             return ErrorInvalidEnum("DrawArrays: invalid mode");
     }
 
-    if (first < 0 || count < 0 || first+count < first || first+count < count) {
-        return ErrorInvalidValue("DrawArrays: overflow in first+count");
-    }
+    if (first < 0 || count < 0)
+        return ErrorInvalidValue("DrawArrays: negative first or count");
+
+    // If count is 0, there's nothing to do.
+    if (count == 0)
+        return NS_OK;
 
     // If there is no current program, this is silently ignored.
     // Any checks below this depend on a program being available.
     if (!mCurrentProgram)
         return NS_OK;
 
+    if (first+count < first || first+count < count)
+        return ErrorInvalidOperation("DrawArrays: overflow in first+count");
+
     if (!ValidateBuffers(first+count))
         return ErrorInvalidOperation("DrawArrays: bound vertex attribute buffers do not have sufficient data for given first and count");
 
     MakeContextCurrent();
 
     gl->fDrawArrays(mode, first, count);
 
     Invalidate();
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
-WebGLContext::DrawElements(WebGLenum mode, WebGLuint count, WebGLenum type, WebGLuint byteOffset)
+WebGLContext::DrawElements(WebGLenum mode, WebGLsizei count, WebGLenum type, WebGLint byteOffset)
 {
-    int elementSize = 0;
-
     switch (mode) {
         case LOCAL_GL_TRIANGLES:
         case LOCAL_GL_TRIANGLE_STRIP:
         case LOCAL_GL_TRIANGLE_FAN:
         case LOCAL_GL_POINTS:
         case LOCAL_GL_LINE_STRIP:
         case LOCAL_GL_LINE_LOOP:
         case LOCAL_GL_LINES:
             break;
         default:
             return ErrorInvalidEnum("DrawElements: invalid mode");
     }
 
-    switch (type) {
-        case LOCAL_GL_UNSIGNED_SHORT:
-            elementSize = 2;
-            if (byteOffset % 2 != 0)
-                 return ErrorInvalidValue("DrawElements: invalid byteOffset for UNSIGNED_SHORT (must be a multiple of 2)");
-            break;
-
-        case LOCAL_GL_UNSIGNED_BYTE:
-            elementSize = 1;
-            break;
-
-        default:
-            return ErrorInvalidEnum("DrawElements: type must be UNSIGNED_SHORT or UNSIGNED_BYTE");
+    if (count < 0 || byteOffset < 0)
+        return ErrorInvalidValue("DrawElements: negative count or offset");
+
+    WebGLuint byteCount;
+    if (type == LOCAL_GL_UNSIGNED_SHORT) {
+        byteCount = WebGLuint(count) << 1;
+        if (byteCount >> 1 != WebGLuint(count))
+            return ErrorInvalidValue("DrawElements: overflow in byteCount");
+
+        if (byteOffset % 2 != 0)
+            return ErrorInvalidValue("DrawElements: invalid byteOffset for UNSIGNED_SHORT (must be a multiple of 2)");
+    } else if (type == LOCAL_GL_UNSIGNED_BYTE) {
+        byteCount = count;
+    } else {
+        return ErrorInvalidEnum("DrawElements: type must be UNSIGNED_SHORT or UNSIGNED_BYTE");
     }
 
-    if (!mBoundElementArrayBuffer)
-        return ErrorInvalidOperation("DrawElements: must have element array buffer binding");
-
-    WebGLuint byteCount = count*elementSize;
-
-    if (count < 0 || byteOffset+byteCount < byteOffset || byteOffset+byteCount < byteCount)
-        return ErrorInvalidValue("DrawElements: overflow in byteOffset+byteCount");
-
-    if (byteOffset + byteCount > mBoundElementArrayBuffer->ByteLength())
-        return ErrorInvalidOperation("DrawElements: bound element array buffer is too small for given count and offset");
+    // If count is 0, there's nothing to do.
+    if (count == 0)
+        return NS_OK;
 
     // If there is no current program, this is silently ignored.
     // Any checks below this depend on a program being available.
     if (!mCurrentProgram)
         return NS_OK;
 
+    if (!mBoundElementArrayBuffer)
+        return ErrorInvalidOperation("DrawElements: must have element array buffer binding");
+
+    if (byteOffset+byteCount < byteOffset || byteOffset+byteCount < byteCount)
+        return ErrorInvalidOperation("DrawElements: overflow in byteOffset+byteCount");
+
+    if (byteOffset + byteCount > mBoundElementArrayBuffer->ByteLength())
+        return ErrorInvalidOperation("DrawElements: bound element array buffer is too small for given count and offset");
+
     WebGLuint maxIndex = 0;
-    if (type == LOCAL_GL_UNSIGNED_SHORT)
+    if (type == LOCAL_GL_UNSIGNED_SHORT) {
         maxIndex = mBoundElementArrayBuffer->FindMaximum<GLushort>(count, byteOffset);
-    else if (type == LOCAL_GL_UNSIGNED_BYTE)
+    } else if (type == LOCAL_GL_UNSIGNED_BYTE) {
         maxIndex = mBoundElementArrayBuffer->FindMaximum<GLubyte>(count, byteOffset);
+    }
 
     // maxIndex+1 because ValidateBuffers expects the number of elements needed
     if (!ValidateBuffers(maxIndex+1)) {
         return ErrorInvalidOperation("DrawElements: bound vertex attribute buffers do not have sufficient "
                                      "data for given indices from the bound element array");
     }
 
     MakeContextCurrent();
@@ -1027,26 +1036,31 @@ WebGLContext::GetActiveAttrib(nsIWebGLPr
     NativeJSContext js;
     if (NS_FAILED(js.error))
         return js.error;
 
     MakeContextCurrent();
 
     GLint len = 0;
     gl->fGetProgramiv(progname, LOCAL_GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &len);
-    if (len == 0)
-        return NS_ERROR_FAILURE; // XXX GL error?  This really shouldn't happen.
+    if (len == 0) {
+        // is this an error?  can you have a program with no attributes?
+        *retval = nsnull;
+        return NS_OK;
+    }
 
     nsAutoArrayPtr<char> name(new char[len+1]);
     PRInt32 attrsize = 0;
     PRUint32 attrtype = 0;
 
     gl->fGetActiveAttrib(progname, index, len+1, &len, (GLint*) &attrsize, (WebGLuint*) &attrtype, name);
-    if (attrsize == 0 || attrtype == 0)
-        return NS_ERROR_FAILURE;
+    if (attrsize == 0 || attrtype == 0) {
+        *retval = nsnull;
+        return NS_OK;
+    }
 
     JSObjectHelper retobj(&js);
     retobj.DefineProperty("size", attrsize);
     retobj.DefineProperty("type", attrtype);
     retobj.DefineProperty("name", name, len);
 
     js.SetRetVal(retobj);
 
--- a/content/canvas/src/WebGLContextValidate.cpp
+++ b/content/canvas/src/WebGLContextValidate.cpp
@@ -46,16 +46,18 @@ using namespace mozilla;
  */
 
 PRBool
 WebGLContext::ValidateBuffers(PRUint32 count)
 {
     GLint currentProgram = -1;
     GLint numAttributes = -1;
 
+    NS_ENSURE_TRUE(count > 0, PR_TRUE);
+
     MakeContextCurrent();
 
     // XXX cache this per program
     gl->fGetIntegerv(LOCAL_GL_CURRENT_PROGRAM, &currentProgram);
     if (currentProgram == -1) {
         // what?
         LogMessage("glGetIntegerv GL_CURRENT_PROGRAM failed: 0x%08x", (uint) gl->fGetError());
         return PR_FALSE;
@@ -88,17 +90,20 @@ WebGLContext::ValidateBuffers(PRUint32 c
       if (!vd.enabled)
           continue;
 
       if (vd.buf == nsnull) {
           LogMessage("No VBO bound to index %d (or it's been deleted)!", i);
           return PR_FALSE;
       }
 
-      WebGLuint needed = vd.byteOffset + vd.actualStride() * count;
+      WebGLuint needed = vd.byteOffset +     // the base offset
+          vd.actualStride() * (count-1) +    // to stride to the start of the last element group
+          vd.componentSize() * vd.size;      // and the number of bytes needed for these components
+
       if (vd.buf->ByteLength() < needed) {
           LogMessage("VBO too small for bound attrib index %d: need at least %d bytes, but have only %d", i, needed, vd.buf->ByteLength());
           return PR_FALSE;
       }
     }
 
     return PR_TRUE;
 }
--- a/content/canvas/test/webgl/conformance/draw-arrays-out-of-bounds.html
+++ b/content/canvas/test/webgl/conformance/draw-arrays-out-of-bounds.html
@@ -45,33 +45,82 @@ context.enableVertexAttribArray(0);
 
 debug("Test empty buffer")
 context.bufferData(context.ARRAY_BUFFER, new WebGLFloatArray([  ]), context.STATIC_DRAW);
 context.vertexAttribPointer(0, 3, context.FLOAT, false, 0, 0);
 shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawArrays(context.TRIANGLES, 0, 1)");
 shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawArrays(context.TRIANGLES, 0, 10000)");
 shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawArrays(context.TRIANGLES, 0, 10000000000000)");
 shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawArrays(context.TRIANGLES, 0, -1)");
-shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawArrays(context.TRIANGLES, 1, 0)");
+shouldGenerateGLError(context, context.NO_ERROR, "context.drawArrays(context.TRIANGLES, 1, 0)");
 shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawArrays(context.TRIANGLES, -1, 0)");
-shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawArrays(context.TRIANGLES, 1, -1)");
-shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawArrays(context.TRIANGLES, -1, 1)");
+shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawArrays(context.TRIANGLES, 1, -1)");
+shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawArrays(context.TRIANGLES, -1, 1)");
 
 debug("")
 debug("Test buffer with 3 float vectors")
 context.bufferData(context.ARRAY_BUFFER, new WebGLFloatArray([ 0,0.5,0, -0.5,-0.5,0, 0.5,-0.5,0 ]), context.STATIC_DRAW);
 context.vertexAttribPointer(0, 3, context.FLOAT, false, 0, 0);
 shouldGenerateGLError(context, context.NO_ERROR, "context.drawArrays(context.TRIANGLES, 0, 3)");
 shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawArrays(context.TRIANGLES, 3, 2)");
 shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawArrays(context.TRIANGLES, 0, 10000)");
 shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawArrays(context.TRIANGLES, 0, 10000000000000)");
 shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawArrays(context.TRIANGLES, 0, -1)");
 shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawArrays(context.TRIANGLES, -1, 0)");
-shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawArrays(context.TRIANGLES, 1, -1)");
-shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawArrays(context.TRIANGLES, -1, 1)");
+shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawArrays(context.TRIANGLES, 1, -1)");
+shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawArrays(context.TRIANGLES, -1, 1)");
+
+debug("")
+debug("Test buffer with interleaved (3+2) float vectors")
+
+var program2 = createProgram(context,
+			     "attribute vec3 aOne;" + 
+			     "attribute vec2 aTwo;" +
+			     "void main() { gl_Position = vec4(aOne, 1.0) + vec4(aTwo, 0.0, 1.0); }",
+			     "void main() { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); }",
+			     [ "aOne", "aTwo" ]);
+if (!program2) {
+    testFailed("failed to create test program");
+}
+
+context.useProgram(program2);
+
+var vbo = context.createBuffer();
+context.bindBuffer(context.ARRAY_BUFFER, vbo);
+// enough for 9 vertices, so 3 triangles
+context.bufferData(context.ARRAY_BUFFER, new WebGLFloatArray(9*5), context.STATIC_DRAW);
+
+// bind first 3 elements, with a stride of 5 float elements
+context.vertexAttribPointer(0, 3, context.FLOAT, false, 5*4, 0);
+// bind 2 elements, starting after the first 3; same stride of 5 float elements
+context.vertexAttribPointer(1, 2, context.FLOAT, false, 5*4, 3*4);
+
+context.enableVertexAttribArray(0);
+context.enableVertexAttribArray(1);
+
+shouldGenerateGLError(context, context.NO_ERROR, "context.drawArrays(context.TRIANGLES, 0, 9)");
+
+// negative values must generate INVALID_VALUE; they can never be valid
+shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawArrays(context.TRIANGLES, 0, -500)");
+shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawArrays(context.TRIANGLES, -200, 1)");
+shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawArrays(context.TRIANGLES, -200, -500)");
+
+// 0xffffffff needs to convert to a 'long' IDL argument as -1, as per
+// WebIDL 4.1.7.  JS ToInt32(0xffffffff) == -1, which is the first step
+// of the conversion.  Thus INVALID_VALUE.
+shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawArrays(context.TRIANGLES, 0, 0xffffffff)");
+shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawArrays(context.TRIANGLES, 0xffffffff, 1)");
+shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawArrays(context.TRIANGLES, 0xffffffff, 0xffffffff)");
+
+// values that could otherwise be valid but aren't due to bindings generate
+// INVALID_OPERATION
+shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawArrays(context.TRIANGLES, 0, 200)");
+shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawArrays(context.TRIANGLES, 0, 0x7fffffff)");
+shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawArrays(context.TRIANGLES, 0x7fffffff, 1)");
+shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawArrays(context.TRIANGLES, 0x7fffffff, 0x7fffffff)");
 
 debug("")
 successfullyParsed = true;
 </script>
 
 <script src="../resources/js-test-post.js"></script>
 </body>
 </html>
--- a/content/canvas/test/webgl/conformance/draw-elements-out-of-bounds.html
+++ b/content/canvas/test/webgl/conformance/draw-elements-out-of-bounds.html
@@ -49,31 +49,86 @@ var indexObject = context.createBuffer()
 
 debug("Test empty index buffer")
 context.bindBuffer(context.ELEMENT_ARRAY_BUFFER, indexObject);
 context.bufferData(context.ELEMENT_ARRAY_BUFFER, new WebGLUnsignedByteArray([  ]), context.STATIC_DRAW);
 shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawElements(context.TRIANGLES, 3, context.UNSIGNED_BYTE, 0)");
 shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawElements(context.TRIANGLES, 10000, context.UNSIGNED_BYTE, 0)");
 shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawElements(context.TRIANGLES, 10000000000000, context.UNSIGNED_BYTE, 0)");
 shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawElements(context.TRIANGLES, 1, context.UNSIGNED_BYTE, 0)");
-shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawElements(context.TRIANGLES, 0, context.UNSIGNED_BYTE, -1)");
+shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawElements(context.TRIANGLES, 0, context.UNSIGNED_BYTE, -1)");
 shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawElements(context.TRIANGLES, -1, context.UNSIGNED_BYTE, 1)");
-shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawElements(context.TRIANGLES, 1, context.UNSIGNED_BYTE, -1)");
+shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawElements(context.TRIANGLES, 1, context.UNSIGNED_BYTE, -1)");
 
 debug("")
 debug("Test buffer with 3 byte indexes")
 context.bindBuffer(context.ELEMENT_ARRAY_BUFFER, indexObject);
 context.bufferData(context.ELEMENT_ARRAY_BUFFER, new WebGLUnsignedByteArray([ 0, 1, 2 ]), context.STATIC_DRAW);
 shouldGenerateGLError(context, context.NO_ERROR, "context.drawElements(context.TRIANGLES, 3, context.UNSIGNED_BYTE, 0)");
 shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawElements(context.TRIANGLES, 3, context.UNSIGNED_BYTE, 2)");
 shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawElements(context.TRIANGLES, 10000, context.UNSIGNED_BYTE, 0)");
 shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawElements(context.TRIANGLES, 10000000000000, context.UNSIGNED_BYTE, 0)");
 shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawElements(context.TRIANGLES, 0, context.UNSIGNED_BYTE, -1)");
 shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawElements(context.TRIANGLES, -1, context.UNSIGNED_BYTE, 1)");
 shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawElements(context.TRIANGLES, 1, context.UNSIGNED_BYTE, -1)");
 
 debug("")
+debug("Test buffer with interleaved (3+2) float vectors")
+
+var program2 = createProgram(context,
+			     "attribute vec3 aOne;" + 
+			     "attribute vec2 aTwo;" +
+			     "void main() { gl_Position = vec4(aOne, 1.0) + vec4(aTwo, 0.0, 1.0); }",
+			     "void main() { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); }",
+			     [ "aOne", "aTwo" ]);
+if (!program2) {
+    testFailed("failed to create test program");
+}
+
+context.useProgram(program2);
+
+var vbo = context.createBuffer();
+context.bindBuffer(context.ARRAY_BUFFER, vbo);
+// enough for 9 vertices, so 3 triangles
+context.bufferData(context.ARRAY_BUFFER, new WebGLFloatArray(9*5), context.STATIC_DRAW);
+
+// bind first 3 elements, with a stride of 5 float elements
+context.vertexAttribPointer(0, 3, context.FLOAT, false, 5*4, 0);
+// bind 2 elements, starting after the first 3; same stride of 5 float elements
+context.vertexAttribPointer(1, 2, context.FLOAT, false, 5*4, 3*4);
+
+context.enableVertexAttribArray(0);
+context.enableVertexAttribArray(1);
+
+var ebo = context.createBuffer();
+context.bindBuffer(context.ELEMENT_ARRAY_BUFFER, ebo);
+context.bufferData(context.ELEMENT_ARRAY_BUFFER, new WebGLUnsignedShortArray([ 0, 1, 2,
+									       1, 2, 0,
+									       2, 0, 1,
+									       200, 200, 200,
+									       0x7fff, 0x7fff, 0x7fff,
+									       0xffff, 0xffff, 0xffff ]),
+		   context.STATIC_DRAW);
+
+shouldGenerateGLError(context, context.NO_ERROR, "context.drawElements(context.TRIANGLES, 9, context.UNSIGNED_SHORT, 0)");
+
+// invalid type arguments
+shouldGenerateGLError(context, context.INVALID_ENUM, "context.drawElements(context.TRIANGLES, 9, context.FLOAT, 0)");
+shouldGenerateGLError(context, context.INVALID_ENUM, "context.drawElements(context.TRIANGLES, 9, context.SHORT, 0)");
+shouldGenerateGLError(context, context.INVALID_ENUM, "context.drawElements(context.TRIANGLES, 9, context.UNSIGNED_INT, 0)");
+
+// invalid operation with indices that would be valid with correct bindings
+shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawElements(context.TRIANGLES, 9, context.UNSIGNED_SHORT, 1000)");
+shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawElements(context.TRIANGLES, 12, context.UNSIGNED_SHORT, 0)");
+shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawElements(context.TRIANGLES, 15, context.UNSIGNED_SHORT, 0)");
+shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawElements(context.TRIANGLES, 18, context.UNSIGNED_SHORT, 0)");
+shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawElements(context.TRIANGLES, 3, context.UNSIGNED_SHORT, 2*15)");
+
+shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawElements(context.TRIANGLES, 0xffffffff, context.UNSIGNED_SHORT, 0)");
+shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawElements(context.TRIANGLES, 0x7fffffff, context.UNSIGNED_SHORT, 0)");
+
+debug("")
 successfullyParsed = true;
 </script>
 
 <script src="../resources/js-test-post.js"></script>
 </body>
 </html>
--- a/dom/interfaces/canvas/nsICanvasRenderingContextWebGL.idl
+++ b/dom/interfaces/canvas/nsICanvasRenderingContextWebGL.idl
@@ -633,17 +633,17 @@ interface nsICanvasRenderingContextWebGL
   void depthMask(in WebGLboolean flag);
   void depthRange(in WebGLclampf zNear, in WebGLclampf zFar);
   void detachShader(in nsIWebGLProgram program, in nsIWebGLShader shader);
   void disable(in WebGLenum cap);
   void disableVertexAttribArray(in WebGLuint index);
   void drawArrays(in WebGLenum mode, in WebGLint first, in WebGLsizei count);
 
   // Modified: void glDrawElements(WebGLenum mode, WebGLsizei count, WebGLenum type, const void* indices);
-  void drawElements(in WebGLenum mode, in WebGLuint count, in WebGLenum type, in WebGLuint offset);
+  void drawElements(in WebGLenum mode, in WebGLsizei count, in WebGLenum type, in WebGLint offset);
 
   void enable(in WebGLenum cap);
   void enableVertexAttribArray(in WebGLuint index);
   void finish();
   void flush();
   void framebufferRenderbuffer(in WebGLenum target, in WebGLenum attachment, in WebGLenum renderbuffertarget, 
                                in nsIWebGLRenderbuffer renderbuffer);
   void framebufferTexture2D(in WebGLenum target, in WebGLenum attachment, in WebGLenum textarget,