dom/canvas/WebGLContextValidate.cpp
author Gurzau Raul <rgurzau@mozilla.com>
Sat, 25 May 2019 09:07:49 +0300
changeset 475564 af54b2de7028db03f42207598f7a0b4ba81e262f
parent 475524 e0cd10d35327b9061a7b825153896fed5e5c151b
child 475614 37fd585d6c8ffd435e422eec6f3e77fca4dcc798
permissions -rw-r--r--
Backed out 31 changesets (bug 1552643, bug 1550422) for xpcshell crash on a CLOSED TREE. Backed out changeset e30c1aa75529 (bug 1552643) Backed out changeset caadcd7e02d3 (bug 1552643) Backed out changeset aa7086ab09be (bug 1552643) Backed out changeset 0b4029671710 (bug 1550422) Backed out changeset a16295296035 (bug 1550422) Backed out changeset 3b70307c0db5 (bug 1550422) Backed out changeset 69df7818d4a3 (bug 1550422) Backed out changeset d98dfc565927 (bug 1550422) Backed out changeset 6f0997976944 (bug 1550422) Backed out changeset 0edd264464c2 (bug 1550422) Backed out changeset 9ea6da7a74ec (bug 1550422) Backed out changeset f855f9309c8b (bug 1550422) Backed out changeset 1033546224a7 (bug 1550422) Backed out changeset ade7384c6186 (bug 1550422) Backed out changeset 75b04de7e99c (bug 1550422) Backed out changeset 91c3acdb2454 (bug 1550422) Backed out changeset 77d2f80257d1 (bug 1550422) Backed out changeset e0cd10d35327 (bug 1550422) Backed out changeset 097091082423 (bug 1550422) Backed out changeset 2f328853c1ab (bug 1550422) Backed out changeset f92f2cc29cb1 (bug 1550422) Backed out changeset 6dc82f88333d (bug 1550422) Backed out changeset c20f66494d69 (bug 1550422) Backed out changeset 2ba22cddeb6f (bug 1550422) Backed out changeset 3aa72f89e295 (bug 1550422) Backed out changeset ab4c4e806977 (bug 1550422) Backed out changeset 72e5de040dda (bug 1550422) Backed out changeset 7d3c2d486706 (bug 1550422) Backed out changeset 132e0b8d8468 (bug 1550422) Backed out changeset 54c85ac75dd0 (bug 1550422) Backed out changeset d7ba4a18dd54 (bug 1550422)

/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 "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(
    const WebGLUniformLocation* const 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(
    const WebGLUniformLocation* const loc, const uint8_t setterElemSize,
    const webgl::AttribBaseType setterType) {
  if (IsContextLost()) return false;

  if (!ValidateUniformLocation(loc)) return false;

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

  return true;
}

bool WebGLContext::ValidateUniformArraySetter(
    const WebGLUniformLocation* const loc, const uint8_t setterElemSize,
    const webgl::AttribBaseType setterType, const 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(
    const WebGLUniformLocation* const loc, const uint8_t setterCols,
    const uint8_t setterRows, const webgl::AttribBaseType setterType,
    const uint32_t setterArraySize, const 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();

  // 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;

  mWebGLError = 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;

  (void)gl->GetPotentialInteger(LOCAL_GL_MAX_VIEWS_OVR,
                                (GLint*)&mGLMaxMultiviewViews);

  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->IsGLES()) {
    mGLMaxFragmentUniformVectors =
        gl->GetIntAs<uint32_t>(LOCAL_GL_MAX_FRAGMENT_UNIFORM_VECTORS);
    mGLMaxVertexUniformVectors =
        gl->GetIntAs<uint32_t>(LOCAL_GL_MAX_VERTEX_UNIFORM_VECTORS);
    if (gl->Version() >= 300) {
      mGLMaxVertexOutputVectors =
          gl->GetIntAs<uint32_t>(LOCAL_GL_MAX_VERTEX_OUTPUT_COMPONENTS) / 4;
      mGLMaxFragmentInputVectors =
          gl->GetIntAs<uint32_t>(LOCAL_GL_MAX_FRAGMENT_INPUT_COMPONENTS) / 4;
    } else {
      mGLMaxFragmentInputVectors =
          gl->GetIntAs<uint32_t>(LOCAL_GL_MAX_VARYING_VECTORS);
      mGLMaxVertexOutputVectors = mGLMaxFragmentInputVectors;
    }
  } else {
    mGLMaxFragmentUniformVectors =
        gl->GetIntAs<uint32_t>(LOCAL_GL_MAX_FRAGMENT_UNIFORM_COMPONENTS) / 4;
    mGLMaxVertexUniformVectors =
        gl->GetIntAs<uint32_t>(LOCAL_GL_MAX_VERTEX_UNIFORM_COMPONENTS) / 4;

    if (gl->Version() >= 320) {
      mGLMaxVertexOutputVectors =
          gl->GetIntAs<uint32_t>(LOCAL_GL_MAX_VERTEX_OUTPUT_COMPONENTS) / 4;
      mGLMaxFragmentInputVectors =
          gl->GetIntAs<uint32_t>(LOCAL_GL_MAX_FRAGMENT_INPUT_COMPONENTS) / 4;
    } else {
      // Same enum val as GL2's GL_MAX_VARYING_FLOATS.
      mGLMaxFragmentInputVectors =
          gl->GetIntAs<uint32_t>(LOCAL_GL_MAX_VARYING_COMPONENTS) / 4;
      mGLMaxVertexOutputVectors = mGLMaxFragmentInputVectors;
    }
  }

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

  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);

  ////////////////
  bool shouldResistFingerprinting =
      mCanvasElement ?
                     // If we're constructed from a canvas element
          nsContentUtils::ShouldResistFingerprinting(GetOwnerDoc())
                     :
                     // If we're constructed from an offscreen canvas
          (mOffscreenCanvas->GetOwnerGlobal()
               ? nsContentUtils::ShouldResistFingerprinting(
                     mOffscreenCanvas->GetOwnerGlobal()->PrincipalOrNull())
               :
               // Last resort, just check the global preference
               nsContentUtils::ShouldResistFingerprinting());

  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(&mGLMaxVertexOutputVectors, kMinMaxVaryingVectors);
    ok &= RestrictCap(&mGLMaxFragmentInputVectors, 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 (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(&mGLMaxVertexOutputVectors, kCommonMaxVaryingVectors);
    ok &= RestrictCap(&mGLMaxFragmentInputVectors, 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);
  }

  // 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;
  }

  // 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. [...]"
  mDefaultVertexArray = WebGLVertexArray::Create(this);
  mDefaultVertexArray->BindVertexArray();
  mDefaultVertexArray->mAttribs.SetLength(mGLMaxVertexAttribs);

  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.assign(mGLMaxVertexAttribs,
                                   webgl::AttribBaseType::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) const {
  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