Backed out 3 changesets (bug 1339256) for build bustage
authorIris Hsiao <ihsiao@mozilla.com>
Wed, 08 Mar 2017 17:02:45 +0800
changeset 380622 3bb0c61a3ee381aba22c15fefd9fc66f3d1b4245
parent 380621 a6bb817bae08cc0a5c53db17ab6c4b25df8bb79a
child 380623 1a2b467e5660da4ed8dc7a5ef3077b357aba087f
push id38
push userfmarier@mozilla.com
push dateThu, 23 Mar 2017 21:01:33 +0000
bugs1339256
milestone55.0a1
backs outa6bb817bae08cc0a5c53db17ab6c4b25df8bb79a
86fe1c44ac5a01699387b155430057c855ea3548
ed97037dae9c0674ca983d5fc037a39821583d18
Backed out 3 changesets (bug 1339256) for build bustage Backed out changeset a6bb817bae08 (bug 1339256) Backed out changeset 86fe1c44ac5a (bug 1339256) Backed out changeset ed97037dae9c (bug 1339256)
dom/canvas/WebGLBuffer.cpp
dom/canvas/WebGLBuffer.h
dom/canvas/WebGLContext.h
dom/canvas/WebGLContextBuffers.cpp
dom/canvas/WebGLContextDraw.cpp
dom/canvas/WebGLContextValidate.cpp
dom/canvas/WebGLElementArrayCache.cpp
dom/canvas/WebGLElementArrayCache.h
dom/canvas/WebGLTypes.h
dom/canvas/gtest/TestWebGLElementArrayCache.cpp
dom/canvas/gtest/moz.build
dom/canvas/moz.build
dom/canvas/test/webgl-conf/generated-mochitest.ini
dom/canvas/test/webgl-conf/mochitest-errata.ini
gfx/gl/GLContext.cpp
gfx/gl/GLContext.h
gfx/gl/GLContextCGL.h
gfx/gl/GLContextEAGL.h
gfx/gl/GLContextEGL.h
gfx/gl/GLContextFeatures.cpp
gfx/gl/GLContextGLX.h
gfx/gl/GLContextProviderEGL.cpp
gfx/gl/GLContextProviderGLX.cpp
gfx/gl/GLContextProviderWGL.cpp
gfx/gl/GLContextWGL.h
gfx/gl/GLDefs.h
gfx/gl/GLLibraryEGL.cpp
gfx/gl/GLLibraryEGL.h
gfx/thebes/gfxPrefs.h
modules/libpref/init/all.js
--- a/dom/canvas/WebGLBuffer.cpp
+++ b/dom/canvas/WebGLBuffer.cpp
@@ -3,16 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebGLBuffer.h"
 
 #include "GLContext.h"
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "WebGLContext.h"
+#include "WebGLElementArrayCache.h"
 
 namespace mozilla {
 
 WebGLBuffer::WebGLBuffer(WebGLContext* webgl, GLuint buf)
     : WebGLRefCountedObject(webgl)
     , mGLName(buf)
     , mContent(Kind::Undefined)
     , mUsage(LOCAL_GL_STATIC_DRAW)
@@ -32,16 +33,19 @@ void
 WebGLBuffer::SetContentAfterBind(GLenum target)
 {
     if (mContent != Kind::Undefined)
         return;
 
     switch (target) {
     case LOCAL_GL_ELEMENT_ARRAY_BUFFER:
         mContent = Kind::ElementArray;
+        if (!mCache) {
+            mCache.reset(new WebGLElementArrayCache);
+        }
         break;
 
     case LOCAL_GL_ARRAY_BUFFER:
     case LOCAL_GL_PIXEL_PACK_BUFFER:
     case LOCAL_GL_PIXEL_UNPACK_BUFFER:
     case LOCAL_GL_UNIFORM_BUFFER:
     case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
     case LOCAL_GL_COPY_READ_BUFFER:
@@ -55,18 +59,17 @@ WebGLBuffer::SetContentAfterBind(GLenum 
 }
 
 void
 WebGLBuffer::Delete()
 {
     mContext->MakeContextCurrent();
     mContext->gl->fDeleteBuffers(1, &mGLName);
     mByteLength = 0;
-    mIndexCache = nullptr;
-    mIndexRanges.clear();
+    mCache = nullptr;
     LinkedListElement<WebGLBuffer>::remove(); // remove from mContext->mBuffers
 }
 
 ////////////////////////////////////////
 
 static bool
 ValidateBufferUsageEnum(WebGLContext* webgl, const char* funcName, GLenum usage)
 {
@@ -102,107 +105,56 @@ WebGLBuffer::BufferData(GLenum target, s
     // Careful: data.Length() could conceivably be any uint32_t, but GLsizeiptr
     // is like intptr_t.
     if (!CheckedInt<GLsizeiptr>(size).isValid())
         return mContext->ErrorOutOfMemory("%s: bad size", funcName);
 
     if (!ValidateBufferUsageEnum(mContext, funcName, usage))
         return;
 
+    const auto& gl = mContext->gl;
+    gl->MakeCurrent();
+    const ScopedLazyBind lazyBind(gl, target, this);
+    mContext->InvalidateBufferFetching();
+
 #ifdef XP_MACOSX
     // bug 790879
-    if (mContext->gl->WorkAroundDriverBugs() &&
+    if (gl->WorkAroundDriverBugs() &&
         size > INT32_MAX)
     {
         mContext->ErrorOutOfMemory("%s: Allocation size too large.", funcName);
         return;
     }
 #endif
 
-    const void* uploadData = data;
-
-    UniqueBuffer newIndexCache;
-    if (target == LOCAL_GL_ELEMENT_ARRAY_BUFFER &&
-        mContext->mNeedsIndexValidation)
-    {
-        newIndexCache = malloc(size);
-        if (!newIndexCache) {
-            mContext->ErrorOutOfMemory("%s: Failed to alloc index cache.", funcName);
-            return;
-        }
-        memcpy(newIndexCache.get(), data, size);
-        uploadData = newIndexCache.get();
-    }
-
-    const auto& gl = mContext->gl;
-    gl->MakeCurrent();
-    const ScopedLazyBind lazyBind(gl, target, this);
-
     const bool sizeChanges = (size != ByteLength());
     if (sizeChanges) {
-        mContext->InvalidateBufferFetching();
-
         gl::GLContext::LocalErrorScope errorScope(*gl);
-        gl->fBufferData(target, size, uploadData, usage);
+        gl->fBufferData(target, size, data, usage);
         const auto error = errorScope.GetError();
 
         if (error) {
             MOZ_ASSERT(error == LOCAL_GL_OUT_OF_MEMORY);
             mContext->ErrorOutOfMemory("%s: Error from driver: 0x%04x", funcName, error);
             return;
         }
     } else {
-        gl->fBufferData(target, size, uploadData, usage);
+        gl->fBufferData(target, size, data, usage);
     }
 
     mUsage = usage;
     mByteLength = size;
-    mIndexCache = Move(newIndexCache);
 
-    if (mIndexCache) {
-        if (mIndexRanges.size()) {
-            mContext->GeneratePerfWarning("[%p] Invalidating %u ranges.", this,
-                                          uint32_t(mIndexRanges.size()));
-            mIndexRanges.clear();
-        }
+    // Warning: Possibly shared memory.  See bug 1225033.
+    if (!ElementArrayCacheBufferData(data, size)) {
+        mByteLength = 0;
+        mContext->ErrorOutOfMemory("%s: Failed update index buffer cache.", funcName);
     }
 }
 
-void
-WebGLBuffer::BufferSubData(GLenum target, size_t dstByteOffset, size_t dataLen,
-                           const void* data) const
-{
-    const char funcName[] = "bufferSubData";
-
-    if (!ValidateRange(funcName, dstByteOffset, dataLen))
-        return;
-
-    if (!CheckedInt<GLintptr>(dataLen).isValid())
-        return mContext->ErrorOutOfMemory("%s: Size too large.", funcName);
-
-    ////
-
-    const void* uploadData = data;
-    if (mIndexCache) {
-        const auto cachedDataBegin = (uint8_t*)mIndexCache.get() + dstByteOffset;
-        memcpy(cachedDataBegin, data, dataLen);
-        uploadData = cachedDataBegin;
-
-        InvalidateCacheRange(dstByteOffset, dataLen);
-    }
-
-    ////
-
-    const auto& gl = mContext->gl;
-    gl->MakeCurrent();
-    const ScopedLazyBind lazyBind(gl, target, this);
-
-    gl->fBufferSubData(target, dstByteOffset, dataLen, uploadData);
-}
-
 bool
 WebGLBuffer::ValidateRange(const char* funcName, size_t byteOffset, size_t byteLen) const
 {
     auto availLength = mByteLength;
     if (byteOffset > availLength) {
         mContext->ErrorInvalidValue("%s: Offset passes the end of the buffer.", funcName);
         return false;
     }
@@ -214,141 +166,64 @@ WebGLBuffer::ValidateRange(const char* f
         return false;
     }
 
     return true;
 }
 
 ////////////////////////////////////////
 
-static uint8_t
-IndexByteSizeByType(GLenum type)
+bool
+WebGLBuffer::ElementArrayCacheBufferData(const void* ptr,
+                                         size_t bufferSizeInBytes)
 {
-    switch (type) {
-    case LOCAL_GL_UNSIGNED_BYTE:  return 1;
-    case LOCAL_GL_UNSIGNED_SHORT: return 2;
-    case LOCAL_GL_UNSIGNED_INT:   return 4;
-    default:
-        MOZ_CRASH();
-    }
+    if (mContext->IsWebGL2())
+        return true;
+
+    if (mContent == Kind::ElementArray)
+        return mCache->BufferData(ptr, bufferSizeInBytes);
+
+    return true;
 }
 
 void
-WebGLBuffer::InvalidateCacheRange(size_t byteOffset, size_t byteLength) const
+WebGLBuffer::ElementArrayCacheBufferSubData(size_t pos, const void* ptr,
+                                            size_t updateSizeInBytes)
 {
-    MOZ_ASSERT(mIndexCache);
+    if (mContext->IsWebGL2())
+        return;
 
-    std::vector<IndexRange> invalids;
-    const size_t updateBegin = byteOffset;
-    const size_t updateEnd = updateBegin + byteLength;
-    for (const auto& cur : mIndexRanges) {
-        const auto& range = cur.first;
-        const auto& indexByteSize = IndexByteSizeByType(range.type);
-        const size_t rangeBegin = range.first * indexByteSize;
-        const size_t rangeEnd = rangeBegin + range.count*indexByteSize;
-        if (rangeBegin >= updateEnd || rangeEnd <= updateBegin)
-            continue;
-        invalids.push_back(range);
-    }
-
-    if (invalids.size()) {
-        mContext->GeneratePerfWarning("[%p] Invalidating %u/%u ranges.", this,
-                                      uint32_t(invalids.size()),
-                                      uint32_t(mIndexRanges.size()));
-
-        for (const auto& cur : invalids) {
-            mIndexRanges.erase(cur);
-        }
-    }
+    if (mContent == Kind::ElementArray)
+        mCache->BufferSubData(pos, ptr, updateSizeInBytes);
 }
 
 size_t
 WebGLBuffer::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
 {
-    size_t size = mallocSizeOf(this);
-    if (mIndexCache) {
-        size += mByteLength;
-    }
-    return size;
+    size_t sizeOfCache = mCache ? mCache->SizeOfIncludingThis(mallocSizeOf)
+                                : 0;
+    return mallocSizeOf(this) + sizeOfCache;
 }
 
-template<typename T>
-static size_t
-MaxForRange(const void* data, size_t first, size_t count, const uint32_t ignoredVal)
+bool
+WebGLBuffer::Validate(GLenum type, uint32_t maxAllowed, size_t first, size_t count) const
 {
-    const T ignoredTVal(ignoredVal);
-    T ret = 0;
-
-    auto itr = (const T*)data + first;
-    const auto end = itr + count;
-
-    for (; itr != end; ++itr) {
-        const auto& val = *itr;
-        if (val <= ret)
-            continue;
-
-        if (val == ignoredTVal)
-            continue;
-
-        ret = val;
-    }
-
-    return size_t(ret);
-}
-
-const uint32_t kMaxIndexRanges = 256;
-
-bool
-WebGLBuffer::ValidateIndexedFetch(GLenum type, uint32_t numFetchable, size_t first,
-                                  size_t count) const
-{
-    if (!mIndexCache)
+    if (mContext->IsWebGL2())
         return true;
 
-    if (!count)
-        return true;
-
-    const IndexRange range = { type, first, count };
-    auto res = mIndexRanges.insert({ range, size_t(0) });
-    if (mIndexRanges.size() > kMaxIndexRanges) {
-        mContext->GeneratePerfWarning("[%p] Clearing mIndexRanges after exceeding %u.",
-                                      this, kMaxIndexRanges);
-        mIndexRanges.clear();
-        res = mIndexRanges.insert({ range, size_t(0) });
-    }
-
-    const auto& itr = res.first;
-    const auto& didInsert = res.second;
-
-    auto& maxFetchIndex = itr->second;
-    if (didInsert) {
-        const auto& data = mIndexCache.get();
-        const uint32_t ignoreVal = (mContext->IsWebGL2() ? UINT32_MAX : 0);
+    return mCache->Validate(type, maxAllowed, first, count);
+}
 
-        switch (type) {
-        case LOCAL_GL_UNSIGNED_BYTE:
-            maxFetchIndex = MaxForRange<uint8_t>(data, first, count, ignoreVal);
-            break;
-        case LOCAL_GL_UNSIGNED_SHORT:
-            maxFetchIndex = MaxForRange<uint16_t>(data, first, count, ignoreVal);
-            break;
-        case LOCAL_GL_UNSIGNED_INT:
-            maxFetchIndex = MaxForRange<uint32_t>(data, first, count, ignoreVal);
-            break;
-        default:
-            MOZ_CRASH();
-        }
+bool
+WebGLBuffer::IsElementArrayUsedWithMultipleTypes() const
+{
+    if (mContext->IsWebGL2())
+        return false;
 
-        mContext->GeneratePerfWarning("[%p] New range #%u: (0x%04x, %u, %u): %u", this,
-                                      uint32_t(mIndexRanges.size()), type,
-                                      uint32_t(first), uint32_t(count),
-                                      uint32_t(maxFetchIndex));
-    }
-
-    return maxFetchIndex < numFetchable;
+    return mCache->BeenUsedWithMultipleTypes();
 }
 
 ////
 
 bool
 WebGLBuffer::ValidateCanBindToTarget(const char* funcName, GLenum target)
 {
     /* https://www.khronos.org/registry/webgl/specs/latest/2.0/#5.1
--- a/dom/canvas/WebGLBuffer.h
+++ b/dom/canvas/WebGLBuffer.h
@@ -1,26 +1,28 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef WEBGL_BUFFER_H_
 #define WEBGL_BUFFER_H_
 
-#include <map>
-
 #include "GLDefs.h"
 #include "mozilla/LinkedList.h"
+#include "mozilla/UniquePtr.h"
 #include "nsWrapperCache.h"
+
 #include "WebGLObjectModel.h"
 #include "WebGLTypes.h"
 
 namespace mozilla {
 
+class WebGLElementArrayCache;
+
 class WebGLBuffer final
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLBuffer>
     , public LinkedListElement<WebGLBuffer>
 {
     friend class WebGLContext;
     friend class WebGL2Context;
     friend class WebGLTexture;
@@ -39,29 +41,34 @@ public:
 
     void Delete();
 
     size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
 
     GLenum Usage() const { return mUsage; }
     size_t ByteLength() const { return mByteLength; }
 
-    bool ValidateIndexedFetch(GLenum type, uint32_t max_allowed, size_t first, size_t count) const;
+    bool ElementArrayCacheBufferData(const void* ptr, size_t bufferSizeInBytes);
+
+    void ElementArrayCacheBufferSubData(size_t pos, const void* ptr,
+                                        size_t updateSizeInBytes);
+
+    bool Validate(GLenum type, uint32_t max_allowed, size_t first, size_t count) const;
     bool ValidateRange(const char* funcName, size_t byteOffset, size_t byteLen) const;
 
+    bool IsElementArrayUsedWithMultipleTypes() const;
+
     WebGLContext* GetParentObject() const {
         return mContext;
     }
 
     virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
 
     bool ValidateCanBindToTarget(const char* funcName, GLenum target);
     void BufferData(GLenum target, size_t size, const void* data, GLenum usage);
-    void BufferSubData(GLenum target, size_t dstByteOffset, size_t dataLen,
-                       const void* data) const;
 
     ////
 
     static void AddBindCount(GLenum target, WebGLBuffer* buffer, int8_t addVal) {
         if (!buffer)
             return;
 
         if (target == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER) {
@@ -90,39 +97,19 @@ public:
     const GLenum mGLName;
 
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLBuffer)
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLBuffer)
 
 protected:
     ~WebGLBuffer();
 
-    void InvalidateCacheRange(size_t offset, size_t length) const;
-
     Kind mContent;
     GLenum mUsage;
     size_t mByteLength;
+    UniquePtr<WebGLElementArrayCache> mCache;
     size_t mTFBindCount;
     size_t mNonTFBindCount;
-
-    struct IndexRange final {
-        GLenum type;
-        size_t first;
-        size_t count;
-
-        bool operator<(const IndexRange& x) const {
-            if (type != x.type)
-                return type < x.type;
-
-            if (first != x.first)
-                return first < x.first;
-
-            return count < x.count;
-        }
-    };
-
-    UniqueBuffer mIndexCache;
-    mutable std::map<IndexRange, size_t> mIndexRanges;
 };
 
 } // namespace mozilla
 
 #endif // WEBGL_BUFFER_H_
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -1960,18 +1960,16 @@ protected:
 
     uint64_t mLastUseIndex;
 
     bool mNeedsFakeNoAlpha;
     bool mNeedsFakeNoDepth;
     bool mNeedsFakeNoStencil;
     bool mNeedsEmulatedLoneDepthStencil;
 
-    bool mNeedsIndexValidation;
-
     const bool mAllowFBInvalidation;
 
     bool Has64BitTimestamps() const;
 
     struct ScopedDrawCallWrapper final {
         WebGLContext& mWebGL;
         const bool mFakeNoAlpha;
         const bool mFakeNoDepth;
@@ -2109,16 +2107,60 @@ bool
 ValidateTexTarget(WebGLContext* webgl, const char* funcName, uint8_t funcDims,
                   GLenum rawTexTarget, TexTarget* const out_texTarget,
                   WebGLTexture** const out_tex);
 bool
 ValidateTexImageTarget(WebGLContext* webgl, const char* funcName, uint8_t funcDims,
                        GLenum rawTexImageTarget, TexImageTarget* const out_texImageTarget,
                        WebGLTexture** const out_tex);
 
+class UniqueBuffer
+{
+    // Like UniquePtr<>, but for void* and malloc/calloc/free.
+    void* mBuffer;
+
+public:
+    UniqueBuffer()
+        : mBuffer(nullptr)
+    { }
+
+    MOZ_IMPLICIT UniqueBuffer(void* buffer)
+        : mBuffer(buffer)
+    { }
+
+    ~UniqueBuffer() {
+        free(mBuffer);
+    }
+
+    UniqueBuffer(UniqueBuffer&& other) {
+        this->mBuffer = other.mBuffer;
+        other.mBuffer = nullptr;
+    }
+
+    UniqueBuffer& operator =(UniqueBuffer&& other) {
+        free(this->mBuffer);
+        this->mBuffer = other.mBuffer;
+        other.mBuffer = nullptr;
+        return *this;
+    }
+
+    UniqueBuffer& operator =(void* newBuffer) {
+        free(this->mBuffer);
+        this->mBuffer = newBuffer;
+        return *this;
+    }
+
+    explicit operator bool() const { return bool(mBuffer); }
+
+    void* get() const { return mBuffer; }
+
+    UniqueBuffer(const UniqueBuffer& other) = delete; // construct using Move()!
+    void operator =(const UniqueBuffer& other) = delete; // assign using Move()!
+};
+
 class ScopedUnpackReset final
     : public gl::ScopedGLWrapper<ScopedUnpackReset>
 {
     friend struct gl::ScopedGLWrapper<ScopedUnpackReset>;
 
 private:
     WebGLContext* const mWebGL;
 
--- a/dom/canvas/WebGLContextBuffers.cpp
+++ b/dom/canvas/WebGLContextBuffers.cpp
@@ -397,21 +397,37 @@ WebGLContext::BufferSubDataImpl(GLenum t
 
     if (!ValidateNonNegative(funcName, "byteOffset", dstByteOffset))
         return;
 
     const auto& buffer = ValidateBufferSelection(funcName, target);
     if (!buffer)
         return;
 
-    buffer->BufferSubData(target, size_t(dstByteOffset), dataLen, data);
+    if (!buffer->ValidateRange(funcName, dstByteOffset, dataLen))
+        return;
+
+    if (!CheckedInt<GLintptr>(dataLen).isValid()) {
+        ErrorOutOfMemory("%s: Size too large.", funcName);
+        return;
+    }
+    const GLintptr glDataLen(dataLen);
+
+    ////
+
+    MakeContextCurrent();
+    const ScopedLazyBind lazyBind(gl, target, buffer);
+
+    // Warning: Possibly shared memory.  See bug 1225033.
+    gl->fBufferSubData(target, dstByteOffset, glDataLen, data);
+
+    // Warning: Possibly shared memory.  See bug 1225033.
+    buffer->ElementArrayCacheBufferSubData(dstByteOffset, data, size_t(glDataLen));
 }
 
-////
-
 void
 WebGLContext::BufferSubData(GLenum target, WebGLsizeiptr dstByteOffset,
                             const dom::ArrayBuffer& src)
 {
     if (IsContextLost())
         return;
 
     src.ComputeLengthAndData();
--- a/dom/canvas/WebGLContextDraw.cpp
+++ b/dom/canvas/WebGLContextDraw.cpp
@@ -733,24 +733,33 @@ WebGLContext::DrawElements_check(const c
                               funcName);
         return false;
     }
 
     if (!ValidateBufferFetching(funcName))
         return false;
 
     if (!mMaxFetchedVertices ||
-        !elemArrayBuffer.ValidateIndexedFetch(type, mMaxFetchedVertices, first, vertCount))
+        !elemArrayBuffer.Validate(type, mMaxFetchedVertices - 1, first, vertCount))
     {
         ErrorInvalidOperation("%s: bound vertex attribute buffers do not have sufficient "
                               "size for given indices from the bound element array",
                               funcName);
         return false;
     }
 
+    // Bug 1008310 - Check if buffer has been used with a different previous type
+    if (elemArrayBuffer.IsElementArrayUsedWithMultipleTypes()) {
+        nsCString typeName;
+        WebGLContext::EnumName(type, &typeName);
+        GenerateWarning("%s: bound element array buffer previously used with a type other than "
+                        "%s, this will affect performance.",
+                        funcName, typeName.BeginReading());
+    }
+
     return true;
 }
 
 static void
 HandleDrawElementsErrors(WebGLContext* webgl, const char* funcName,
                          gl::GLContext::LocalErrorScope& errorScope)
 {
     const auto err = errorScope.GetError();
@@ -980,18 +989,18 @@ WebGLContext::ValidateBufferFetching(con
         }
 
         size_t availBytes = bufByteLen - vd.ByteOffset();
         if (vd.BytesPerVertex() > availBytes) {
             maxVertices = 0;
             maxInstances = 0;
             break;
         }
-        availBytes -= vd.BytesPerVertex(); // Snip off the tail.
-        const size_t vertCapacity = availBytes / vd.ExplicitStride() + 1; // Add +1 for the snipped tail.
+        availBytes -= vd.BytesPerVertex();
+        const size_t vertCapacity = 1 + availBytes / vd.ExplicitStride();
 
         if (vd.mDivisor == 0) {
             if (vertCapacity < maxVertices) {
                 maxVertices = vertCapacity;
             }
             hasPerVertex = true;
         } else {
             const auto curMaxInstances = CheckedInt<size_t>(vertCapacity) * vd.mDivisor;
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -720,21 +720,16 @@ WebGLContext::InitAndValidateGL(FailureR
     std::fill_n(mGenericVertexAttribTypes.get(), mGLMaxVertexAttribs, LOCAL_GL_FLOAT);
 
     static const float kDefaultGenericVertexAttribData[4] = { 0, 0, 0, 1 };
     memcpy(mGenericVertexAttrib0Data, kDefaultGenericVertexAttribData,
            sizeof(mGenericVertexAttrib0Data));
 
     mFakeVertexAttrib0BufferObject = 0;
 
-    mNeedsIndexValidation = !gl->IsSupported(gl::GLFeature::robust_buffer_access_behavior);
-    if (gfxPrefs::WebGLForceIndexValidation()) {
-        mNeedsIndexValidation = true;
-    }
-
     return true;
 }
 
 bool
 WebGLContext::ValidateFramebufferTarget(GLenum target,
                                         const char* const info)
 {
     bool isValid = true;
new file mode 100644
--- /dev/null
+++ b/dom/canvas/WebGLElementArrayCache.cpp
@@ -0,0 +1,622 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WebGLElementArrayCache.h"
+
+#include <algorithm>
+#include <cstdlib>
+#include <cstring>
+#include <limits>
+#include "mozilla/Assertions.h"
+#include "mozilla/MathAlgorithms.h"
+#include "mozilla/MemoryReporting.h"
+
+namespace mozilla {
+
+/* WebGLElementArrayCacheTree contains most of the implementation of
+ * WebGLElementArrayCache, which performs WebGL element array buffer validation
+ * for drawElements.
+ *
+ * Attention: Here lie nontrivial data structures, bug-prone algorithms, and
+ * non-canonical tweaks! Whence the explanatory comments, and compiled unit
+ * test.
+ *
+ * *** What problem are we solving here? ***
+ *
+ * WebGL::DrawElements has to validate that the elements are in range wrt the
+ * current vertex attribs. This boils down to the problem, given an array of
+ * integers, of computing the maximum in an arbitrary sub-array. The naive
+ * algorithm has linear complexity; this has been a major performance problem,
+ * see bug 569431. In that bug, we took the approach of caching the max for the
+ * whole array, which does cover most cases (DrawElements typically consumes the
+ * whole element array buffer) but doesn't help in other use cases:
+ *  - when doing "partial DrawElements" i.e. consuming only part of the element
+ *    array buffer
+ *  - when doing frequent "partial buffer updates" i.e. bufferSubData calls
+ *    updating parts of the element array buffer
+ *
+ * *** The solution: A binary tree ***
+ *
+ * The solution implemented here is to use a binary tree as the cache data
+ * structure. Each tree node contains the max of its two children nodes. In this
+ * way, finding the maximum in any contiguous sub-array has log complexity
+ * instead of linear complexity.
+ *
+ * Simplistically, if the element array is:
+ *
+ *    [1   4   3   2]
+ *
+ * then the corresponding tree is:
+ *
+ *           4
+ *         _/ \_
+ *       4       3
+ *      / \     / \
+ *     1   4   3   2
+ *
+ * In practice, the bottom-most levels of the tree are both the largest to store
+ * (because they have more nodes), and the least useful performance-wise
+ * (because each node in the bottom levels concerns only few entries in the
+ * elements array buffer, it is cheap to compute).
+ *
+ * For this reason, we stop the tree a few levels above, so that each tree leaf
+ * actually corresponds to more than one element array entry.
+ *
+ * The number of levels that we "drop" is |kSkippedBottomTreeLevels| and the
+ * number of element array entries that each leaf corresponds to, is
+ * |kElementsPerLeaf|. This being a binary tree, we have:
+ *
+ *   kElementsPerLeaf = 2 ^ kSkippedBottomTreeLevels.
+ *
+ * *** Storage layout of the binary tree ***
+ *
+ * We take advantage of the specifics of the situation to avoid generalist tree
+ * storage and instead store the tree entries in a vector, mTreeData.
+ *
+ * TreeData is always a vector of length:
+ *
+ *    2 * (number of leaves).
+ *
+ * Its data layout is as follows: mTreeData[0] is unused, mTreeData[1] is the
+ * root node, then at offsets 2..3 is the tree level immediately below the root
+ * node, then at offsets 4..7 is the tree level below that, etc.
+ *
+ * The figure below illustrates this by writing at each tree node the offset
+ * into mTreeData at which it is stored:
+ *
+ *           1
+ *         _/ \_
+ *       2       3
+ *      / \     / \
+ *     4   5   6   7
+ *    ...
+ *
+ * Thus, under the convention that the root level is level 0, we see that level
+ * N is stored at offsets:
+ *
+ *    [ 2^n .. 2^(n+1) - 1 ]
+ *
+ * in mTreeData. Likewise, all the usual tree operations have simple
+ * mathematical expressions in terms of mTreeData offsets, see all the methods
+ * such as ParentNode, LeftChildNode, etc.
+ *
+ * *** Design constraint: Element types aren't known at buffer-update time ***
+ *
+ * Note that a key constraint that we're operating under, is that we don't know
+ * the types of the elements by the time WebGL bufferData/bufferSubData methods
+ * are called. The type of elements is only specified in the drawElements call.
+ * This means that we may potentially have to store caches for multiple element
+ * types, for the same element array buffer. Since we don't know yet how many
+ * element types we'll eventually support (extensions add more), the concern
+ * about memory usage is serious. This is addressed by kSkippedBottomTreeLevels
+ * as explained above. Of course, in the typical case where each element array
+ * buffer is only ever used with one type, this is also addressed by having
+ * WebGLElementArrayCache lazily create trees for each type only upon first use.
+ *
+ * Another consequence of this constraint is that when updating the trees, we
+ * have to update all existing trees. So if trees for types uint8_t, uint16_t
+ * and uint32_t have ever been constructed for this buffer, every subsequent
+ * update will have to update all trees even if one of the types is never used
+ * again. That's inefficient, but content should not put indices of different
+ * types in the same element array buffer anyways. Different index types can
+ * only be consumed in separate drawElements calls, so nothing particular is
+ * to be achieved by lumping them in the same buffer object.
+ */
+template<typename T>
+struct WebGLElementArrayCacheTree
+{
+    /* A too-high kSkippedBottomTreeLevels would harm the performance of small
+     * drawElements calls. A too-low kSkippedBottomTreeLevels would cause undue
+     * memory usage. The current value has been validated by some benchmarking.
+     * See bug 732660.
+     */
+    static const size_t kSkippedBottomTreeLevels = 3;
+    static const size_t kElementsPerLeaf = 1 << kSkippedBottomTreeLevels;
+    // Since kElementsPerLeaf is POT:
+    static const size_t kElementsPerLeafMask = kElementsPerLeaf - 1;
+
+private:
+    // The WebGLElementArrayCache that owns this tree:
+    WebGLElementArrayCache& mParent;
+
+    // The tree's internal data storage. Its length is 2 * (number of leaves)
+    // because of its data layout explained in the above class comment.
+    FallibleTArray<T> mTreeData;
+
+public:
+    // Constructor. Takes a reference to the WebGLElementArrayCache that is to be
+    // the parent. Does not initialize the tree. Should be followed by a call
+    // to Update() to attempt initializing the tree.
+    explicit WebGLElementArrayCacheTree(WebGLElementArrayCache& value)
+        : mParent(value)
+    {
+    }
+
+    T GlobalMaximum() const {
+        return mTreeData[1];
+    }
+
+    // returns the index of the parent node; if treeIndex=1 (the root node),
+    // the return value is 0.
+    static size_t ParentNode(size_t treeIndex) {
+        MOZ_ASSERT(treeIndex > 1);
+        return treeIndex >> 1;
+    }
+
+    static bool IsRightNode(size_t treeIndex) {
+        MOZ_ASSERT(treeIndex > 1);
+        return treeIndex & 1;
+    }
+
+    static bool IsLeftNode(size_t treeIndex) {
+        MOZ_ASSERT(treeIndex > 1);
+        return !IsRightNode(treeIndex);
+    }
+
+    static size_t SiblingNode(size_t treeIndex) {
+        MOZ_ASSERT(treeIndex > 1);
+        return treeIndex ^ 1;
+    }
+
+    static size_t LeftChildNode(size_t treeIndex) {
+        MOZ_ASSERT(treeIndex);
+        return treeIndex << 1;
+    }
+
+    static size_t RightChildNode(size_t treeIndex) {
+        MOZ_ASSERT(treeIndex);
+        return SiblingNode(LeftChildNode(treeIndex));
+    }
+
+    static size_t LeftNeighborNode(size_t treeIndex, size_t distance = 1) {
+        MOZ_ASSERT(treeIndex > 1);
+        return treeIndex - distance;
+    }
+
+    static size_t RightNeighborNode(size_t treeIndex, size_t distance = 1) {
+        MOZ_ASSERT(treeIndex > 1);
+        return treeIndex + distance;
+    }
+
+    size_t NumLeaves() const {
+        // See class comment for why we the tree storage size is 2 * numLeaves.
+        return mTreeData.Length() >> 1;
+    }
+
+    size_t LeafForElement(size_t element) const {
+        size_t leaf = element / kElementsPerLeaf;
+        MOZ_ASSERT(leaf < NumLeaves());
+        return leaf;
+    }
+
+    size_t LeafForByte(size_t byte) const {
+        return LeafForElement(byte / sizeof(T));
+    }
+
+    // Returns the index, into the tree storage, where a given leaf is stored.
+    size_t TreeIndexForLeaf(size_t leaf) const {
+        // See above class comment. The tree storage is an array of length
+        // 2 * numLeaves. The leaves are stored in its second half.
+        return leaf + NumLeaves();
+    }
+
+    static size_t LastElementUnderSameLeaf(size_t element) {
+        return element | kElementsPerLeafMask;
+    }
+
+    static size_t FirstElementUnderSameLeaf(size_t element) {
+        return element & ~kElementsPerLeafMask;
+    }
+
+    static size_t NextMultipleOfElementsPerLeaf(size_t numElements) {
+        MOZ_ASSERT(numElements >= 1);
+        return ((numElements - 1) | kElementsPerLeafMask) + 1;
+    }
+
+    bool Validate(T maxAllowed, size_t firstLeaf, size_t lastLeaf)
+    {
+        size_t firstTreeIndex = TreeIndexForLeaf(firstLeaf);
+        size_t lastTreeIndex  = TreeIndexForLeaf(lastLeaf);
+
+        while (true) {
+            // Given that we tweak these values in nontrivial ways, it doesn't
+            // hurt to do this sanity check.
+            MOZ_ASSERT(firstTreeIndex <= lastTreeIndex);
+
+            // Final case where there is only one node to validate at the
+            // current tree level:
+            if (lastTreeIndex == firstTreeIndex) {
+                const T& curData = mTreeData[firstTreeIndex];
+                return curData <= maxAllowed;
+            }
+
+            // If the first node at current tree level is a right node, handle
+            // it individually and replace it with its right neighbor, which is
+            // a left node.
+            if (IsRightNode(firstTreeIndex)) {
+                const T& curData = mTreeData[firstTreeIndex];
+                if (curData > maxAllowed)
+                  return false;
+
+                firstTreeIndex = RightNeighborNode(firstTreeIndex);
+            }
+
+            // If the last node at current tree level is a left node, handle it
+            // individually and replace it with its left neighbor, which is a
+            // right node.
+            if (IsLeftNode(lastTreeIndex)) {
+                const T& curData = mTreeData[lastTreeIndex];
+                if (curData > maxAllowed)
+                    return false;
+
+                lastTreeIndex = LeftNeighborNode(lastTreeIndex);
+            }
+
+            /* At this point it can happen that firstTreeIndex and lastTreeIndex
+             * "crossed" eachother. That happens if firstTreeIndex was a right
+             * node and lastTreeIndex was its right neighor: In that case, both
+             * above tweaks happened and as a result, they ended up being
+             * swapped: LastTreeIndex is now the _left_ neighbor of
+             * firstTreeIndex. When that happens, there is nothing left to
+             * validate.
+             */
+            if (lastTreeIndex == LeftNeighborNode(firstTreeIndex))
+                return true;
+
+            // Walk up one level.
+            firstTreeIndex = ParentNode(firstTreeIndex);
+            lastTreeIndex = ParentNode(lastTreeIndex);
+        }
+    }
+
+    // Updates the tree from the parent's buffer contents. Fallible, as it
+    // may have to resize the tree storage.
+    bool Update(size_t firstByte, size_t lastByte);
+
+    size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
+    {
+        return mallocSizeOf(this) +
+               mTreeData.ShallowSizeOfExcludingThis(mallocSizeOf);
+    }
+};
+
+// TreeForType: just a template helper to select the right tree object for a given
+// element type.
+template<typename T>
+struct TreeForType {};
+
+template<>
+struct TreeForType<uint8_t>
+{
+    static UniquePtr<WebGLElementArrayCacheTree<uint8_t>>&
+    Value(WebGLElementArrayCache* b) {
+        return b->mUint8Tree;
+    }
+};
+
+template<>
+struct TreeForType<uint16_t>
+{
+    static UniquePtr<WebGLElementArrayCacheTree<uint16_t>>&
+    Value(WebGLElementArrayCache* b) {
+        return b->mUint16Tree;
+    }
+};
+
+template<>
+struct TreeForType<uint32_t>
+{
+    static UniquePtr<WebGLElementArrayCacheTree<uint32_t>>&
+    Value(WebGLElementArrayCache* b) {
+        return b->mUint32Tree;
+    }
+};
+
+// Calling this method will 1) update the leaves in this interval
+// from the raw buffer data, and 2) propagate this update up the tree.
+template<typename T>
+bool
+WebGLElementArrayCacheTree<T>::Update(size_t firstByte, size_t lastByte)
+{
+    MOZ_ASSERT(firstByte <= lastByte);
+    MOZ_ASSERT(lastByte < mParent.mBytes.Length());
+
+    size_t numberOfElements = mParent.mBytes.Length() / sizeof(T);
+    size_t requiredNumLeaves = 0;
+    if (numberOfElements > 0) {
+        /* If we didn't require the number of leaves to be a power of two, then
+         * it would just be equal to
+         *
+         *    ceil(numberOfElements / kElementsPerLeaf)
+         *
+         * The way we implement this (division+ceil) operation in integer
+         * arithmetic
+         * is as follows:
+         */
+        size_t numLeavesNonPOT = (numberOfElements + kElementsPerLeaf - 1) / kElementsPerLeaf;
+        // It only remains to round that up to the next power of two:
+        requiredNumLeaves = RoundUpPow2(numLeavesNonPOT);
+    }
+
+    // Step #0: If needed, resize our tree data storage.
+    if (requiredNumLeaves != NumLeaves()) {
+        // See class comment for why we the tree storage size is 2 * numLeaves.
+        if (!mTreeData.SetLength(2 * requiredNumLeaves, fallible)) {
+            mTreeData.Clear();
+            return false;
+        }
+        MOZ_ASSERT(NumLeaves() == requiredNumLeaves);
+
+        if (NumLeaves()) {
+            // When resizing, update the whole tree, not just the subset
+            // corresponding to the part of the buffer being updated.
+            memset(mTreeData.Elements(), 0, mTreeData.Length() * sizeof(T));
+            firstByte = 0;
+            lastByte = mParent.mBytes.Length() - 1;
+        }
+    }
+
+    if (NumLeaves() == 0)
+        return true;
+
+    lastByte = std::min(lastByte, NumLeaves() * kElementsPerLeaf * sizeof(T) - 1);
+    if (firstByte > lastByte)
+        return true;
+
+    size_t firstLeaf = LeafForByte(firstByte);
+    size_t lastLeaf = LeafForByte(lastByte);
+
+    MOZ_ASSERT(firstLeaf <= lastLeaf && lastLeaf < NumLeaves());
+
+    size_t firstTreeIndex = TreeIndexForLeaf(firstLeaf);
+    size_t lastTreeIndex = TreeIndexForLeaf(lastLeaf);
+
+    // Step #1: Initialize the tree leaves from plain buffer data.
+    // That is, each tree leaf must be set to the max of the |kElementsPerLeaf|
+    // corresponding buffer entries.
+
+    // Condition-less scope to prevent leaking this scope's variables into the
+    // code below:
+    {
+        // TreeIndex is the index of the tree leaf we're writing, i.e. the
+        // destination index.
+        size_t treeIndex = firstTreeIndex;
+        // srcIndex is the index in the source buffer.
+        size_t srcIndex = firstLeaf * kElementsPerLeaf;
+        while (treeIndex <= lastTreeIndex) {
+            T m = 0;
+            size_t a = srcIndex;
+            size_t srcIndexNextLeaf = std::min(a + kElementsPerLeaf, numberOfElements);
+            for (; srcIndex < srcIndexNextLeaf; srcIndex++) {
+                m = std::max(m, mParent.Element<T>(srcIndex));
+            }
+            mTreeData[treeIndex] = m;
+            treeIndex++;
+        }
+    }
+
+    // Step #2: Propagate the values up the tree. This is simply a matter of
+    // walking up the tree and setting each node to the max of its two children.
+    while (firstTreeIndex > 1) {
+        // Move up one level.
+        firstTreeIndex = ParentNode(firstTreeIndex);
+        lastTreeIndex = ParentNode(lastTreeIndex);
+
+        // Fast-exit case where only one node is updated at the current level.
+        if (firstTreeIndex == lastTreeIndex) {
+            mTreeData[firstTreeIndex] = std::max(mTreeData[LeftChildNode(firstTreeIndex)], mTreeData[RightChildNode(firstTreeIndex)]);
+            continue;
+        }
+
+        size_t child = LeftChildNode(firstTreeIndex);
+        size_t parent = firstTreeIndex;
+        while (parent <= lastTreeIndex) {
+            T a = mTreeData[child];
+            child = RightNeighborNode(child);
+            T b = mTreeData[child];
+            child = RightNeighborNode(child);
+            mTreeData[parent] = std::max(a, b);
+            parent = RightNeighborNode(parent);
+        }
+    }
+
+    return true;
+}
+
+WebGLElementArrayCache::WebGLElementArrayCache()
+{
+}
+
+WebGLElementArrayCache::~WebGLElementArrayCache()
+{
+}
+
+bool
+WebGLElementArrayCache::BufferData(const void* ptr, size_t byteLength)
+{
+    if (mBytes.Length() != byteLength) {
+        if (!mBytes.SetLength(byteLength, fallible)) {
+            mBytes.Clear();
+            return false;
+        }
+    }
+    MOZ_ASSERT(mBytes.Length() == byteLength);
+    return BufferSubData(0, ptr, byteLength);
+}
+
+bool
+WebGLElementArrayCache::BufferSubData(size_t pos, const void* ptr,
+                                      size_t updateByteLength)
+{
+    MOZ_ASSERT(pos + updateByteLength <= mBytes.Length());
+    if (!updateByteLength)
+        return true;
+
+    // Note, using memcpy on shared racy data is not well-defined, this
+    // will need to use safe-for-races operations when those become available.
+    // See bug 1225033.
+    if (ptr)
+        memcpy(mBytes.Elements() + pos, ptr, updateByteLength);
+    else
+        memset(mBytes.Elements() + pos, 0, updateByteLength);
+    return UpdateTrees(pos, pos + updateByteLength - 1);
+}
+
+bool
+WebGLElementArrayCache::UpdateTrees(size_t firstByte, size_t lastByte)
+{
+    bool result = true;
+    if (mUint8Tree)
+        result &= mUint8Tree->Update(firstByte, lastByte);
+    if (mUint16Tree)
+        result &= mUint16Tree->Update(firstByte, lastByte);
+    if (mUint32Tree)
+        result &= mUint32Tree->Update(firstByte, lastByte);
+    return result;
+}
+
+template<typename T>
+bool
+WebGLElementArrayCache::Validate(uint32_t maxAllowed, size_t firstElement,
+                                 size_t countElements)
+{
+    // If maxAllowed is >= the max T value, then there is no way that a T index
+    // could be invalid.
+    uint32_t maxTSize = std::numeric_limits<T>::max();
+    if (maxAllowed >= maxTSize)
+        return true;
+
+    T maxAllowedT(maxAllowed);
+
+    // Integer overflow must have been handled earlier, so we assert that
+    // maxAllowedT is exactly the max allowed value.
+    MOZ_ASSERT(uint32_t(maxAllowedT) == maxAllowed);
+
+    if (!mBytes.Length() || !countElements)
+      return true;
+
+    UniquePtr<WebGLElementArrayCacheTree<T>>& tree = TreeForType<T>::Value(this);
+    if (!tree) {
+        tree = MakeUnique<WebGLElementArrayCacheTree<T>>(*this);
+        if (mBytes.Length()) {
+            bool valid = tree->Update(0, mBytes.Length() - 1);
+            if (!valid) {
+                // Do not assert here. This case would happen if an allocation
+                // failed. We've already settled on fallible allocations around
+                // here.
+                tree = nullptr;
+                return false;
+            }
+        }
+    }
+
+    size_t lastElement = firstElement + countElements - 1;
+
+    // Fast-exit path when the global maximum for the whole element array buffer
+    // falls in the allowed range:
+    T globalMax = tree->GlobalMaximum();
+    if (globalMax <= maxAllowedT)
+        return true;
+
+    const T* elements = Elements<T>();
+
+    // Before calling tree->Validate, we have to validate ourselves the
+    // boundaries of the elements span, to round them to the nearest multiple of
+    // kElementsPerLeaf.
+    size_t firstElementAdjustmentEnd = std::min(lastElement,
+                                                tree->LastElementUnderSameLeaf(firstElement));
+    while (firstElement <= firstElementAdjustmentEnd) {
+        const T& curData = elements[firstElement];
+        if (curData > maxAllowedT)
+            return false;
+
+        firstElement++;
+    }
+    size_t lastElementAdjustmentEnd = std::max(firstElement,
+                                               tree->FirstElementUnderSameLeaf(lastElement));
+    while (lastElement >= lastElementAdjustmentEnd) {
+        const T& curData = elements[lastElement];
+        if (curData > maxAllowedT)
+            return false;
+
+        lastElement--;
+    }
+
+    // at this point, for many tiny validations, we're already done.
+    if (firstElement > lastElement)
+        return true;
+
+    // general case
+    return tree->Validate(maxAllowedT, tree->LeafForElement(firstElement),
+                          tree->LeafForElement(lastElement));
+}
+
+bool
+WebGLElementArrayCache::Validate(GLenum type, uint32_t maxAllowed,
+                                 size_t firstElement, size_t countElements)
+{
+    if (type == LOCAL_GL_UNSIGNED_BYTE)
+        return Validate<uint8_t>(maxAllowed, firstElement, countElements);
+    if (type == LOCAL_GL_UNSIGNED_SHORT)
+        return Validate<uint16_t>(maxAllowed, firstElement, countElements);
+    if (type == LOCAL_GL_UNSIGNED_INT)
+        return Validate<uint32_t>(maxAllowed, firstElement, countElements);
+
+    MOZ_ASSERT(false, "Invalid type.");
+    return false;
+}
+
+template<typename T>
+static size_t
+SizeOfNullable(mozilla::MallocSizeOf mallocSizeOf, const T& obj)
+{
+    if (!obj)
+        return 0;
+    return obj->SizeOfIncludingThis(mallocSizeOf);
+}
+
+size_t
+WebGLElementArrayCache::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
+{
+    return mallocSizeOf(this) +
+           mBytes.ShallowSizeOfExcludingThis(mallocSizeOf) +
+           SizeOfNullable(mallocSizeOf, mUint8Tree) +
+           SizeOfNullable(mallocSizeOf, mUint16Tree) +
+           SizeOfNullable(mallocSizeOf, mUint32Tree);
+}
+
+bool
+WebGLElementArrayCache::BeenUsedWithMultipleTypes() const
+{
+  // C++ Standard ($4.7)
+  // "If the source type is bool, the value false is converted to zero and
+  //  the value true is converted to one."
+  const int num_types_used = (mUint8Tree  != nullptr) +
+                             (mUint16Tree != nullptr) +
+                             (mUint32Tree != nullptr);
+  return num_types_used > 1;
+}
+
+} // end namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/canvas/WebGLElementArrayCache.h
@@ -0,0 +1,101 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WEBGL_ELEMENT_ARRAY_CACHE_H
+#define WEBGL_ELEMENT_ARRAY_CACHE_H
+
+#include "GLDefs.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/UniquePtr.h"
+#include "nscore.h"
+#include "nsTArray.h"
+#include <stdint.h>
+
+namespace mozilla {
+
+template<typename T>
+struct WebGLElementArrayCacheTree;
+
+/* WebGLElementArrayCache implements WebGL element array buffer validation for
+ * drawElements.
+ *
+ * Its exposes methods meant to be called by WebGL method implementations:
+ *
+ * - Validate, to be called by WebGLContext::DrawElements, is where we use the
+ *   cache.
+ *
+ * - BufferData and BufferSubData, to be called by eponymous WebGL methods, are
+ *   how data is fed into the cache.
+ *
+ * Most of the implementation is hidden in the auxilary class template,
+ * WebGLElementArrayCacheTree. Refer to its code for design comments.
+ */
+class WebGLElementArrayCache {
+public:
+    bool BufferData(const void* ptr, size_t byteLength);
+    bool BufferSubData(size_t pos, const void* ptr, size_t updateByteSize);
+
+    bool Validate(GLenum type, uint32_t maxAllowed, size_t first, size_t count);
+
+    template<typename T>
+    T Element(size_t i) const { return Elements<T>()[i]; }
+
+    WebGLElementArrayCache();
+    ~WebGLElementArrayCache();
+
+    size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+    bool BeenUsedWithMultipleTypes() const;
+
+private:
+    /* Returns true if a drawElements call with the given parameters should
+     * succeed, false otherwise.
+     *
+     * In other words, this returns true if all entries in the element array at
+     * positions:
+     *
+     *    first .. first+count-1
+     *
+     * are less than or equal to maxAllowed.
+     *
+     * Input parameters:
+     *   maxAllowed: Maximum value to be allowed in the specificied portion of
+     *               the element array.
+     *   first: Start of the portion of the element array to consume.
+     *   count: Number of entries from the element array to consume.
+     *
+     * Output parameter:
+     *   out_upperBound: Upon success, is set to the actual maximum value in the
+     *                   specified range, which is then guaranteed to be less
+     *                   than or equal to maxAllowed. upon failure, is set to
+     *                   the first value in the specified range, that is greater
+     *                   than maxAllowed.
+     */
+    template<typename T>
+    bool Validate(uint32_t maxAllowed, size_t first, size_t count);
+
+    template<typename T>
+    const T* Elements() const {
+        return reinterpret_cast<const T*>(mBytes.Elements());
+    }
+
+    template<typename T>
+    T* Elements() { return reinterpret_cast<T*>(mBytes.Elements()); }
+
+    bool UpdateTrees(size_t firstByte, size_t lastByte);
+
+    template<typename T>
+    friend struct WebGLElementArrayCacheTree;
+    template<typename T>
+    friend struct TreeForType;
+
+    FallibleTArray<uint8_t> mBytes;
+    UniquePtr<WebGLElementArrayCacheTree<uint8_t>> mUint8Tree;
+    UniquePtr<WebGLElementArrayCacheTree<uint16_t>> mUint16Tree;
+    UniquePtr<WebGLElementArrayCacheTree<uint32_t>> mUint32Tree;
+};
+
+} // end namespace mozilla
+
+#endif // WEBGL_ELEMENT_ARRAY_CACHE_H
--- a/dom/canvas/WebGLTypes.h
+++ b/dom/canvas/WebGLTypes.h
@@ -168,55 +168,11 @@ enum class WebGLExtensionID : uint8_t {
     WEBGL_debug_shaders,
     WEBGL_depth_texture,
     WEBGL_draw_buffers,
     WEBGL_lose_context,
     Max,
     Unknown
 };
 
-class UniqueBuffer
-{
-    // Like UniquePtr<>, but for void* and malloc/calloc/free.
-    void* mBuffer;
-
-public:
-    UniqueBuffer()
-        : mBuffer(nullptr)
-    { }
-
-    MOZ_IMPLICIT UniqueBuffer(void* buffer)
-        : mBuffer(buffer)
-    { }
-
-    ~UniqueBuffer() {
-        free(mBuffer);
-    }
-
-    UniqueBuffer(UniqueBuffer&& other) {
-        this->mBuffer = other.mBuffer;
-        other.mBuffer = nullptr;
-    }
-
-    UniqueBuffer& operator =(UniqueBuffer&& other) {
-        free(this->mBuffer);
-        this->mBuffer = other.mBuffer;
-        other.mBuffer = nullptr;
-        return *this;
-    }
-
-    UniqueBuffer& operator =(void* newBuffer) {
-        free(this->mBuffer);
-        this->mBuffer = newBuffer;
-        return *this;
-    }
-
-    explicit operator bool() const { return bool(mBuffer); }
-
-    void* get() const { return mBuffer; }
-
-    UniqueBuffer(const UniqueBuffer& other) = delete; // construct using Move()!
-    void operator =(const UniqueBuffer& other) = delete; // assign using Move()!
-};
-
 } // namespace mozilla
 
 #endif
new file mode 100644
--- /dev/null
+++ b/dom/canvas/gtest/TestWebGLElementArrayCache.cpp
@@ -0,0 +1,206 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/Assertions.h"
+
+#include "WebGLElementArrayCache.h"
+
+#include <cstdio>
+#include <cstdlib>
+#include "nscore.h"
+#include "nsTArray.h"
+
+void
+MakeRandomVector(nsTArray<uint8_t>& a, size_t size)
+{
+  a.SetLength(size);
+  // only the most-significant bits of rand() are reasonably random.
+  // RAND_MAX can be as low as 0x7fff, and we need 8 bits for the result, so we can only
+  // ignore the 7 least significant bits.
+  for (size_t i = 0; i < size; i++)
+    a[i] = static_cast<uint8_t>((unsigned int)(rand()) >> 7);
+}
+
+template<typename T>
+T
+RandomInteger(T a, T b)
+{
+  T result(a + rand() % (b - a + 1));
+  return result;
+}
+
+template<typename T>
+GLenum
+GLType()
+{
+  switch (sizeof(T)) {
+  case 4:  return LOCAL_GL_UNSIGNED_INT;
+  case 2:  return LOCAL_GL_UNSIGNED_SHORT;
+  case 1:  return LOCAL_GL_UNSIGNED_BYTE;
+  default:
+    MOZ_RELEASE_ASSERT(false);
+    return 0;
+  }
+}
+
+void
+CheckValidate(bool expectSuccess, mozilla::WebGLElementArrayCache& c, GLenum type,
+              uint32_t maxAllowed, size_t first, size_t count)
+{
+  const bool success = c.Validate(type, maxAllowed, first, count);
+  ASSERT_TRUE(success == expectSuccess);
+}
+
+template<typename T>
+void
+CheckValidateOneTypeVariousBounds(mozilla::WebGLElementArrayCache& c, size_t firstByte,
+                                  size_t countBytes)
+{
+  size_t first = firstByte / sizeof(T);
+  size_t count = countBytes / sizeof(T);
+
+  GLenum type = GLType<T>();
+
+  T max = 0;
+  for (size_t i = 0; i < count; i++)
+    if (c.Element<T>(first + i) > max)
+      max = c.Element<T>(first + i);
+
+  CheckValidate(true, c, type, max, first, count);
+  CheckValidate(true, c, type, T(-1), first, count);
+  if (T(max + 1)) CheckValidate(true, c, type, T(max + 1), first, count);
+  if (max > 0) {
+    CheckValidate(false, c, type, max - 1, first, count);
+    CheckValidate(false, c, type, 0, first, count);
+  }
+}
+
+void CheckValidateAllTypes(mozilla::WebGLElementArrayCache& c, size_t firstByte,
+                           size_t countBytes)
+{
+  CheckValidateOneTypeVariousBounds<uint8_t>(c, firstByte, countBytes);
+  CheckValidateOneTypeVariousBounds<uint16_t>(c, firstByte, countBytes);
+  CheckValidateOneTypeVariousBounds<uint32_t>(c, firstByte, countBytes);
+}
+
+template<typename T>
+void
+CheckSanity()
+{
+  const size_t numElems = 64; // should be significantly larger than tree leaf size to
+                        // ensure we exercise some nontrivial tree-walking
+  T data[numElems] = {1,0,3,1,2,6,5,4}; // intentionally specify only 8 elements for now
+  size_t numBytes = numElems * sizeof(T);
+  ASSERT_TRUE(numBytes == sizeof(data));
+
+  GLenum type = GLType<T>();
+
+  mozilla::WebGLElementArrayCache c;
+  c.BufferData(data, numBytes);
+  CheckValidate(true,  c, type, 6, 0, 8);
+  CheckValidate(false, c, type, 5, 0, 8);
+  CheckValidate(true,  c, type, 3, 0, 3);
+  CheckValidate(false, c, type, 2, 0, 3);
+  CheckValidate(true,  c, type, 6, 2, 4);
+  CheckValidate(false, c, type, 5, 2, 4);
+
+  c.BufferSubData(5*sizeof(T), data, sizeof(T));
+  CheckValidate(true,  c, type, 5, 0, 8);
+  CheckValidate(false, c, type, 4, 0, 8);
+
+  // now test a somewhat larger size to ensure we exceed the size of a tree leaf
+  for(size_t i = 0; i < numElems; i++)
+    data[i] = numElems - i;
+  c.BufferData(data, numBytes);
+  CheckValidate(true,  c, type, numElems,     0, numElems);
+  CheckValidate(false, c, type, numElems - 1, 0, numElems);
+
+  ASSERT_TRUE(numElems > 10);
+  CheckValidate(true,  c, type, numElems - 10, 10, numElems - 10);
+  CheckValidate(false, c, type, numElems - 11, 10, numElems - 10);
+}
+
+template<typename T>
+void
+CheckUintOverflow()
+{
+  // This test is only for integer types smaller than uint32_t
+  static_assert(sizeof(T) < sizeof(uint32_t), "This test is only for integer types \
+                smaller than uint32_t");
+
+  const size_t numElems = 64; // should be significantly larger than tree leaf size to
+                              // ensure we exercise some nontrivial tree-walking
+  T data[numElems];
+  size_t numBytes = numElems * sizeof(T);
+  ASSERT_TRUE(numBytes == sizeof(data));
+
+  GLenum type = GLType<T>();
+
+  mozilla::WebGLElementArrayCache c;
+
+  for(size_t i = 0; i < numElems; i++)
+    data[i] = numElems - i;
+  c.BufferData(data, numBytes);
+
+  // bug 825205
+  uint32_t bigValWrappingToZero = uint32_t(T(-1)) + 1;
+  CheckValidate(true,  c, type, bigValWrappingToZero,     0, numElems);
+  CheckValidate(true,  c, type, bigValWrappingToZero - 1, 0, numElems);
+  CheckValidate(false, c, type,                        0, 0, numElems);
+}
+
+TEST(WebGLElementArrayCache, Test)
+{
+  srand(0); // do not want a random seed here.
+
+  CheckSanity<uint8_t>();
+  CheckSanity<uint16_t>();
+  CheckSanity<uint32_t>();
+
+  CheckUintOverflow<uint8_t>();
+  CheckUintOverflow<uint16_t>();
+
+  nsTArray<uint8_t> v, vsub;
+  mozilla::WebGLElementArrayCache b;
+
+  for (int maxBufferSize = 1; maxBufferSize <= 4096; maxBufferSize *= 2) {
+    // See bug 800612. We originally had | repeat = min(maxBufferSize, 20) |
+    // and a real bug was only caught on Windows and not on Linux due to rand()
+    // producing different values. In that case, the minimum value by which to replace
+    // this 20 to reproduce the bug on Linux, was 25. Replacing it with 64 should give
+    // us some comfort margin.
+    int repeat = std::min(maxBufferSize, 64);
+    for (int i = 0; i < repeat; i++) {
+      size_t size = RandomInteger<size_t>(0, maxBufferSize);
+      MakeRandomVector(v, size);
+      b.BufferData(v.Elements(), size);
+      CheckValidateAllTypes(b, 0, size);
+
+      for (int j = 0; j < 16; j++) {
+        for (int bufferSubDataCalls = 1; bufferSubDataCalls <= 8; bufferSubDataCalls *= 2) {
+          for (int validateCalls = 1; validateCalls <= 8; validateCalls *= 2) {
+
+            size_t offset = 0, subsize = 0;
+
+            for (int k = 0; k < bufferSubDataCalls; k++) {
+              offset = RandomInteger<size_t>(0, size);
+              subsize = RandomInteger<size_t>(0, size - offset);
+              MakeRandomVector(vsub, subsize);
+              b.BufferSubData(offset, vsub.Elements(), subsize);
+            }
+
+            for (int k = 0; k < validateCalls; k++) {
+              offset = RandomInteger<size_t>(0, size);
+              subsize = RandomInteger<size_t>(0, size - offset);
+              CheckValidateAllTypes(b, offset, subsize);
+            }
+          } // validateCalls
+        } // bufferSubDataCalls
+      } // j
+    } // i
+  } // maxBufferSize
+}
+
--- a/dom/canvas/gtest/moz.build
+++ b/dom/canvas/gtest/moz.build
@@ -2,18 +2,22 @@
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 with Files('**'):
     BUG_COMPONENT = ('Core', 'Canvas: 2D')
 
+with Files('*WebGL*'):
+    BUG_COMPONENT = ('Core', 'Canvas: WebGL')
+
 UNIFIED_SOURCES += [
     'TestImageBitmapColorUtils.cpp',
+    'TestWebGLElementArrayCache.cpp',
 ]
 
 LOCAL_INCLUDES += [
     '/dom/canvas',
     '/media/libyuv/libyuv/include'
 ]
 
 FINAL_LIBRARY = 'xul-gtest'
--- a/dom/canvas/moz.build
+++ b/dom/canvas/moz.build
@@ -120,16 +120,17 @@ UNIFIED_SOURCES += [
     'WebGLContextLossHandler.cpp',
     'WebGLContextState.cpp',
     'WebGLContextTextures.cpp',
     'WebGLContextUnchecked.cpp',
     'WebGLContextUtils.cpp',
     'WebGLContextValidate.cpp',
     'WebGLContextVertexArray.cpp',
     'WebGLContextVertices.cpp',
+    'WebGLElementArrayCache.cpp',
     'WebGLExtensionBase.cpp',
     'WebGLExtensionBlendMinMax.cpp',
     'WebGLExtensionColorBufferFloat.cpp',
     'WebGLExtensionColorBufferHalfFloat.cpp',
     'WebGLExtensionCompressedTextureASTC.cpp',
     'WebGLExtensionCompressedTextureATC.cpp',
     'WebGLExtensionCompressedTextureES3.cpp',
     'WebGLExtensionCompressedTextureETC1.cpp',
--- a/dom/canvas/test/webgl-conf/generated-mochitest.ini
+++ b/dom/canvas/test/webgl-conf/generated-mochitest.ini
@@ -4953,16 +4953,17 @@ fail-if = (os == 'mac') || (os == 'win')
 skip-if = (os == 'android' || os == 'linux')
 [generated/test_2_conformance2__rendering__clear-srgb-color-buffer.html]
 skip-if = (os == 'android' || os == 'linux')
 [generated/test_2_conformance2__rendering__clipping-wide-points.html]
 skip-if = (os == 'android' || os == 'linux')
 [generated/test_2_conformance2__rendering__draw-buffers.html]
 skip-if = (os == 'android' || os == 'linux')
 [generated/test_2_conformance2__rendering__element-index-uint.html]
+fail-if = (os != 'win')
 skip-if = (os == 'android' || os == 'linux')
 [generated/test_2_conformance2__rendering__framebuffer-completeness-unaffected.html]
 skip-if = (os == 'android' || os == 'linux')
 [generated/test_2_conformance2__rendering__framebuffer-unsupported.html]
 fail-if = (os == 'mac')
 skip-if = (os == 'android' || os == 'linux')
 [generated/test_2_conformance2__rendering__fs-color-type-mismatch-color-buffer-type.html]
 fail-if = (os == 'mac') || (os == 'win')
@@ -6119,26 +6120,29 @@ skip-if = (os == 'android' || os == 'lin
 skip-if = (os == 'android' || os == 'linux')
 [generated/test_2_conformance__buffers__buffer-data-array-buffer-delete.html]
 skip-if = (os == 'android' || os == 'linux')
 [generated/test_2_conformance__buffers__buffer-uninitialized.html]
 skip-if = (os == 'android' || os == 'linux')
 [generated/test_2_conformance__buffers__element-array-buffer-delete-recreate.html]
 skip-if = (os == 'android' || os == 'linux')
 [generated/test_2_conformance__buffers__index-validation-copies-indices.html]
+fail-if = (os != 'win')
 skip-if = (os == 'android' || os == 'linux')
 [generated/test_2_conformance__buffers__index-validation-crash-with-buffer-sub-data.html]
 skip-if = (os == 'android' || os == 'linux')
 [generated/test_2_conformance__buffers__index-validation-large-buffer.html]
 skip-if = (os == 'android' || os == 'linux')
 [generated/test_2_conformance__buffers__index-validation-verifies-too-many-indices.html]
+fail-if = (os != 'win')
 skip-if = (os == 'android' || os == 'linux')
 [generated/test_2_conformance__buffers__index-validation-with-resized-buffer.html]
 skip-if = (os == 'android' || os == 'linux')
 [generated/test_2_conformance__buffers__index-validation.html]
+fail-if = (os != 'win')
 skip-if = (os == 'android' || os == 'linux')
 [generated/test_2_conformance__canvas__buffer-offscreen-test.html]
 skip-if = (os == 'android' || os == 'linux')
 [generated/test_2_conformance__canvas__buffer-preserve-test.html]
 skip-if = (os == 'android' || os == 'linux')
 [generated/test_2_conformance__canvas__canvas-test.html]
 skip-if = (os == 'win') || (os == 'android' || os == 'linux')
 [generated/test_2_conformance__canvas__canvas-zero-size.html]
@@ -7250,16 +7254,17 @@ skip-if = (os == 'android' || os == 'lin
 skip-if = (os == 'android' || os == 'linux')
 [generated/test_2_conformance__rendering__culling.html]
 skip-if = (os == 'android' || os == 'linux')
 [generated/test_2_conformance__rendering__default-texture-draw-bug.html]
 skip-if = (os == 'android' || os == 'linux')
 [generated/test_2_conformance__rendering__draw-arrays-out-of-bounds.html]
 skip-if = (os == 'android' || os == 'linux')
 [generated/test_2_conformance__rendering__draw-elements-out-of-bounds.html]
+fail-if = (os != 'win')
 skip-if = (os == 'android' || os == 'linux')
 [generated/test_2_conformance__rendering__draw-with-changing-start-vertex-bug.html]
 skip-if = (os == 'android' || os == 'linux')
 [generated/test_2_conformance__rendering__framebuffer-switch.html]
 skip-if = (os == 'android' || os == 'linux')
 [generated/test_2_conformance__rendering__framebuffer-texture-switch.html]
 skip-if = (os == 'android' || os == 'linux')
 [generated/test_2_conformance__rendering__gl-clear.html]
--- a/dom/canvas/test/webgl-conf/mochitest-errata.ini
+++ b/dom/canvas/test/webgl-conf/mochitest-errata.ini
@@ -66,16 +66,30 @@ skip-if = 1
 # Timing out
 [generated/test_conformance__uniforms__uniform-default-values.html]
 # Timeout on Windows, crash on Android/Linux.
 skip-if = (os == 'android') || (os == 'linux') || (os == 'win')
 [generated/test_conformance__ogles__GL__mat3__mat3_001_to_006.html]
 # Timeout on D3D11
 skip-if = (os == 'win')
 
+####################
+# Tests expect conservative index validation, which we skip on WebGL 2.
+# ANGLE still provides it though, so they pass on windows.
+[generated/test_2_conformance__rendering__draw-elements-out-of-bounds.html]
+fail-if = (os != 'win')
+[generated/test_2_conformance__buffers__index-validation-copies-indices.html]
+fail-if = (os != 'win')
+[generated/test_2_conformance__buffers__index-validation.html]
+fail-if = (os != 'win')
+[generated/test_2_conformance__buffers__index-validation-verifies-too-many-indices.html]
+fail-if = (os != 'win')
+[generated/test_2_conformance2__rendering__element-index-uint.html]
+fail-if = (os != 'win')
+
 ########################################################################
 # Complicated
 
 [generated/test_conformance__context__context-attributes-alpha-depth-stencil-antialias.html]
 fail-if = (os == 'mac' && os_version == '10.6')
 # Asserts on 'B2G ICS Emulator Debug' and linux debug. Crashes on Android.
 skip-if = (os == 'b2g') || (os == 'linux') || (os == 'android')
 
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -98,17 +98,16 @@ static const char* const sExtensionNames
     "GL_ARB_geometry_shader4",
     "GL_ARB_half_float_pixel",
     "GL_ARB_instanced_arrays",
     "GL_ARB_internalformat_query",
     "GL_ARB_invalidate_subdata",
     "GL_ARB_map_buffer_range",
     "GL_ARB_occlusion_query2",
     "GL_ARB_pixel_buffer_object",
-    "GL_ARB_robust_buffer_access_behavior",
     "GL_ARB_robustness",
     "GL_ARB_sampler_objects",
     "GL_ARB_seamless_cube_map",
     "GL_ARB_shader_texture_lod",
     "GL_ARB_sync",
     "GL_ARB_texture_compression",
     "GL_ARB_texture_float",
     "GL_ARB_texture_non_power_of_two",
@@ -153,18 +152,16 @@ static const char* const sExtensionNames
     "GL_EXT_texture_storage",
     "GL_EXT_timer_query",
     "GL_EXT_transform_feedback",
     "GL_EXT_unpack_subimage",
     "GL_IMG_read_format",
     "GL_IMG_texture_compression_pvrtc",
     "GL_IMG_texture_npot",
     "GL_KHR_debug",
-    "GL_KHR_robust_buffer_access_behavior",
-    "GL_KHR_robustness",
     "GL_KHR_texture_compression_astc_hdr",
     "GL_KHR_texture_compression_astc_ldr",
     "GL_NV_draw_instanced",
     "GL_NV_fence",
     "GL_NV_framebuffer_blit",
     "GL_NV_geometry_program4",
     "GL_NV_half_float",
     "GL_NV_instanced_arrays",
@@ -1004,21 +1001,16 @@ GLContext::InitWithPrefixImpl(const char
     {
         NS_ERROR("GLContext requires support for framebuffer objects.");
         return false;
     }
     MOZ_RELEASE_ASSERT(mSymbols.fBindFramebuffer, "GFX: mSymbols.fBindFramebuffer zero or not set.");
 
     ////////////////
 
-    const auto err = mSymbols.fGetError();
-    MOZ_RELEASE_ASSERT(!err);
-    if (err)
-        return false;
-
     LoadMoreSymbols(prefix, trygl);
 
     ////////////////////////////////////////////////////////////////////////////
 
     raw_fGetIntegerv(LOCAL_GL_VIEWPORT, mViewportRect);
     raw_fGetIntegerv(LOCAL_GL_SCISSOR_BOX, mScissorRect);
     raw_fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
     raw_fGetIntegerv(LOCAL_GL_MAX_CUBE_MAP_TEXTURE_SIZE, &mMaxCubeMapTextureSize);
@@ -1146,38 +1138,40 @@ GLContext::LoadMoreSymbols(const char* p
                                                               GLFeature feature)
     {
         const bool useCore = this->IsFeatureProvidedByCoreSymbols(feature);
         const auto list = useCore ? coreList : extList;
         return fnLoadForFeature(list, feature);
     };
 
     if (IsSupported(GLFeature::robustness)) {
-        const auto resetStrategy = GetIntAs<GLuint>(LOCAL_GL_RESET_NOTIFICATION_STRATEGY);
-        if (resetStrategy != LOCAL_GL_LOSE_CONTEXT_ON_RESET) {
-            MOZ_ASSERT(resetStrategy == LOCAL_GL_NO_RESET_NOTIFICATION);
-            NS_WARNING("Robustness supported, but not active!");
-            MarkUnsupported(GLFeature::robustness);
+        bool hasRobustness = false;
+
+        if (!hasRobustness && IsExtensionSupported(ARB_robustness)) {
+            const SymLoadStruct symbols[] = {
+                { (PRFuncPtr*) &mSymbols.fGetGraphicsResetStatus, { "GetGraphicsResetStatusARB", nullptr } },
+                END_SYMBOLS
+            };
+            if (fnLoadForExt(symbols, ARB_robustness)) {
+                hasRobustness = true;
+            }
         }
-    }
-    if (IsSupported(GLFeature::robustness)) {
-        const SymLoadStruct symbols[] = {
-            { (PRFuncPtr*) &mSymbols.fGetGraphicsResetStatus, { "GetGraphicsResetStatus",
-                                                                "GetGraphicsResetStatusARB",
-                                                                "GetGraphicsResetStatusKHR",
-                                                                "GetGraphicsResetStatusEXT",
-                                                                nullptr } },
-            END_SYMBOLS
-        };
-        if (fnLoadForFeature(symbols, GLFeature::robustness)) {
-            const auto status = mSymbols.fGetGraphicsResetStatus();
-            MOZ_ALWAYS_TRUE(!status);
-
-            const auto err = mSymbols.fGetError();
-            MOZ_ALWAYS_TRUE(!err);
+
+        if (!hasRobustness && IsExtensionSupported(EXT_robustness)) {
+            const SymLoadStruct symbols[] = {
+                { (PRFuncPtr*) &mSymbols.fGetGraphicsResetStatus, { "GetGraphicsResetStatusEXT", nullptr } },
+                END_SYMBOLS
+            };
+            if (fnLoadForExt(symbols, EXT_robustness)) {
+                hasRobustness = true;
+            }
+        }
+
+        if (!hasRobustness) {
+            MarkUnsupported(GLFeature::robustness);
         }
     }
 
     if (IsSupported(GLFeature::sync)) {
         const SymLoadStruct symbols[] = {
             { (PRFuncPtr*) &mSymbols.fFenceSync,      { "FenceSync",      nullptr } },
             { (PRFuncPtr*) &mSymbols.fIsSync,         { "IsSync",         nullptr } },
             { (PRFuncPtr*) &mSymbols.fDeleteSync,     { "DeleteSync",     nullptr } },
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -116,17 +116,16 @@ enum class GLFeature {
     prim_restart,
     prim_restart_fixed,
     query_counter,
     query_objects,
     query_time_elapsed,
     read_buffer,
     renderbuffer_color_float,
     renderbuffer_color_half_float,
-    robust_buffer_access_behavior,
     robustness,
     sRGB_framebuffer,
     sRGB_texture,
     sampler_objects,
     seamless_cube_map_opt_in,
     shader_texture_lod,
     split_framebuffer,
     standard_derivatives,
@@ -196,16 +195,20 @@ class GLContext
     : public GLLibraryLoader
     , public GenericAtomicRefCounted
     , public SupportsWeakPtr<GLContext>
 {
 public:
     MOZ_DECLARE_WEAKREFERENCE_TYPENAME(GLContext)
 
 // -----------------------------------------------------------------------------
+// basic enums
+public:
+
+// -----------------------------------------------------------------------------
 // basic getters
 public:
 
     /**
      * Returns true if the context is using ANGLE. This should only be overridden
      * for an ANGLE implementation.
      */
     virtual bool IsANGLE() const {
@@ -426,17 +429,16 @@ public:
         ARB_geometry_shader4,
         ARB_half_float_pixel,
         ARB_instanced_arrays,
         ARB_internalformat_query,
         ARB_invalidate_subdata,
         ARB_map_buffer_range,
         ARB_occlusion_query2,
         ARB_pixel_buffer_object,
-        ARB_robust_buffer_access_behavior,
         ARB_robustness,
         ARB_sampler_objects,
         ARB_seamless_cube_map,
         ARB_shader_texture_lod,
         ARB_sync,
         ARB_texture_compression,
         ARB_texture_float,
         ARB_texture_non_power_of_two,
@@ -481,18 +483,16 @@ public:
         EXT_texture_storage,
         EXT_timer_query,
         EXT_transform_feedback,
         EXT_unpack_subimage,
         IMG_read_format,
         IMG_texture_compression_pvrtc,
         IMG_texture_npot,
         KHR_debug,
-        KHR_robust_buffer_access_behavior,
-        KHR_robustness,
         KHR_texture_compression_astc_hdr,
         KHR_texture_compression_astc_ldr,
         NV_draw_instanced,
         NV_fence,
         NV_framebuffer_blit,
         NV_geometry_program4,
         NV_half_float,
         NV_instanced_arrays,
@@ -566,16 +566,25 @@ private:
      */
     void MarkUnsupported(GLFeature feature);
 
     /**
      * Is this feature supported using the core (unsuffixed) symbols?
      */
     bool IsFeatureProvidedByCoreSymbols(GLFeature feature);
 
+// -----------------------------------------------------------------------------
+// Robustness handling
+private:
+    /**
+     * The derived class is expected to provide information on whether or not it
+     * supports robustness.
+     */
+    virtual bool SupportsRobustness() const = 0;
+
 public:
 // -----------------------------------------------------------------------------
 // Error handling
     static const char* GLErrorToString(GLenum aError) {
         switch (aError) {
             case LOCAL_GL_INVALID_ENUM:
                 return "GL_INVALID_ENUM";
             case LOCAL_GL_INVALID_VALUE:
--- a/gfx/gl/GLContextCGL.h
+++ b/gfx/gl/GLContextCGL.h
@@ -50,16 +50,18 @@ public:
     virtual bool IsCurrent() override;
 
     virtual GLenum GetPreferredARGB32Format() const override;
 
     virtual bool SetupLookupFunction() override;
 
     virtual bool IsDoubleBuffered() const override;
 
+    virtual bool SupportsRobustness() const override { return false; }
+
     virtual bool SwapBuffers() override;
 
     virtual void GetWSIInfo(nsCString* const out) const override;
 };
 
 } // namespace gl
 } // namespace mozilla
 
--- a/gfx/gl/GLContextEAGL.h
+++ b/gfx/gl/GLContextEAGL.h
@@ -46,16 +46,18 @@ public:
     virtual bool MakeCurrentImpl(bool aForce) override;
 
     virtual bool IsCurrent() override;
 
     virtual bool SetupLookupFunction() override;
 
     virtual bool IsDoubleBuffered() const override;
 
+    virtual bool SupportsRobustness() const override { return false; }
+
     virtual bool SwapBuffers() override;
 
     virtual void GetWSIInfo(nsCString* const out) const override;
 
     virtual GLuint GetDefaultFramebuffer() override {
         return mBackbufferFB;
     }
 
--- a/gfx/gl/GLContextEGL.h
+++ b/gfx/gl/GLContextEGL.h
@@ -53,16 +53,20 @@ public:
     virtual bool IsDoubleBuffered() const override {
         return mIsDoubleBuffered;
     }
 
     void SetIsDoubleBuffered(bool aIsDB) {
         mIsDoubleBuffered = aIsDB;
     }
 
+    virtual bool SupportsRobustness() const override {
+        return sEGLLibrary.HasRobustness();
+    }
+
     virtual bool IsANGLE() const override {
         return sEGLLibrary.IsANGLE();
     }
 
     virtual bool IsWARP() const override {
         return sEGLLibrary.IsWARP();
     }
 
--- a/gfx/gl/GLContextFeatures.cpp
+++ b/gfx/gl/GLContextFeatures.cpp
@@ -528,35 +528,23 @@ static const FeatureInfo sFeatureInfoArr
         {
             GLContext::ARB_texture_float,
             GLContext::EXT_color_buffer_float,
             GLContext::EXT_color_buffer_half_float,
             GLContext::Extensions_End
         }
     },
     {
-        "robust_buffer_access_behavior",
-        GLVersion::NONE,
-        GLESVersion::NONE,
-        GLContext::Extension_None,
-        {
-            GLContext::ARB_robust_buffer_access_behavior,
-            GLContext::KHR_robust_buffer_access_behavior,
-            GLContext::Extensions_End
-        }
-    },
-    {
         "robustness",
         GLVersion::NONE,
         GLESVersion::NONE,
         GLContext::Extension_None,
         {
             GLContext::ARB_robustness,
             GLContext::EXT_robustness,
-            GLContext::KHR_robustness,
             GLContext::Extensions_End
         }
     },
     {
         "sRGB_framebuffer",
         GLVersion::GL3,
         GLESVersion::ES3,
         GLContext::ARB_framebuffer_sRGB,
--- a/gfx/gl/GLContextGLX.h
+++ b/gfx/gl/GLContextGLX.h
@@ -51,16 +51,18 @@ public:
     virtual bool MakeCurrentImpl(bool aForce) override;
 
     virtual bool IsCurrent() override;
 
     virtual bool SetupLookupFunction() override;
 
     virtual bool IsDoubleBuffered() const override;
 
+    virtual bool SupportsRobustness() const override;
+
     virtual bool SwapBuffers() override;
 
     virtual void GetWSIInfo(nsCString* const out) const override;
 
     // Overrides the current GLXDrawable backing the context and makes the
     // context current.
     bool OverrideDrawable(GLXDrawable drawable);
 
--- a/gfx/gl/GLContextProviderEGL.cpp
+++ b/gfx/gl/GLContextProviderEGL.cpp
@@ -79,17 +79,17 @@ static bool
 CreateConfig(EGLConfig* aConfig);
 
 // append three zeros at the end of attribs list to work around
 // EGL implementation bugs that iterate until they find 0, instead of
 // EGL_NONE. See bug 948406.
 #define EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS \
      LOCAL_EGL_NONE, 0, 0, 0
 
-static EGLint kTerminationAttribs[] = {
+static EGLint gTerminationAttribs[] = {
     EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS
 };
 
 static int
 next_power_of_two(int v)
 {
     v--;
     v |= v >> 1;
@@ -477,48 +477,41 @@ GLContextEGL::CreateGLContext(CreateCont
         *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_ES");
         NS_WARNING("Failed to bind API to GLES!");
         return nullptr;
     }
 
     EGLContext eglShareContext = shareContext ? shareContext->mContext
                                               : EGL_NO_CONTEXT;
 
-    std::vector<EGLint> contextAttribs;
-
-    contextAttribs.push_back(LOCAL_EGL_CONTEXT_CLIENT_VERSION);
-    if (flags & CreateContextFlags::PREFER_ES3)
-        contextAttribs.push_back(3);
-    else
-        contextAttribs.push_back(2);
+    nsTArray<EGLint> contextAttribs;
 
-    if (sEGLLibrary.IsExtensionSupported(GLLibraryEGL::KHR_create_context)) {
-        contextAttribs.push_back(LOCAL_EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR);
-        contextAttribs.push_back(LOCAL_EGL_LOSE_CONTEXT_ON_RESET_KHR);
-        contextAttribs.push_back(LOCAL_EGL_CONTEXT_FLAGS_KHR);
-        contextAttribs.push_back(LOCAL_EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR);
-    } else if (sEGLLibrary.IsExtensionSupported(GLLibraryEGL::EXT_create_context_robustness)) {
-        contextAttribs.push_back(LOCAL_EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT);
-        contextAttribs.push_back(LOCAL_EGL_LOSE_CONTEXT_ON_RESET_EXT);
-        contextAttribs.push_back(LOCAL_EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT);
-        contextAttribs.push_back(LOCAL_EGL_TRUE);
+    contextAttribs.AppendElement(LOCAL_EGL_CONTEXT_CLIENT_VERSION);
+    if (flags & CreateContextFlags::PREFER_ES3)
+        contextAttribs.AppendElement(3);
+    else
+        contextAttribs.AppendElement(2);
+
+    if (sEGLLibrary.HasRobustness()) {
+//    contextAttribs.AppendElement(LOCAL_EGL_CONTEXT_ROBUST_ACCESS_EXT);
+//    contextAttribs.AppendElement(LOCAL_EGL_TRUE);
     }
 
-    for (const auto& cur : kTerminationAttribs) {
-        contextAttribs.push_back(cur);
+    for (size_t i = 0; i < MOZ_ARRAY_LENGTH(gTerminationAttribs); i++) {
+      contextAttribs.AppendElement(gTerminationAttribs[i]);
     }
 
     EGLContext context = sEGLLibrary.fCreateContext(EGL_DISPLAY(),
                                                     config,
                                                     eglShareContext,
-                                                    contextAttribs.data());
+                                                    contextAttribs.Elements());
     if (!context && shareContext) {
         shareContext = nullptr;
         context = sEGLLibrary.fCreateContext(EGL_DISPLAY(), config, EGL_NO_CONTEXT,
-                                             contextAttribs.data());
+                                             contextAttribs.Elements());
     }
     if (!context) {
         *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_CREATE");
         NS_WARNING("Failed to create EGLContext!");
         return nullptr;
     }
 
     RefPtr<GLContextEGL> glContext = new GLContextEGL(flags, caps, shareContext,
@@ -549,18 +542,18 @@ TRY_AGAIN_POWER_OF_TWO:
     if (bindToTextureFormat != LOCAL_EGL_NONE) {
         pbattrs.AppendElement(LOCAL_EGL_TEXTURE_TARGET);
         pbattrs.AppendElement(LOCAL_EGL_TEXTURE_2D);
 
         pbattrs.AppendElement(LOCAL_EGL_TEXTURE_FORMAT);
         pbattrs.AppendElement(bindToTextureFormat);
     }
 
-    for (const auto& cur : kTerminationAttribs) {
-        pbattrs.AppendElement(cur);
+    for (size_t i = 0; i < MOZ_ARRAY_LENGTH(gTerminationAttribs); i++) {
+      pbattrs.AppendElement(gTerminationAttribs[i]);
     }
 
     surface = sEGLLibrary.fCreatePbufferSurface(EGL_DISPLAY(), config, &pbattrs[0]);
     if (!surface) {
         if (!is_power_of_two(pbsize.width) ||
             !is_power_of_two(pbsize.height))
         {
             if (!is_power_of_two(pbsize.width))
--- a/gfx/gl/GLContextProviderGLX.cpp
+++ b/gfx/gl/GLContextProviderGLX.cpp
@@ -506,21 +506,19 @@ GLContextGLX::CreateGLContext(CreateCont
 
     do {
         error = false;
 
         GLXContext glxContext = shareContext ? shareContext->mContext : nullptr;
         if (glx.HasCreateContextAttribs()) {
             AutoTArray<int, 11> attrib_list;
             if (glx.HasRobustness()) {
-                const int robust_attribs[] = {
-                    LOCAL_GLX_CONTEXT_FLAGS_ARB,
-                    LOCAL_GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB,
-                    LOCAL_GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB,
-                    LOCAL_GLX_LOSE_CONTEXT_ON_RESET_ARB,
+                int robust_attribs[] = {
+                    LOCAL_GL_CONTEXT_FLAGS_ARB, LOCAL_GL_CONTEXT_ROBUST_ACCESS_BIT_ARB,
+                    LOCAL_GL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, LOCAL_GL_LOSE_CONTEXT_ON_RESET_ARB,
                 };
                 attrib_list.AppendElements(robust_attribs, MOZ_ARRAY_LENGTH(robust_attribs));
             }
             if (profile == ContextProfile::OpenGLCore) {
                 int core_attribs[] = {
                     LOCAL_GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
                     LOCAL_GLX_CONTEXT_MINOR_VERSION_ARB, 2,
                     LOCAL_GLX_CONTEXT_FLAGS_ARB, LOCAL_GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
@@ -660,16 +658,22 @@ GLContextGLX::SetupLookupFunction()
 
 bool
 GLContextGLX::IsDoubleBuffered() const
 {
     return mDoubleBuffered;
 }
 
 bool
+GLContextGLX::SupportsRobustness() const
+{
+    return mGLX->HasRobustness();
+}
+
+bool
 GLContextGLX::SwapBuffers()
 {
     if (!mDoubleBuffered)
         return false;
     mGLX->fSwapBuffers(mDisplay, mDrawable);
     return true;
 }
 
--- a/gfx/gl/GLContextProviderWGL.cpp
+++ b/gfx/gl/GLContextProviderWGL.cpp
@@ -357,16 +357,22 @@ GLContextWGL::SetIsDoubleBuffered(bool a
 
 bool
 GLContextWGL::IsDoubleBuffered() const
 {
     return mIsDoubleBuffered;
 }
 
 bool
+GLContextWGL::SupportsRobustness() const
+{
+    return sWGLLib.HasRobustness();
+}
+
+bool
 GLContextWGL::SwapBuffers() {
     if (!mIsDoubleBuffered)
         return false;
     return ::SwapBuffers(mDC);
 }
 
 void
 GLContextWGL::GetWSIInfo(nsCString* const out) const
--- a/gfx/gl/GLContextWGL.h
+++ b/gfx/gl/GLContextWGL.h
@@ -48,16 +48,18 @@ public:
     virtual bool MakeCurrentImpl(bool aForce) override;
 
     virtual bool IsCurrent() override;
 
     void SetIsDoubleBuffered(bool aIsDB);
 
     virtual bool IsDoubleBuffered() const override;
 
+    virtual bool SupportsRobustness() const override;
+
     virtual bool SwapBuffers() override;
 
     virtual bool SetupLookupFunction() override;
 
     virtual void GetWSIInfo(nsCString* const out) const override;
 
     HGLRC Context() { return mContext; }
 
--- a/gfx/gl/GLDefs.h
+++ b/gfx/gl/GLDefs.h
@@ -70,24 +70,19 @@
 #define LOCAL_EGL_D3D11_ELSE_D3D9_DISPLAY_ANGLE         ((EGLNativeDisplayType)-2)
 #define LOCAL_EGL_D3D11_ONLY_DISPLAY_ANGLE              ((EGLNativeDisplayType)-3)
 
 // WGL_NV_DX_interop
 #define LOCAL_WGL_ACCESS_READ_ONLY                      0x0000
 #define LOCAL_WGL_ACCESS_READ_WRITE                     0x0001
 #define LOCAL_WGL_ACCESS_WRITE_DISCARD                  0x0002
 
-// GL_KHR_robustness
-#define LOCAL_GL_CONTEXT_ROBUST_ACCESS                  0x90F3
-
 // Others
 #define LOCAL_EGL_PRESERVED_RESOURCES                   0x3030
 #define LOCAL_EGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_EXT 0x3138
-#define LOCAL_GL_RESET_NOTIFICATION_STRATEGY            0x8256
-#define LOCAL_GL_LOSE_CONTEXT_ON_RESET                  0x8252
-#define LOCAL_GL_NO_RESET_NOTIFICATION                  0x8261
+#define LOCAL_GL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256
 #define LOCAL_GL_CONTEXT_LOST                           0x9242
 #define LOCAL_GL_CONTEXT_FLAGS_ARB                      0x2094
 #define LOCAL_GL_CONTEXT_CORE_PROFILE_BIT_ARB           0x00000001
 #define LOCAL_GL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB  0x00000002
 #define LOCAL_GL_CONTEXT_ROBUST_ACCESS_BIT_ARB          0x00000004
 
 #endif
--- a/gfx/gl/GLLibraryEGL.cpp
+++ b/gfx/gl/GLLibraryEGL.cpp
@@ -48,17 +48,16 @@ static const char* sEGLExtensionNames[] 
     "EGL_EXT_create_context_robustness",
     "EGL_KHR_image",
     "EGL_KHR_fence_sync",
     "EGL_ANDROID_native_fence_sync",
     "EGL_ANDROID_image_crop",
     "EGL_ANGLE_platform_angle",
     "EGL_ANGLE_platform_angle_d3d",
     "EGL_ANGLE_d3d_share_handle_client_buffer"
-    "EGL_KHR_create_context",
 };
 
 #if defined(ANDROID)
 
 static PRLibrary* LoadApitraceLibrary()
 {
     // Initialization of gfx prefs here is only needed during the unit tests...
     gfxPrefs::GetSingleton();
--- a/gfx/gl/GLLibraryEGL.h
+++ b/gfx/gl/GLLibraryEGL.h
@@ -100,17 +100,16 @@ public:
         EXT_create_context_robustness,
         KHR_image,
         KHR_fence_sync,
         ANDROID_native_fence_sync,
         EGL_ANDROID_image_crop,
         ANGLE_platform_angle,
         ANGLE_platform_angle_d3d,
         ANGLE_d3d_share_handle_client_buffer,
-        KHR_create_context,
         Extensions_Max
     };
 
     bool IsExtensionSupported(EGLExtensions aKnownExtension) const {
         return mAvailableExtensions[aKnownExtension];
     }
 
     void MarkExtensionUnsupported(EGLExtensions aKnownExtension) {
@@ -323,16 +322,20 @@ public:
     bool HasKHRImageTexture2D() {
         return IsExtensionSupported(KHR_gl_texture_2D_image);
     }
 
     bool HasANGLESurfaceD3DTexture2DShareHandle() {
         return IsExtensionSupported(ANGLE_surface_d3d_texture_2d_share_handle);
     }
 
+    bool HasRobustness() const {
+        return IsExtensionSupported(EXT_create_context_robustness);
+    }
+
     bool ReadbackEGLImage(EGLImage image, gfx::DataSourceSurface* out_surface);
 
     bool EnsureInitialized(bool forceAccel, nsACString* const out_failureId);
 
     void DumpEGLConfig(EGLConfig cfg);
     void DumpEGLConfigs();
 
 private:
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -623,17 +623,16 @@ private:
 
   DECL_GFX_PREF(Live, "webgl.disabled",                        WebGLDisabled, bool, false);
 
   DECL_GFX_PREF(Live, "webgl.enable-draft-extensions",         WebGLDraftExtensionsEnabled, bool, false);
   DECL_GFX_PREF(Live, "webgl.enable-privileged-extensions",    WebGLPrivilegedExtensionsEnabled, bool, false);
   DECL_GFX_PREF(Live, "webgl.enable-webgl2",                   WebGL2Enabled, bool, true);
   DECL_GFX_PREF(Live, "webgl.force-enabled",                   WebGLForceEnabled, bool, false);
   DECL_GFX_PREF(Once, "webgl.force-layers-readback",           WebGLForceLayersReadback, bool, false);
-  DECL_GFX_PREF(Live, "webgl.force-index-validation",          WebGLForceIndexValidation, bool, false);
   DECL_GFX_PREF(Live, "webgl.lose-context-on-memory-pressure", WebGLLoseContextOnMemoryPressure, bool, false);
   DECL_GFX_PREF(Live, "webgl.max-warnings-per-context",        WebGLMaxWarningsPerContext, uint32_t, 32);
   DECL_GFX_PREF(Live, "webgl.min_capability_mode",             WebGLMinCapabilityMode, bool, false);
   DECL_GFX_PREF(Live, "webgl.msaa-force",                      WebGLForceMSAA, bool, false);
   DECL_GFX_PREF(Live, "webgl.prefer-16bpp",                    WebGLPrefer16bpp, bool, false);
   DECL_GFX_PREF(Live, "webgl.restore-context-when-visible",    WebGLRestoreWhenVisible, bool, true);
   DECL_GFX_PREF(Live, "webgl.allow-immediate-queries",         WebGLImmediateQueries, bool, false);
   DECL_GFX_PREF(Live, "webgl.allow-fb-invalidation",           WebGLFBInvalidation, bool, false);
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -4535,17 +4535,16 @@ pref("webgl.disabled", false);
 pref("webgl.disable-angle", false);
 pref("webgl.disable-wgl", false);
 pref("webgl.min_capability_mode", false);
 pref("webgl.disable-extensions", false);
 pref("webgl.msaa-force", false);
 pref("webgl.prefer-16bpp", false);
 pref("webgl.default-no-alpha", false);
 pref("webgl.force-layers-readback", false);
-pref("webgl.force-index-validation", false);
 pref("webgl.lose-context-on-memory-pressure", false);
 pref("webgl.can-lose-context-in-foreground", true);
 pref("webgl.restore-context-when-visible", true);
 pref("webgl.max-warnings-per-context", 32);
 pref("webgl.enable-draft-extensions", false);
 pref("webgl.enable-privileged-extensions", false);
 pref("webgl.bypass-shader-validation", false);
 pref("webgl.disable-fail-if-major-performance-caveat", false);