dom/canvas/WebGLContextValidate.cpp
author Brian Hackett <bhackett1024@gmail.com>
Wed, 03 Oct 2018 15:03:02 -1000
changeset 495437 00c56e070d3835c77fd0138a5c641d04a72dcb89
parent 486013 b807f63147aeb40dc4139b258d630cc1739662e0
child 497329 59befcc4a2d6886d0d642710dd4b0ebc25a62082
permissions -rw-r--r--
Bug 1495272 Part 1 - Don't register deferred finalizer in BindingJSObjectCreator until initialization succeeds, r=bz.

/* -*- Mode: C++; tab-width: 20; 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 "WebGLContext.h"

#include <algorithm>
#include "GLSLANG/ShaderLang.h"
#include "CanvasUtils.h"
#include "gfxPrefs.h"
#include "GLContext.h"
#include "jsfriendapi.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/Preferences.h"
#include "mozilla/Services.h"
#include "nsIObserverService.h"
#include "nsPrintfCString.h"
#include "WebGLActiveInfo.h"
#include "WebGLBuffer.h"
#include "WebGLContextUtils.h"
#include "WebGLFramebuffer.h"
#include "WebGLProgram.h"
#include "WebGLRenderbuffer.h"
#include "WebGLSampler.h"
#include "WebGLShader.h"
#include "WebGLTexture.h"
#include "WebGLUniformLocation.h"
#include "WebGLValidateStrings.h"
#include "WebGLVertexArray.h"
#include "WebGLVertexAttribData.h"

#if defined(MOZ_WIDGET_COCOA)
#include "nsCocoaFeatures.h"
#endif

////////////////////
// Minimum value constants defined in GLES 2.0.25 $6.2 "State Tables":
const uint32_t kMinMaxVertexAttribs          =   8; // Page 164
const uint32_t kMinMaxVertexUniformVectors   = 128; // Page 164
const uint32_t kMinMaxFragmentUniformVectors =  16; // Page 164
const uint32_t kMinMaxVaryingVectors         =   8; // Page 164

const uint32_t kMinMaxVertexTextureImageUnits   = 0; // Page 164
const uint32_t kMinMaxFragmentTextureImageUnits = 8; // Page 164
const uint32_t kMinMaxCombinedTextureImageUnits = 8; // Page 164

const uint32_t kMinMaxColorAttachments = 4;
const uint32_t kMinMaxDrawBuffers      = 4;

// These few deviate from the spec: (The minimum values in the spec are ridiculously low)
const uint32_t kMinMaxTextureSize        = 1024; // ES2 spec says `64` (p162)
const uint32_t kMinMaxCubeMapTextureSize =  512; // ES2 spec says `16` (p162)
const uint32_t kMinMaxRenderbufferSize   = 1024; // ES2 spec says `1` (p164)

// Minimum value constants defined in GLES 3.0.4 $6.2 "State Tables":
const uint32_t kMinMax3DTextureSize      = 256;
const uint32_t kMinMaxArrayTextureLayers = 256;

////////////////////
// "Common" but usable values to avoid WebGL fingerprinting:
const uint32_t kCommonMaxTextureSize        = 2048;
const uint32_t kCommonMaxCubeMapTextureSize = 2048;
const uint32_t kCommonMaxRenderbufferSize   = 2048;

const uint32_t kCommonMaxVertexTextureImageUnits   =  8;
const uint32_t kCommonMaxFragmentTextureImageUnits =  8;
const uint32_t kCommonMaxCombinedTextureImageUnits = 16;

const uint32_t kCommonMaxVertexAttribs          =  16;
const uint32_t kCommonMaxVertexUniformVectors   = 256;
const uint32_t kCommonMaxFragmentUniformVectors = 224;
const uint32_t kCommonMaxVaryingVectors         =   8;

const uint32_t kCommonMaxViewportDims = 4096;

// The following ranges came from a 2013 Moto E and an old macbook.
const float kCommonAliasedPointSizeRangeMin =  1;
const float kCommonAliasedPointSizeRangeMax = 63;
const float kCommonAliasedLineWidthRangeMin =  1;
const float kCommonAliasedLineWidthRangeMax =  1;

template<class T>
static bool
RestrictCap(T* const cap, const T restrictedVal)
{
    if (*cap < restrictedVal) {
        return false; // already too low!
    }

    *cap = restrictedVal;
    return true;
}

////////////////////

namespace mozilla {

bool
WebGLContext::ValidateBlendEquationEnum(GLenum mode, const char* info)
{
    switch (mode) {
    case LOCAL_GL_FUNC_ADD:
    case LOCAL_GL_FUNC_SUBTRACT:
    case LOCAL_GL_FUNC_REVERSE_SUBTRACT:
        return true;

    case LOCAL_GL_MIN:
    case LOCAL_GL_MAX:
        if (IsWebGL2() ||
            IsExtensionEnabled(WebGLExtensionID::EXT_blend_minmax))
        {
            return true;
        }

        break;

    default:
        break;
    }

    ErrorInvalidEnumInfo(info, mode);
    return false;
}

bool
WebGLContext::ValidateBlendFuncEnumsCompatibility(GLenum sfactor,
                                                  GLenum dfactor,
                                                  const char* info)
{
    bool sfactorIsConstantColor = sfactor == LOCAL_GL_CONSTANT_COLOR ||
                                  sfactor == LOCAL_GL_ONE_MINUS_CONSTANT_COLOR;
    bool sfactorIsConstantAlpha = sfactor == LOCAL_GL_CONSTANT_ALPHA ||
                                  sfactor == LOCAL_GL_ONE_MINUS_CONSTANT_ALPHA;
    bool dfactorIsConstantColor = dfactor == LOCAL_GL_CONSTANT_COLOR ||
                                  dfactor == LOCAL_GL_ONE_MINUS_CONSTANT_COLOR;
    bool dfactorIsConstantAlpha = dfactor == LOCAL_GL_CONSTANT_ALPHA ||
                                  dfactor == LOCAL_GL_ONE_MINUS_CONSTANT_ALPHA;
    if ( (sfactorIsConstantColor && dfactorIsConstantAlpha) ||
         (dfactorIsConstantColor && sfactorIsConstantAlpha) )
    {
        ErrorInvalidOperation("%s are mutually incompatible, see section 6.8 in"
                              " the WebGL 1.0 spec", info);
        return false;
    }

    return true;
}

bool
WebGLContext::ValidateStencilOpEnum(GLenum action, const char* info)
{
    switch (action) {
    case LOCAL_GL_KEEP:
    case LOCAL_GL_ZERO:
    case LOCAL_GL_REPLACE:
    case LOCAL_GL_INCR:
    case LOCAL_GL_INCR_WRAP:
    case LOCAL_GL_DECR:
    case LOCAL_GL_DECR_WRAP:
    case LOCAL_GL_INVERT:
        return true;

    default:
        ErrorInvalidEnumInfo(info, action);
        return false;
    }
}

bool
WebGLContext::ValidateFaceEnum(const GLenum face)
{
    switch (face) {
    case LOCAL_GL_FRONT:
    case LOCAL_GL_BACK:
    case LOCAL_GL_FRONT_AND_BACK:
        return true;

    default:
        ErrorInvalidEnumInfo("face", face);
        return false;
    }
}

bool
WebGLContext::ValidateUniformLocation(WebGLUniformLocation* loc)
{
    /* GLES 2.0.25, p38:
     *   If the value of location is -1, the Uniform* commands will silently
     *   ignore the data passed in, and the current uniform values will not be
     *   changed.
     */
    if (!loc)
        return false;

    if (!ValidateObjectAllowDeleted("loc", *loc))
        return false;

    if (!mCurrentProgram) {
        ErrorInvalidOperation("No program is currently bound.");
        return false;
    }

    return loc->ValidateForProgram(mCurrentProgram);
}

bool
WebGLContext::ValidateAttribArraySetter(uint32_t setterElemSize, uint32_t arrayLength)
{
    if (IsContextLost())
        return false;

    if (arrayLength < setterElemSize) {
        ErrorInvalidValue("Array must have >= %d elements.", setterElemSize);
        return false;
    }

    return true;
}

bool
WebGLContext::ValidateUniformSetter(WebGLUniformLocation* loc,
                                    uint8_t setterElemSize, GLenum setterType)
{
    if (IsContextLost())
        return false;

    if (!ValidateUniformLocation(loc))
        return false;

    if (!loc->ValidateSizeAndType(setterElemSize, setterType))
        return false;

    return true;
}

bool
WebGLContext::ValidateUniformArraySetter(WebGLUniformLocation* loc,
                                         uint8_t setterElemSize,
                                         GLenum setterType,
                                         uint32_t setterArraySize,
                                         uint32_t* const out_numElementsToUpload)
{
    if (IsContextLost())
        return false;

    if (!ValidateUniformLocation(loc))
        return false;

    if (!loc->ValidateSizeAndType(setterElemSize, setterType))
        return false;

    if (!loc->ValidateArrayLength(setterElemSize, setterArraySize))
        return false;

    const auto& elemCount = loc->mInfo->mActiveInfo->mElemCount;
    MOZ_ASSERT(elemCount > loc->mArrayIndex);
    const uint32_t uniformElemCount = elemCount - loc->mArrayIndex;

    *out_numElementsToUpload = std::min(uniformElemCount,
                                        setterArraySize / setterElemSize);
    return true;
}

bool
WebGLContext::ValidateUniformMatrixArraySetter(WebGLUniformLocation* loc,
                                               uint8_t setterCols,
                                               uint8_t setterRows,
                                               GLenum setterType,
                                               uint32_t setterArraySize,
                                               bool setterTranspose,
                                               uint32_t* const out_numElementsToUpload)
{
    const uint8_t setterElemSize = setterCols * setterRows;

    if (IsContextLost())
        return false;

    if (!ValidateUniformLocation(loc))
        return false;

    if (!loc->ValidateSizeAndType(setterElemSize, setterType))
        return false;

    if (!loc->ValidateArrayLength(setterElemSize, setterArraySize))
        return false;

    if (setterTranspose && !IsWebGL2()) {
        ErrorInvalidValue("`transpose` must be false.");
        return false;
    }

    const auto& elemCount = loc->mInfo->mActiveInfo->mElemCount;
    MOZ_ASSERT(elemCount > loc->mArrayIndex);
    const uint32_t uniformElemCount = elemCount - loc->mArrayIndex;

    *out_numElementsToUpload = std::min(uniformElemCount,
                                        setterArraySize / setterElemSize);
    return true;
}

bool
WebGLContext::InitAndValidateGL(FailureReason* const out_failReason)
{
    MOZ_RELEASE_ASSERT(gl, "GFX: GL not initialized");

    // Unconditionally create a new format usage authority. This is
    // important when restoring contexts and extensions need to add
    // formats back into the authority.
    mFormatUsage = CreateFormatUsage(gl);
    if (!mFormatUsage) {
        *out_failReason = { "FEATURE_FAILURE_WEBGL_FORMAT",
                            "Failed to create mFormatUsage." };
        return false;
    }

    GLenum error = gl->fGetError();
    if (error != LOCAL_GL_NO_ERROR) {
        const nsPrintfCString reason("GL error 0x%x occurred during OpenGL context"
                                     " initialization, before WebGL initialization!",
                                     error);
        *out_failReason = { "FEATURE_FAILURE_WEBGL_GLERR_1", reason };
        return false;
    }

    mDisableExtensions = gfxPrefs::WebGLDisableExtensions();
    mLoseContextOnMemoryPressure = gfxPrefs::WebGLLoseContextOnMemoryPressure();
    mCanLoseContextInForeground = gfxPrefs::WebGLCanLoseContextInForeground();
    mRestoreWhenVisible = gfxPrefs::WebGLRestoreWhenVisible();

    // These are the default values, see 6.2 State tables in the
    // OpenGL ES 2.0.25 spec.
    mColorWriteMask = 0x0f;
    mDriverColorMask = mColorWriteMask;
    mColorClearValue[0] = 0.f;
    mColorClearValue[1] = 0.f;
    mColorClearValue[2] = 0.f;
    mColorClearValue[3] = 0.f;
    mDepthWriteMask = true;
    mDepthClearValue = 1.f;
    mStencilClearValue = 0;
    mStencilRefFront = 0;
    mStencilRefBack = 0;

    mLineWidth = 1.0;

    /*
    // Technically, we should be setting mStencil[...] values to
    // `allOnes`, but either ANGLE breaks or the SGX540s on Try break.
    GLuint stencilBits = 0;
    gl->GetUIntegerv(LOCAL_GL_STENCIL_BITS, &stencilBits);
    GLuint allOnes = ~(UINT32_MAX << stencilBits);
    mStencilValueMaskFront = allOnes;
    mStencilValueMaskBack  = allOnes;
    mStencilWriteMaskFront = allOnes;
    mStencilWriteMaskBack  = allOnes;
    */

    gl->GetUIntegerv(LOCAL_GL_STENCIL_VALUE_MASK,      &mStencilValueMaskFront);
    gl->GetUIntegerv(LOCAL_GL_STENCIL_BACK_VALUE_MASK, &mStencilValueMaskBack);
    gl->GetUIntegerv(LOCAL_GL_STENCIL_WRITEMASK,       &mStencilWriteMaskFront);
    gl->GetUIntegerv(LOCAL_GL_STENCIL_BACK_WRITEMASK,  &mStencilWriteMaskBack);

    AssertUintParamCorrect(gl, LOCAL_GL_STENCIL_VALUE_MASK,      mStencilValueMaskFront);
    AssertUintParamCorrect(gl, LOCAL_GL_STENCIL_BACK_VALUE_MASK, mStencilValueMaskBack);
    AssertUintParamCorrect(gl, LOCAL_GL_STENCIL_WRITEMASK,       mStencilWriteMaskFront);
    AssertUintParamCorrect(gl, LOCAL_GL_STENCIL_BACK_WRITEMASK,  mStencilWriteMaskBack);

    mDitherEnabled = true;
    mRasterizerDiscardEnabled = false;
    mScissorTestEnabled = false;

    mDepthTestEnabled = 0;
    mDriverDepthTest = false;
    mStencilTestEnabled = 0;
    mDriverStencilTest = false;

    mGenerateMipmapHint = LOCAL_GL_DONT_CARE;

    // Bindings, etc.
    mActiveTexture = 0;
    mDefaultFB_DrawBuffer0 = LOCAL_GL_BACK;
    mDefaultFB_ReadBuffer = LOCAL_GL_BACK;

    mEmitContextLostErrorOnce = true;
    mWebGLError = LOCAL_GL_NO_ERROR;
    mUnderlyingGLError = LOCAL_GL_NO_ERROR;

    mBound2DTextures.Clear();
    mBoundCubeMapTextures.Clear();
    mBound3DTextures.Clear();
    mBound2DArrayTextures.Clear();
    mBoundSamplers.Clear();

    mBoundArrayBuffer = nullptr;
    mCurrentProgram = nullptr;

    mBoundDrawFramebuffer = nullptr;
    mBoundReadFramebuffer = nullptr;
    mBoundRenderbuffer = nullptr;

    gl->GetUIntegerv(LOCAL_GL_MAX_VERTEX_ATTRIBS, &mGLMaxVertexAttribs);

    if (mGLMaxVertexAttribs < 8) {
        const nsPrintfCString reason("GL_MAX_VERTEX_ATTRIBS: %d is < 8!",
                                     mGLMaxVertexAttribs);
        *out_failReason = { "FEATURE_FAILURE_WEBGL_V_ATRB", reason };
        return false;
    }

    // Note: GL_MAX_TEXTURE_UNITS is fixed at 4 for most desktop hardware,
    // even though the hardware supports much more.  The
    // GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS value is the accurate value.
    mGLMaxCombinedTextureImageUnits = gl->GetIntAs<GLuint>(LOCAL_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS);
    mGLMaxTextureUnits = mGLMaxCombinedTextureImageUnits;

    if (mGLMaxCombinedTextureImageUnits < 8) {
        const nsPrintfCString reason("GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: %u is < 8!",
                                     mGLMaxTextureUnits);
        *out_failReason = { "FEATURE_FAILURE_WEBGL_T_UNIT", reason };
        return false;
    }

    mBound2DTextures.SetLength(mGLMaxTextureUnits);
    mBoundCubeMapTextures.SetLength(mGLMaxTextureUnits);
    mBound3DTextures.SetLength(mGLMaxTextureUnits);
    mBound2DArrayTextures.SetLength(mGLMaxTextureUnits);
    mBoundSamplers.SetLength(mGLMaxTextureUnits);

    gl->fGetIntegerv(LOCAL_GL_MAX_VIEWPORT_DIMS, (GLint*)mGLMaxViewportDims);

    ////////////////

    gl->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, (GLint*)&mGLMaxTextureSize);
    gl->fGetIntegerv(LOCAL_GL_MAX_CUBE_MAP_TEXTURE_SIZE, (GLint*)&mGLMaxCubeMapTextureSize);
    gl->fGetIntegerv(LOCAL_GL_MAX_RENDERBUFFER_SIZE, (GLint*)&mGLMaxRenderbufferSize);

    if (!gl->GetPotentialInteger(LOCAL_GL_MAX_3D_TEXTURE_SIZE, (GLint*)&mGLMax3DTextureSize))
        mGLMax3DTextureSize = 0;
    if (!gl->GetPotentialInteger(LOCAL_GL_MAX_ARRAY_TEXTURE_LAYERS, (GLint*)&mGLMaxArrayTextureLayers))
        mGLMaxArrayTextureLayers = 0;

    gl->GetUIntegerv(LOCAL_GL_MAX_TEXTURE_IMAGE_UNITS, &mGLMaxFragmentTextureImageUnits);
    gl->GetUIntegerv(LOCAL_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &mGLMaxVertexTextureImageUnits);

    ////////////////

    mGLMaxColorAttachments = 1;
    mGLMaxDrawBuffers = 1;

    if (IsWebGL2()) {
        UpdateMaxDrawBuffers();
    }

    ////////////////

    if (gl->IsSupported(gl::GLFeature::ES2_compatibility)) {
        gl->GetUIntegerv(LOCAL_GL_MAX_FRAGMENT_UNIFORM_VECTORS, &mGLMaxFragmentUniformVectors);
        gl->GetUIntegerv(LOCAL_GL_MAX_VERTEX_UNIFORM_VECTORS, &mGLMaxVertexUniformVectors);
        gl->GetUIntegerv(LOCAL_GL_MAX_VARYING_VECTORS, &mGLMaxVaryingVectors);
    } else {
        gl->GetUIntegerv(LOCAL_GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &mGLMaxFragmentUniformVectors);
        mGLMaxFragmentUniformVectors /= 4;
        gl->GetUIntegerv(LOCAL_GL_MAX_VERTEX_UNIFORM_COMPONENTS, &mGLMaxVertexUniformVectors);
        mGLMaxVertexUniformVectors /= 4;

        /* We are now going to try to read GL_MAX_VERTEX_OUTPUT_COMPONENTS
         * and GL_MAX_FRAGMENT_INPUT_COMPONENTS, however these constants
         * only entered the OpenGL standard at OpenGL 3.2. So we will try
         * reading, and check OpenGL error for INVALID_ENUM.
         *
         * On the public_webgl list, "problematic GetParameter pnames"
         * thread, the following formula was given:
         *   maxVaryingVectors = min(GL_MAX_VERTEX_OUTPUT_COMPONENTS,
         *                           GL_MAX_FRAGMENT_INPUT_COMPONENTS) / 4
         */
        uint32_t maxVertexOutputComponents = 0;
        uint32_t maxFragmentInputComponents = 0;
        bool ok = true;
        ok &= gl->GetPotentialInteger(LOCAL_GL_MAX_VERTEX_OUTPUT_COMPONENTS,
                                      (GLint*)&maxVertexOutputComponents);
        ok &= gl->GetPotentialInteger(LOCAL_GL_MAX_FRAGMENT_INPUT_COMPONENTS,
                                      (GLint*)&maxFragmentInputComponents);
        if (ok) {
            mGLMaxVaryingVectors = std::min(maxVertexOutputComponents,
                                            maxFragmentInputComponents) / 4;
        } else {
            mGLMaxVaryingVectors = 16;
            // 16 = 64/4, and 64 is the min value for
            // maxVertexOutputComponents in the OpenGL 3.2 spec.
        }
    }

    ////////////////

    gl->fGetFloatv(LOCAL_GL_ALIASED_LINE_WIDTH_RANGE, mGLAliasedLineWidthRange);

    const GLenum driverPName = gl->IsCoreProfile() ? LOCAL_GL_POINT_SIZE_RANGE
                                                   : LOCAL_GL_ALIASED_POINT_SIZE_RANGE;
    gl->fGetFloatv(driverPName, mGLAliasedPointSizeRange);

    ////////////////

    if (gfxPrefs::WebGLMinCapabilityMode()) {
        bool ok = true;

        ok &= RestrictCap(&mGLMaxVertexTextureImageUnits  , kMinMaxVertexTextureImageUnits);
        ok &= RestrictCap(&mGLMaxFragmentTextureImageUnits, kMinMaxFragmentTextureImageUnits);
        ok &= RestrictCap(&mGLMaxCombinedTextureImageUnits, kMinMaxCombinedTextureImageUnits);

        ok &= RestrictCap(&mGLMaxVertexAttribs         , kMinMaxVertexAttribs);
        ok &= RestrictCap(&mGLMaxVertexUniformVectors  , kMinMaxVertexUniformVectors);
        ok &= RestrictCap(&mGLMaxFragmentUniformVectors, kMinMaxFragmentUniformVectors);
        ok &= RestrictCap(&mGLMaxVaryingVectors        , kMinMaxVaryingVectors);

        ok &= RestrictCap(&mGLMaxColorAttachments, kMinMaxColorAttachments);
        ok &= RestrictCap(&mGLMaxDrawBuffers     , kMinMaxDrawBuffers);

        ok &= RestrictCap(&mGLMaxTextureSize       , kMinMaxTextureSize);
        ok &= RestrictCap(&mGLMaxCubeMapTextureSize, kMinMaxCubeMapTextureSize);
        ok &= RestrictCap(&mGLMax3DTextureSize     , kMinMax3DTextureSize);

        ok &= RestrictCap(&mGLMaxArrayTextureLayers, kMinMaxArrayTextureLayers);
        ok &= RestrictCap(&mGLMaxRenderbufferSize  , kMinMaxRenderbufferSize);

        if (!ok) {
            GenerateWarning("Unable to restrict WebGL limits to minimums.");
            return false;
        }

        mDisableFragHighP = true;
    } else if (nsContentUtils::ShouldResistFingerprinting()) {
        bool ok = true;

        ok &= RestrictCap(&mGLMaxTextureSize       , kCommonMaxTextureSize);
        ok &= RestrictCap(&mGLMaxCubeMapTextureSize, kCommonMaxCubeMapTextureSize);
        ok &= RestrictCap(&mGLMaxRenderbufferSize  , kCommonMaxRenderbufferSize);

        ok &= RestrictCap(&mGLMaxVertexTextureImageUnits  , kCommonMaxVertexTextureImageUnits);
        ok &= RestrictCap(&mGLMaxFragmentTextureImageUnits, kCommonMaxFragmentTextureImageUnits);
        ok &= RestrictCap(&mGLMaxCombinedTextureImageUnits, kCommonMaxCombinedTextureImageUnits);

        ok &= RestrictCap(&mGLMaxVertexAttribs         , kCommonMaxVertexAttribs);
        ok &= RestrictCap(&mGLMaxVertexUniformVectors  , kCommonMaxVertexUniformVectors);
        ok &= RestrictCap(&mGLMaxFragmentUniformVectors, kCommonMaxFragmentUniformVectors);
        ok &= RestrictCap(&mGLMaxVaryingVectors        , kCommonMaxVaryingVectors);

        ok &= RestrictCap(&mGLAliasedLineWidthRange[0], kCommonAliasedLineWidthRangeMin);
        ok &= RestrictCap(&mGLAliasedLineWidthRange[1], kCommonAliasedLineWidthRangeMax);
        ok &= RestrictCap(&mGLAliasedPointSizeRange[0], kCommonAliasedPointSizeRangeMin);
        ok &= RestrictCap(&mGLAliasedPointSizeRange[1], kCommonAliasedPointSizeRangeMax);

        ok &= RestrictCap(&mGLMaxViewportDims[0], kCommonMaxViewportDims);
        ok &= RestrictCap(&mGLMaxViewportDims[1], kCommonMaxViewportDims);

        if (!ok) {
            GenerateWarning("Unable to restrict WebGL limits in order to resist fingerprinting");
            return false;
        }
    }

    ////////////////

    if (gl->IsCompatibilityProfile()) {
        gl->fEnable(LOCAL_GL_POINT_SPRITE);
    }

    if (!gl->IsGLES()) {
        gl->fEnable(LOCAL_GL_PROGRAM_POINT_SIZE);
    }

#ifdef XP_MACOSX
    if (gl->WorkAroundDriverBugs() &&
        gl->Vendor() == gl::GLVendor::ATI &&
        !nsCocoaFeatures::IsAtLeastVersion(10,9))
    {
        // The Mac ATI driver, in all known OSX version up to and including
        // 10.8, renders points sprites upside-down. (Apple bug 11778921)
        gl->fPointParameterf(LOCAL_GL_POINT_SPRITE_COORD_ORIGIN,
                             LOCAL_GL_LOWER_LEFT);
    }
#endif

    if (gl->IsSupported(gl::GLFeature::seamless_cube_map_opt_in)) {
        gl->fEnable(LOCAL_GL_TEXTURE_CUBE_MAP_SEAMLESS);
    }

    // Check the shader validator pref
    mBypassShaderValidation = gfxPrefs::WebGLBypassShaderValidator();

    // initialize shader translator
    if (!sh::Initialize()) {
        *out_failReason = { "FEATURE_FAILURE_WEBGL_GLSL",
                            "GLSL translator initialization failed!" };
        return false;
    }

    // Mesa can only be detected with the GL_VERSION string, of the form
    // "2.1 Mesa 7.11.0"
    const char* versionStr = (const char*)(gl->fGetString(LOCAL_GL_VERSION));
    mIsMesa = strstr(versionStr, "Mesa");

    // Notice that the point of calling fGetError here is not only to check for
    // errors, but also to reset the error flags so that a subsequent WebGL
    // getError call will give the correct result.
    error = gl->fGetError();
    if (error != LOCAL_GL_NO_ERROR) {
        const nsPrintfCString reason("GL error 0x%x occurred during WebGL context"
                                     " initialization!",
                                     error);
        *out_failReason = { "FEATURE_FAILURE_WEBGL_GLERR_2", reason };
        return false;
    }

    if (IsWebGL2() &&
        !InitWebGL2(out_failReason))
    {
        // Todo: Bug 898404: Only allow WebGL2 on GL>=3.0 on desktop GL.
        return false;
    }

    if (!gl->IsSupported(GLFeature::vertex_array_object)) {
        *out_failReason = { "FEATURE_FAILURE_WEBGL_VAOS",
                            "Requires vertex_array_object." };
        return false;
    }

    mDefaultVertexArray = WebGLVertexArray::Create(this);
    mDefaultVertexArray->mAttribs.SetLength(mGLMaxVertexAttribs);
    mBoundVertexArray = mDefaultVertexArray;

    // OpenGL core profiles remove the default VAO object from version
    // 4.0.0. We create a default VAO for all core profiles,
    // regardless of version.
    //
    // GL Spec 4.0.0:
    // (https://www.opengl.org/registry/doc/glspec40.core.20100311.pdf)
    // in Section E.2.2 "Removed Features", pg 397: "[...] The default
    // vertex array object (the name zero) is also deprecated. [...]"

    if (gl->IsCoreProfile()) {
        mDefaultVertexArray->GenVertexArray();
        mDefaultVertexArray->BindVertexArray();
    }

    mPixelStore_FlipY = false;
    mPixelStore_PremultiplyAlpha = false;
    mPixelStore_ColorspaceConversion = BROWSER_DEFAULT_WEBGL;
    mPixelStore_RequireFastPath = false;

    // GLES 3.0.4, p259:
    mPixelStore_UnpackImageHeight = 0;
    mPixelStore_UnpackSkipImages = 0;
    mPixelStore_UnpackRowLength = 0;
    mPixelStore_UnpackSkipRows = 0;
    mPixelStore_UnpackSkipPixels = 0;
    mPixelStore_UnpackAlignment = 4;
    mPixelStore_PackRowLength = 0;
    mPixelStore_PackSkipRows = 0;
    mPixelStore_PackSkipPixels = 0;
    mPixelStore_PackAlignment = 4;

    mPrimRestartTypeBytes = 0;

    mGenericVertexAttribTypes.reset(new GLenum[mGLMaxVertexAttribs]);
    std::fill_n(mGenericVertexAttribTypes.get(), mGLMaxVertexAttribs, LOCAL_GL_FLOAT);
    mGenericVertexAttribTypeInvalidator.InvalidateCaches();

    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);
    switch (gfxPrefs::WebGLForceIndexValidation()) {
    case -1:
        mNeedsIndexValidation = false;
        break;
    case 1:
        mNeedsIndexValidation = true;
        break;
    default:
        MOZ_ASSERT(gfxPrefs::WebGLForceIndexValidation() == 0);
        break;
    }

    return true;
}

bool
WebGLContext::ValidateFramebufferTarget(GLenum target)
{
    bool isValid = true;
    switch (target) {
    case LOCAL_GL_FRAMEBUFFER:
        break;

    case LOCAL_GL_DRAW_FRAMEBUFFER:
    case LOCAL_GL_READ_FRAMEBUFFER:
        isValid = IsWebGL2();
        break;

    default:
        isValid = false;
        break;
    }

    if (MOZ_LIKELY(isValid)) {
        return true;
    }

    ErrorInvalidEnumArg("target", target);
    return false;
}

} // namespace mozilla