void
WebGLContext::BindVertexArray(WebGLVertexArray* array)
{
    if (IsContextLost())
        return;

    if (!ValidateObjectAllowDeletedOrNull("bindVertexArrayObject", array))
        return;

    if (array && array->IsDeleted()) {
        /* http://www.khronos.org/registry/gles/extensions/OES/OES_vertex_array_object.txt
         * BindVertexArrayOES fails and an INVALID_OPERATION error is
         * generated if array is not a name returned from a previous call to
         * GenVertexArraysOES, or if such a name has since been deleted with
         * DeleteVertexArraysOES
         */
        ErrorInvalidOperation("bindVertexArray: can't bind a deleted array!");
        return;
    }

    InvalidateBufferFetching();

    MakeContextCurrent();

    if (array == nullptr) {
        array = mDefaultVertexArray;
    }

    array->BindVertexArray();

    MOZ_ASSERT(mBoundVertexArray == array);
}
void
WebGLContext::VertexAttribPointer(GLuint index, GLint size, GLenum type,
                                  WebGLboolean normalized, GLsizei stride,
                                  WebGLintptr byteOffset)
{
    if (IsContextLost())
        return;

    if (!ValidateAttribIndex(index, "vertexAttribPointer"))
        return;

    if (!ValidateAttribPointer(false, index, size, type, normalized, stride, byteOffset, "vertexAttribPointer"))
        return;

    MOZ_ASSERT(mBoundVertexArray);

    InvalidateBufferFetching();

    /* XXX make work with bufferSubData & heterogeneous types
     if (type != mBoundArrayBuffer->GLType())
     return ErrorInvalidOperation("vertexAttribPointer: type must match bound VBO type: %d != %d", type, mBoundArrayBuffer->GLType());
     */

    MakeContextCurrent();
    gl->fVertexAttribPointer(index, size, type, normalized, stride,
                             reinterpret_cast<void*>(byteOffset));

    WebGLVertexAttribData& vd = mBoundVertexArray->mAttribs[index];
    const bool integerFunc = false;
    vd.VertexAttribPointer(integerFunc, mBoundArrayBuffer, size, type, normalized, stride,
                           byteOffset);
}
void
WebGL2Context::VertexAttribIPointer(GLuint index, GLint size, GLenum type, GLsizei stride,
                                    GLintptr offset)
{
  if (IsContextLost())
    return;

  if (!ValidateAttribIndex(index, "vertexAttribIPointer"))
    return;

  if (!ValidateAttribPointer(true, index, size, type, LOCAL_GL_FALSE, stride, offset,
                             "vertexAttribIPointer"))
  {
    return;
  }

  MOZ_ASSERT(mBoundVertexArray);
  mBoundVertexArray->EnsureAttrib(index);

  InvalidateBufferFetching();

  WebGLVertexAttribData& vd = mBoundVertexArray->mAttribs[index];
  vd.buf = mBoundArrayBuffer;
  vd.stride = stride;
  vd.size = size;
  vd.byteOffset = offset;
  vd.type = type;
  vd.normalized = false;
  vd.integer = true;

  MakeContextCurrent();
  gl->fVertexAttribIPointer(index, size, type, stride, reinterpret_cast<void*>(offset));
}
void
WebGLContext::BindVertexArray(WebGLVertexArray* array)
{
    if (IsContextLost())
        return;

    if (array && !ValidateObject("bindVertexArrayObject", *array))
        return;

    InvalidateBufferFetching();

    MakeContextCurrent();

    if (mBoundVertexArray) {
        mBoundVertexArray->AddBufferBindCounts(-1);
    }

    if (array == nullptr) {
        array = mDefaultVertexArray;
    }

    array->BindVertexArray();

    MOZ_ASSERT(mBoundVertexArray == array);
    if (mBoundVertexArray) {
        mBoundVertexArray->AddBufferBindCounts(+1);
    }
}
void
WebGLContext::EnableVertexAttribArray(GLuint index)
{
    if (IsContextLost())
        return;

    if (!ValidateAttribIndex(index, "enableVertexAttribArray"))
        return;

    MakeContextCurrent();
    InvalidateBufferFetching();

    gl->fEnableVertexAttribArray(index);

    MOZ_ASSERT(mBoundVertexArray);
    mBoundVertexArray->mAttribs[index].mEnabled = true;
}
void
WebGLContext::BufferData(GLenum target, WebGLsizeiptr size, GLenum usage)
{
    if (IsContextLost())
        return;

    if (!ValidateBufferTarget(target, "bufferData"))
        return;

    WebGLRefPtr<WebGLBuffer>& bufferSlot = GetBufferSlotByTarget(target);

    if (size < 0)
        return ErrorInvalidValue("bufferData: negative size");

    if (!ValidateBufferUsageEnum(usage, "bufferData: usage"))
        return;

    // careful: WebGLsizeiptr is always 64-bit, but GLsizeiptr is like intptr_t.
    if (!CheckedInt<GLsizeiptr>(size).isValid())
        return ErrorOutOfMemory("bufferData: bad size");

    WebGLBuffer* boundBuffer = bufferSlot.get();

    if (!boundBuffer)
        return ErrorInvalidOperation("bufferData: no buffer bound!");

    UniquePtr<uint8_t> zeroBuffer((uint8_t*)calloc(size, 1));
    if (!zeroBuffer)
        return ErrorOutOfMemory("bufferData: out of memory");

    MakeContextCurrent();
    InvalidateBufferFetching();

    GLenum error = CheckedBufferData(target, size, zeroBuffer.get(), usage);

    if (error) {
        GenerateWarning("bufferData generated error %s", ErrorName(error));
        return;
    }

    boundBuffer->SetByteLength(size);
    if (!boundBuffer->ElementArrayCacheBufferData(nullptr, size)) {
        return ErrorOutOfMemory("bufferData: out of memory");
    }
}
void
WebGLContext::DisableVertexAttribArray(GLuint index)
{
    if (IsContextLost())
        return;

    if (!ValidateAttribIndex(index, "disableVertexAttribArray"))
        return;

    MakeContextCurrent();
    InvalidateBufferFetching();

    if (index || !gl->IsCompatibilityProfile()) {
        gl->fDisableVertexAttribArray(index);
    }

    MOZ_ASSERT(mBoundVertexArray);
    mBoundVertexArray->mAttribs[index].mEnabled = false;
}
void
WebGLContext::BufferDataT(GLenum target,
                          const BufferT& data,
                          GLenum usage)
{
    if (IsContextLost())
        return;

    if (!ValidateBufferTarget(target, "bufferData"))
        return;

    const WebGLRefPtr<WebGLBuffer>& bufferSlot = GetBufferSlotByTarget(target);

    data.ComputeLengthAndData();

    // Careful: data.Length() could conceivably be any uint32_t, but GLsizeiptr
    // is like intptr_t.
    if (!CheckedInt<GLsizeiptr>(data.Length()).isValid())
        return ErrorOutOfMemory("bufferData: bad size");

    if (!ValidateBufferUsageEnum(usage, "bufferData: usage"))
        return;

    WebGLBuffer* boundBuffer = bufferSlot.get();

    if (!boundBuffer)
        return ErrorInvalidOperation("bufferData: no buffer bound!");

    MakeContextCurrent();
    InvalidateBufferFetching();

    GLenum error = CheckedBufferData(target, data.Length(), data.Data(), usage);

    if (error) {
        GenerateWarning("bufferData generated error %s", ErrorName(error));
        return;
    }

    boundBuffer->SetByteLength(data.Length());
    if (!boundBuffer->ElementArrayCacheBufferData(data.Data(), data.Length()))
        return ErrorOutOfMemory("bufferData: out of memory");
}
void
WebGLContext::VertexAttribDivisor(GLuint index, GLuint divisor)
{
    if (IsContextLost())
        return;

    if (!ValidateAttribIndex(index, "vertexAttribDivisor"))
        return;

    MOZ_ASSERT(mBoundVertexArray);

    WebGLVertexAttribData& vd = mBoundVertexArray->mAttribs[index];
    vd.mDivisor = divisor;

    InvalidateBufferFetching();

    MakeContextCurrent();

    gl->fVertexAttribDivisor(index, divisor);
}
void
WebGLContext::VertexAttribAnyPointer(const char* funcName, bool isFuncInt, GLuint index,
                                     GLint size, GLenum type, bool normalized,
                                     GLsizei stride, WebGLintptr byteOffset)
{
    if (IsContextLost())
        return;

    if (!ValidateAttribIndex(index, funcName))
        return;

    ////

    if (size < 1 || size > 4) {
        ErrorInvalidValue("%s: invalid element size", funcName);
        return;
    }

    // see WebGL spec section 6.6 "Vertex Attribute Data Stride"
    if (stride < 0 || stride > 255) {
        ErrorInvalidValue("%s: negative or too large stride", funcName);
        return;
    }

    if (byteOffset < 0) {
        ErrorInvalidValue("%s: negative offset", funcName);
        return;
    }

    ////

    bool isTypeValid = true;
    uint8_t typeAlignment;
    switch (type) {
    // WebGL 1:
    case LOCAL_GL_BYTE:
    case LOCAL_GL_UNSIGNED_BYTE:
        typeAlignment = 1;
        break;

    case LOCAL_GL_SHORT:
    case LOCAL_GL_UNSIGNED_SHORT:
        typeAlignment = 2;
        break;

    case LOCAL_GL_FLOAT:
        if (isFuncInt) {
            isTypeValid = false;
        }
        typeAlignment = 4;
        break;

    // WebGL 2:
    case LOCAL_GL_INT:
    case LOCAL_GL_UNSIGNED_INT:
        if (!IsWebGL2()) {
            isTypeValid = false;
        }
        typeAlignment = 4;
        break;

    case LOCAL_GL_HALF_FLOAT:
        if (isFuncInt || !IsWebGL2()) {
            isTypeValid = false;
        }
        typeAlignment = 2;
        break;

    case LOCAL_GL_FIXED:
        if (isFuncInt || !IsWebGL2()) {
            isTypeValid = false;
        }
        typeAlignment = 4;
        break;

    case LOCAL_GL_INT_2_10_10_10_REV:
    case LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV:
        if (isFuncInt || !IsWebGL2()) {
            isTypeValid = false;
            break;
        }
        if (size != 4) {
            ErrorInvalidOperation("%s: size must be 4 for this type.", funcName);
            return;
        }
        typeAlignment = 4;
        break;

    default:
        isTypeValid = false;
        break;
    }
    if (!isTypeValid) {
        ErrorInvalidEnumArg(funcName, "type", type);
        return;
    }

    ////

    // `alignment` should always be a power of two.
    MOZ_ASSERT(IsPowerOfTwo(typeAlignment));
    const GLsizei typeAlignmentMask = typeAlignment - 1;

    if (stride & typeAlignmentMask ||
        byteOffset & typeAlignmentMask)
    {
        ErrorInvalidOperation("%s: `stride` and `byteOffset` must satisfy the alignment"
                              " requirement of `type`.",
                              funcName);
        return;
    }

    ////

    const auto& buffer = mBoundArrayBuffer;
    if (!buffer && byteOffset) {
        ErrorInvalidOperation("%s: If ARRAY_BUFFER is null, byteOffset must be zero.",
                              funcName);
        return;
    }

    ////

    gl->MakeCurrent();
    if (isFuncInt) {
        gl->fVertexAttribIPointer(index, size, type, stride,
                                  reinterpret_cast<void*>(byteOffset));
    } else {
        gl->fVertexAttribPointer(index, size, type, normalized, stride,
                                 reinterpret_cast<void*>(byteOffset));
    }

    WebGLVertexAttribData& vd = mBoundVertexArray->mAttribs[index];
    vd.VertexAttribPointer(isFuncInt, buffer, size, type, normalized, stride, byteOffset);

    InvalidateBufferFetching();
}