1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include "WebGLContext.h"
7 
8 #include <algorithm>
9 #include "GLSLANG/ShaderLang.h"
10 #include "CanvasUtils.h"
11 #include "GLContext.h"
12 #include "jsfriendapi.h"
13 #include "mozilla/CheckedInt.h"
14 #include "mozilla/Preferences.h"
15 #include "mozilla/StaticPrefs_webgl.h"
16 #include "nsPrintfCString.h"
17 #include "WebGLBuffer.h"
18 #include "WebGLContextUtils.h"
19 #include "WebGLFramebuffer.h"
20 #include "WebGLProgram.h"
21 #include "WebGLRenderbuffer.h"
22 #include "WebGLSampler.h"
23 #include "WebGLShader.h"
24 #include "WebGLTexture.h"
25 #include "WebGLValidateStrings.h"
26 #include "WebGLVertexArray.h"
27 
28 #if defined(MOZ_WIDGET_COCOA)
29 #  include "nsCocoaFeatures.h"
30 #endif
31 
32 ////////////////////
33 // Minimum value constants defined in GLES 2.0.25 $6.2 "State Tables":
34 const uint32_t kMinMaxVertexAttribs = 8;            // Page 164
35 const uint32_t kMinMaxVertexUniformVectors = 128;   // Page 164
36 const uint32_t kMinMaxFragmentUniformVectors = 16;  // Page 164
37 const uint32_t kMinMaxVaryingVectors = 8;           // Page 164
38 
39 const uint32_t kMinMaxVertexTextureImageUnits = 0;    // Page 164
40 const uint32_t kMinMaxFragmentTextureImageUnits = 8;  // Page 164
41 const uint32_t kMinMaxCombinedTextureImageUnits = 8;  // Page 164
42 
43 const uint32_t kMinMaxDrawBuffers = 4;
44 
45 // These few deviate from the spec: (The minimum values in the spec are
46 // ridiculously low)
47 const uint32_t kMinMaxTextureSize = 1024;        // ES2 spec says `64` (p162)
48 const uint32_t kMinMaxCubeMapTextureSize = 512;  // ES2 spec says `16` (p162)
49 const uint32_t kMinMaxRenderbufferSize = 1024;   // ES2 spec says `1` (p164)
50 
51 // Minimum value constants defined in GLES 3.0.4 $6.2 "State Tables":
52 const uint32_t kMinMax3DTextureSize = 256;
53 const uint32_t kMinMaxArrayTextureLayers = 256;
54 
55 ////////////////////
56 // "Common" but usable values to avoid WebGL fingerprinting:
57 const uint32_t kCommonMaxTextureSize = 2048;
58 const uint32_t kCommonMaxCubeMapTextureSize = 2048;
59 const uint32_t kCommonMaxRenderbufferSize = 2048;
60 
61 const uint32_t kCommonMaxVertexTextureImageUnits = 8;
62 const uint32_t kCommonMaxFragmentTextureImageUnits = 8;
63 const uint32_t kCommonMaxCombinedTextureImageUnits = 16;
64 
65 const uint32_t kCommonMaxVertexAttribs = 16;
66 const uint32_t kCommonMaxVertexUniformVectors = 256;
67 const uint32_t kCommonMaxFragmentUniformVectors = 224;
68 const uint32_t kCommonMaxVaryingVectors = 8;
69 
70 const uint32_t kCommonMaxViewportDims = 4096;
71 
72 // The following ranges came from a 2013 Moto E and an old macbook.
73 const float kCommonAliasedPointSizeRangeMin = 1;
74 const float kCommonAliasedPointSizeRangeMax = 63;
75 const float kCommonAliasedLineWidthRangeMin = 1;
76 const float kCommonAliasedLineWidthRangeMax = 1;
77 
78 template <class T>
RestrictCap(T * const cap,const T restrictedVal)79 static bool RestrictCap(T* const cap, const T restrictedVal) {
80   if (*cap < restrictedVal) {
81     return false;  // already too low!
82   }
83 
84   *cap = restrictedVal;
85   return true;
86 }
87 
88 ////////////////////
89 
90 namespace mozilla {
91 
ValidateBlendEquationEnum(GLenum mode,const char * info)92 bool WebGLContext::ValidateBlendEquationEnum(GLenum mode, const char* info) {
93   switch (mode) {
94     case LOCAL_GL_FUNC_ADD:
95     case LOCAL_GL_FUNC_SUBTRACT:
96     case LOCAL_GL_FUNC_REVERSE_SUBTRACT:
97       return true;
98 
99     case LOCAL_GL_MIN:
100     case LOCAL_GL_MAX:
101       if (IsWebGL2() ||
102           IsExtensionEnabled(WebGLExtensionID::EXT_blend_minmax)) {
103         return true;
104       }
105 
106       break;
107 
108     default:
109       break;
110   }
111 
112   ErrorInvalidEnumInfo(info, mode);
113   return false;
114 }
115 
ValidateBlendFuncEnumsCompatibility(GLenum sfactor,GLenum dfactor,const char * info)116 bool WebGLContext::ValidateBlendFuncEnumsCompatibility(GLenum sfactor,
117                                                        GLenum dfactor,
118                                                        const char* info) {
119   bool sfactorIsConstantColor = sfactor == LOCAL_GL_CONSTANT_COLOR ||
120                                 sfactor == LOCAL_GL_ONE_MINUS_CONSTANT_COLOR;
121   bool sfactorIsConstantAlpha = sfactor == LOCAL_GL_CONSTANT_ALPHA ||
122                                 sfactor == LOCAL_GL_ONE_MINUS_CONSTANT_ALPHA;
123   bool dfactorIsConstantColor = dfactor == LOCAL_GL_CONSTANT_COLOR ||
124                                 dfactor == LOCAL_GL_ONE_MINUS_CONSTANT_COLOR;
125   bool dfactorIsConstantAlpha = dfactor == LOCAL_GL_CONSTANT_ALPHA ||
126                                 dfactor == LOCAL_GL_ONE_MINUS_CONSTANT_ALPHA;
127   if ((sfactorIsConstantColor && dfactorIsConstantAlpha) ||
128       (dfactorIsConstantColor && sfactorIsConstantAlpha)) {
129     ErrorInvalidOperation(
130         "%s are mutually incompatible, see section 6.8 in"
131         " the WebGL 1.0 spec",
132         info);
133     return false;
134   }
135 
136   return true;
137 }
138 
ValidateStencilOpEnum(GLenum action,const char * info)139 bool WebGLContext::ValidateStencilOpEnum(GLenum action, const char* info) {
140   switch (action) {
141     case LOCAL_GL_KEEP:
142     case LOCAL_GL_ZERO:
143     case LOCAL_GL_REPLACE:
144     case LOCAL_GL_INCR:
145     case LOCAL_GL_INCR_WRAP:
146     case LOCAL_GL_DECR:
147     case LOCAL_GL_DECR_WRAP:
148     case LOCAL_GL_INVERT:
149       return true;
150 
151     default:
152       ErrorInvalidEnumInfo(info, action);
153       return false;
154   }
155 }
156 
ValidateFaceEnum(const GLenum face)157 bool WebGLContext::ValidateFaceEnum(const GLenum face) {
158   switch (face) {
159     case LOCAL_GL_FRONT:
160     case LOCAL_GL_BACK:
161     case LOCAL_GL_FRONT_AND_BACK:
162       return true;
163 
164     default:
165       ErrorInvalidEnumInfo("face", face);
166       return false;
167   }
168 }
169 
ValidateAttribArraySetter(uint32_t setterElemSize,uint32_t arrayLength)170 bool WebGLContext::ValidateAttribArraySetter(uint32_t setterElemSize,
171                                              uint32_t arrayLength) {
172   if (IsContextLost()) return false;
173 
174   if (arrayLength < setterElemSize) {
175     ErrorInvalidValue("Array must have >= %d elements.", setterElemSize);
176     return false;
177   }
178 
179   return true;
180 }
181 
182 // ---------------------
183 
MakeLimits(const WebGLContext & webgl)184 static webgl::Limits MakeLimits(const WebGLContext& webgl) {
185   webgl::Limits limits;
186 
187   gl::GLContext& gl = *webgl.GL();
188 
189   // -
190 
191   for (const auto i : IntegerRange(UnderlyingValue(WebGLExtensionID::Max))) {
192     const auto ext = WebGLExtensionID(i);
193     limits.supportedExtensions[ext] = webgl.IsExtensionSupported(ext);
194   }
195 
196   // -
197   // WebGL 1
198 
199   // Note: GL_MAX_TEXTURE_UNITS is fixed at 4 for most desktop hardware,
200   // even though the hardware supports much more.  The
201   // GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS value is the accurate value.
202   gl.GetUIntegerv(LOCAL_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS,
203                   &limits.maxTexUnits);
204 
205   gl.GetUIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, &limits.maxTex2dSize);
206   gl.GetUIntegerv(LOCAL_GL_MAX_CUBE_MAP_TEXTURE_SIZE, &limits.maxTexCubeSize);
207   gl.GetUIntegerv(LOCAL_GL_MAX_VERTEX_ATTRIBS, &limits.maxVertexAttribs);
208 
209   auto dims = std::array<uint32_t, 2>{};
210   gl.GetUIntegerv(LOCAL_GL_MAX_VIEWPORT_DIMS, dims.data());
211   limits.maxViewportDim = std::min(dims[0], dims[1]);
212 
213   if (!gl.IsCoreProfile()) {
214     gl.fGetFloatv(LOCAL_GL_ALIASED_LINE_WIDTH_RANGE,
215                   limits.lineWidthRange.data());
216   }
217 
218   {
219     const GLenum driverPName = gl.IsCoreProfile()
220                                    ? LOCAL_GL_POINT_SIZE_RANGE
221                                    : LOCAL_GL_ALIASED_POINT_SIZE_RANGE;
222     gl.fGetFloatv(driverPName, limits.pointSizeRange.data());
223   }
224 
225   if (webgl.IsWebGL2()) {
226     gl.GetUIntegerv(LOCAL_GL_MAX_ARRAY_TEXTURE_LAYERS,
227                     &limits.maxTexArrayLayers);
228     gl.GetUIntegerv(LOCAL_GL_MAX_3D_TEXTURE_SIZE, &limits.maxTex3dSize);
229     gl.GetUIntegerv(LOCAL_GL_MAX_UNIFORM_BUFFER_BINDINGS,
230                     &limits.maxUniformBufferBindings);
231     gl.GetUIntegerv(LOCAL_GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT,
232                     &limits.uniformBufferOffsetAlignment);
233   }
234 
235   if (limits.supportedExtensions
236           [WebGLExtensionID::WEBGL_compressed_texture_astc]) {
237     limits.astcHdr = gl.IsExtensionSupported(
238         gl::GLContext::KHR_texture_compression_astc_hdr);
239   }
240 
241   if (webgl.IsWebGL2() ||
242       limits.supportedExtensions[WebGLExtensionID::WEBGL_draw_buffers]) {
243     gl.GetUIntegerv(LOCAL_GL_MAX_DRAW_BUFFERS, &limits.maxColorDrawBuffers);
244   }
245 
246   if (limits.supportedExtensions[WebGLExtensionID::EXT_disjoint_timer_query]) {
247     gl.fGetQueryiv(LOCAL_GL_TIME_ELAPSED_EXT, LOCAL_GL_QUERY_COUNTER_BITS,
248                    (int32_t*)&limits.queryCounterBitsTimeElapsed);
249     gl.fGetQueryiv(LOCAL_GL_TIMESTAMP_EXT, LOCAL_GL_QUERY_COUNTER_BITS,
250                    (int32_t*)&limits.queryCounterBitsTimestamp);
251   }
252 
253   if (limits.supportedExtensions[WebGLExtensionID::OVR_multiview2]) {
254     gl.GetUIntegerv(LOCAL_GL_MAX_VIEWS_OVR, &limits.maxMultiviewLayers);
255   }
256 
257   return limits;
258 }
259 
InitAndValidateGL(FailureReason * const out_failReason)260 bool WebGLContext::InitAndValidateGL(FailureReason* const out_failReason) {
261   MOZ_RELEASE_ASSERT(gl, "GFX: GL not initialized");
262 
263   // Unconditionally create a new format usage authority. This is
264   // important when restoring contexts and extensions need to add
265   // formats back into the authority.
266   mFormatUsage = CreateFormatUsage(gl);
267   if (!mFormatUsage) {
268     *out_failReason = {"FEATURE_FAILURE_WEBGL_FORMAT",
269                        "Failed to create mFormatUsage."};
270     return false;
271   }
272 
273   GLenum error = gl->fGetError();
274   if (error != LOCAL_GL_NO_ERROR) {
275     const nsPrintfCString reason(
276         "GL error 0x%x occurred during OpenGL context"
277         " initialization, before WebGL initialization!",
278         error);
279     *out_failReason = {"FEATURE_FAILURE_WEBGL_GLERR_1", reason};
280     return false;
281   }
282 
283   mLoseContextOnMemoryPressure =
284       StaticPrefs::webgl_lose_context_on_memory_pressure();
285   mCanLoseContextInForeground =
286       StaticPrefs::webgl_can_lose_context_in_foreground();
287 
288   // These are the default values, see 6.2 State tables in the
289   // OpenGL ES 2.0.25 spec.
290   mDriverColorMask = mColorWriteMask;
291   mColorClearValue[0] = 0.f;
292   mColorClearValue[1] = 0.f;
293   mColorClearValue[2] = 0.f;
294   mColorClearValue[3] = 0.f;
295   mDepthWriteMask = true;
296   mDepthClearValue = 1.f;
297   mStencilClearValue = 0;
298   mStencilRefFront = 0;
299   mStencilRefBack = 0;
300 
301   mLineWidth = 1.0;
302 
303   /*
304   // Technically, we should be setting mStencil[...] values to
305   // `allOnes`, but either ANGLE breaks or the SGX540s on Try break.
306   GLuint stencilBits = 0;
307   gl->GetUIntegerv(LOCAL_GL_STENCIL_BITS, &stencilBits);
308   GLuint allOnes = ~(UINT32_MAX << stencilBits);
309   mStencilValueMaskFront = allOnes;
310   mStencilValueMaskBack  = allOnes;
311   mStencilWriteMaskFront = allOnes;
312   mStencilWriteMaskBack  = allOnes;
313   */
314 
315   gl->GetUIntegerv(LOCAL_GL_STENCIL_VALUE_MASK, &mStencilValueMaskFront);
316   gl->GetUIntegerv(LOCAL_GL_STENCIL_BACK_VALUE_MASK, &mStencilValueMaskBack);
317   gl->GetUIntegerv(LOCAL_GL_STENCIL_WRITEMASK, &mStencilWriteMaskFront);
318   gl->GetUIntegerv(LOCAL_GL_STENCIL_BACK_WRITEMASK, &mStencilWriteMaskBack);
319 
320   AssertUintParamCorrect(gl, LOCAL_GL_STENCIL_VALUE_MASK,
321                          mStencilValueMaskFront);
322   AssertUintParamCorrect(gl, LOCAL_GL_STENCIL_BACK_VALUE_MASK,
323                          mStencilValueMaskBack);
324   AssertUintParamCorrect(gl, LOCAL_GL_STENCIL_WRITEMASK,
325                          mStencilWriteMaskFront);
326   AssertUintParamCorrect(gl, LOCAL_GL_STENCIL_BACK_WRITEMASK,
327                          mStencilWriteMaskBack);
328 
329   mDitherEnabled = true;
330   mRasterizerDiscardEnabled = false;
331   mScissorTestEnabled = false;
332 
333   mDepthTestEnabled = 0;
334   mDriverDepthTest = false;
335   mStencilTestEnabled = 0;
336   mDriverStencilTest = false;
337 
338   mGenerateMipmapHint = LOCAL_GL_DONT_CARE;
339 
340   // Bindings, etc.
341   mActiveTexture = 0;
342   mDefaultFB_DrawBuffer0 = LOCAL_GL_BACK;
343   mDefaultFB_ReadBuffer = LOCAL_GL_BACK;
344 
345   mWebGLError = LOCAL_GL_NO_ERROR;
346 
347   mBound2DTextures.Clear();
348   mBoundCubeMapTextures.Clear();
349   mBound3DTextures.Clear();
350   mBound2DArrayTextures.Clear();
351   mBoundSamplers.Clear();
352 
353   mBoundArrayBuffer = nullptr;
354   mCurrentProgram = nullptr;
355 
356   mBoundDrawFramebuffer = nullptr;
357   mBoundReadFramebuffer = nullptr;
358 
359   // -----------------------
360 
361   auto limits = MakeLimits(*this);
362 
363   // -
364 
365   if (limits.maxVertexAttribs < 8) {
366     const nsPrintfCString reason("GL_MAX_VERTEX_ATTRIBS: %d is < 8!",
367                                  limits.maxVertexAttribs);
368     *out_failReason = {"FEATURE_FAILURE_WEBGL_V_ATRB", reason};
369     return false;
370   }
371 
372   if (limits.maxTexUnits < 8) {
373     const nsPrintfCString reason(
374         "GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: %u is < 8!", limits.maxTexUnits);
375     *out_failReason = {"FEATURE_FAILURE_WEBGL_T_UNIT", reason};
376     return false;
377   }
378 
379   mBound2DTextures.SetLength(limits.maxTexUnits);
380   mBoundCubeMapTextures.SetLength(limits.maxTexUnits);
381   mBound3DTextures.SetLength(limits.maxTexUnits);
382   mBound2DArrayTextures.SetLength(limits.maxTexUnits);
383   mBoundSamplers.SetLength(limits.maxTexUnits);
384 
385   ////////////////
386 
387   gl->GetUIntegerv(LOCAL_GL_MAX_RENDERBUFFER_SIZE, &mGLMaxRenderbufferSize);
388   gl->GetUIntegerv(LOCAL_GL_MAX_TEXTURE_IMAGE_UNITS,
389                    &mGLMaxFragmentTextureImageUnits);
390   gl->GetUIntegerv(LOCAL_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS,
391                    &mGLMaxVertexTextureImageUnits);
392 
393   ////////////////
394 
395   if (gl->IsGLES()) {
396     mGLMaxFragmentUniformVectors =
397         gl->GetIntAs<uint32_t>(LOCAL_GL_MAX_FRAGMENT_UNIFORM_VECTORS);
398     mGLMaxVertexUniformVectors =
399         gl->GetIntAs<uint32_t>(LOCAL_GL_MAX_VERTEX_UNIFORM_VECTORS);
400     if (gl->Version() >= 300) {
401       mGLMaxVertexOutputVectors =
402           gl->GetIntAs<uint32_t>(LOCAL_GL_MAX_VERTEX_OUTPUT_COMPONENTS) / 4;
403       mGLMaxFragmentInputVectors =
404           gl->GetIntAs<uint32_t>(LOCAL_GL_MAX_FRAGMENT_INPUT_COMPONENTS) / 4;
405     } else {
406       mGLMaxFragmentInputVectors =
407           gl->GetIntAs<uint32_t>(LOCAL_GL_MAX_VARYING_VECTORS);
408       mGLMaxVertexOutputVectors = mGLMaxFragmentInputVectors;
409     }
410   } else {
411     mGLMaxFragmentUniformVectors =
412         gl->GetIntAs<uint32_t>(LOCAL_GL_MAX_FRAGMENT_UNIFORM_COMPONENTS) / 4;
413     mGLMaxVertexUniformVectors =
414         gl->GetIntAs<uint32_t>(LOCAL_GL_MAX_VERTEX_UNIFORM_COMPONENTS) / 4;
415 
416     if (gl->Version() >= 320) {
417       mGLMaxVertexOutputVectors =
418           gl->GetIntAs<uint32_t>(LOCAL_GL_MAX_VERTEX_OUTPUT_COMPONENTS) / 4;
419       mGLMaxFragmentInputVectors =
420           gl->GetIntAs<uint32_t>(LOCAL_GL_MAX_FRAGMENT_INPUT_COMPONENTS) / 4;
421     } else {
422       // Same enum val as GL2's GL_MAX_VARYING_FLOATS.
423       mGLMaxFragmentInputVectors =
424           gl->GetIntAs<uint32_t>(LOCAL_GL_MAX_VARYING_COMPONENTS) / 4;
425       mGLMaxVertexOutputVectors = mGLMaxFragmentInputVectors;
426     }
427   }
428 
429   ////////////////
430 
431   if (StaticPrefs::webgl_min_capability_mode()) {
432     bool ok = true;
433 
434     ok &= RestrictCap(&mGLMaxVertexTextureImageUnits,
435                       kMinMaxVertexTextureImageUnits);
436     ok &= RestrictCap(&mGLMaxFragmentTextureImageUnits,
437                       kMinMaxFragmentTextureImageUnits);
438     ok &= RestrictCap(&limits.maxTexUnits, kMinMaxCombinedTextureImageUnits);
439 
440     ok &= RestrictCap(&limits.maxVertexAttribs, kMinMaxVertexAttribs);
441     ok &= RestrictCap(&mGLMaxVertexUniformVectors, kMinMaxVertexUniformVectors);
442     ok &= RestrictCap(&mGLMaxFragmentUniformVectors,
443                       kMinMaxFragmentUniformVectors);
444     ok &= RestrictCap(&mGLMaxVertexOutputVectors, kMinMaxVaryingVectors);
445     ok &= RestrictCap(&mGLMaxFragmentInputVectors, kMinMaxVaryingVectors);
446 
447     ok &= RestrictCap(&limits.maxColorDrawBuffers, kMinMaxDrawBuffers);
448 
449     ok &= RestrictCap(&limits.maxTex2dSize, kMinMaxTextureSize);
450     ok &= RestrictCap(&limits.maxTexCubeSize, kMinMaxCubeMapTextureSize);
451     ok &= RestrictCap(&limits.maxTex3dSize, kMinMax3DTextureSize);
452 
453     ok &= RestrictCap(&limits.maxTexArrayLayers, kMinMaxArrayTextureLayers);
454     ok &= RestrictCap(&mGLMaxRenderbufferSize, kMinMaxRenderbufferSize);
455 
456     if (!ok) {
457       GenerateWarning("Unable to restrict WebGL limits to minimums.");
458       return false;
459     }
460 
461     mDisableFragHighP = true;
462   } else if (mResistFingerprinting) {
463     bool ok = true;
464 
465     ok &= RestrictCap(&limits.maxTex2dSize, kCommonMaxTextureSize);
466     ok &= RestrictCap(&limits.maxTexCubeSize, kCommonMaxCubeMapTextureSize);
467     ok &= RestrictCap(&mGLMaxRenderbufferSize, kCommonMaxRenderbufferSize);
468 
469     ok &= RestrictCap(&mGLMaxVertexTextureImageUnits,
470                       kCommonMaxVertexTextureImageUnits);
471     ok &= RestrictCap(&mGLMaxFragmentTextureImageUnits,
472                       kCommonMaxFragmentTextureImageUnits);
473     ok &= RestrictCap(&limits.maxTexUnits, kCommonMaxCombinedTextureImageUnits);
474 
475     ok &= RestrictCap(&limits.maxVertexAttribs, kCommonMaxVertexAttribs);
476     ok &= RestrictCap(&mGLMaxVertexUniformVectors,
477                       kCommonMaxVertexUniformVectors);
478     ok &= RestrictCap(&mGLMaxFragmentUniformVectors,
479                       kCommonMaxFragmentUniformVectors);
480     ok &= RestrictCap(&mGLMaxVertexOutputVectors, kCommonMaxVaryingVectors);
481     ok &= RestrictCap(&mGLMaxFragmentInputVectors, kCommonMaxVaryingVectors);
482 
483     if (limits.lineWidthRange[0] <= kCommonAliasedLineWidthRangeMin) {
484       limits.lineWidthRange[0] = kCommonAliasedLineWidthRangeMin;
485     } else {
486       ok = false;
487     }
488     if (limits.pointSizeRange[0] <= kCommonAliasedPointSizeRangeMin) {
489       limits.pointSizeRange[0] = kCommonAliasedPointSizeRangeMin;
490     } else {
491       ok = false;
492     }
493 
494     ok &=
495         RestrictCap(&limits.lineWidthRange[1], kCommonAliasedLineWidthRangeMax);
496     ok &=
497         RestrictCap(&limits.pointSizeRange[1], kCommonAliasedPointSizeRangeMax);
498     ok &= RestrictCap(&limits.maxViewportDim, kCommonMaxViewportDims);
499 
500     if (!ok) {
501       GenerateWarning(
502           "Unable to restrict WebGL limits in order to resist fingerprinting");
503       return false;
504     }
505   }
506 
507   mLimits = Some(limits);
508 
509   ////////////////
510 
511   if (gl->IsCompatibilityProfile()) {
512     gl->fEnable(LOCAL_GL_POINT_SPRITE);
513   }
514 
515   if (!gl->IsGLES()) {
516     gl->fEnable(LOCAL_GL_PROGRAM_POINT_SIZE);
517   }
518 
519 #ifdef XP_MACOSX
520   if (gl->WorkAroundDriverBugs() && gl->Vendor() == gl::GLVendor::ATI &&
521       !nsCocoaFeatures::IsAtLeastVersion(10, 9)) {
522     // The Mac ATI driver, in all known OSX version up to and including
523     // 10.8, renders points sprites upside-down. (Apple bug 11778921)
524     gl->fPointParameterf(LOCAL_GL_POINT_SPRITE_COORD_ORIGIN,
525                          LOCAL_GL_LOWER_LEFT);
526   }
527 #endif
528 
529   if (gl->IsSupported(gl::GLFeature::seamless_cube_map_opt_in)) {
530     gl->fEnable(LOCAL_GL_TEXTURE_CUBE_MAP_SEAMLESS);
531   }
532 
533   // initialize shader translator
534   if (!sh::Initialize()) {
535     *out_failReason = {"FEATURE_FAILURE_WEBGL_GLSL",
536                        "GLSL translator initialization failed!"};
537     return false;
538   }
539 
540   // Mesa can only be detected with the GL_VERSION string, of the form
541   // "2.1 Mesa 7.11.0"
542   const char* versionStr = (const char*)(gl->fGetString(LOCAL_GL_VERSION));
543   mIsMesa = strstr(versionStr, "Mesa");
544 
545   // Notice that the point of calling fGetError here is not only to check for
546   // errors, but also to reset the error flags so that a subsequent WebGL
547   // getError call will give the correct result.
548   error = gl->fGetError();
549   if (error != LOCAL_GL_NO_ERROR) {
550     const nsPrintfCString reason(
551         "GL error 0x%x occurred during WebGL context"
552         " initialization!",
553         error);
554     *out_failReason = {"FEATURE_FAILURE_WEBGL_GLERR_2", reason};
555     return false;
556   }
557 
558   if (IsWebGL2() && !InitWebGL2(out_failReason)) {
559     // Todo: Bug 898404: Only allow WebGL2 on GL>=3.0 on desktop GL.
560     return false;
561   }
562 
563   if (!gl->IsSupported(gl::GLFeature::vertex_array_object)) {
564     *out_failReason = {"FEATURE_FAILURE_WEBGL_VAOS",
565                        "Requires vertex_array_object."};
566     return false;
567   }
568 
569   // OpenGL core profiles remove the default VAO object from version
570   // 4.0.0. We create a default VAO for all core profiles,
571   // regardless of version.
572   //
573   // GL Spec 4.0.0:
574   // (https://www.opengl.org/registry/doc/glspec40.core.20100311.pdf)
575   // in Section E.2.2 "Removed Features", pg 397: "[...] The default
576   // vertex array object (the name zero) is also deprecated. [...]"
577   mDefaultVertexArray = WebGLVertexArray::Create(this);
578   mDefaultVertexArray->BindVertexArray();
579 
580   mPrimRestartTypeBytes = 0;
581 
582   mGenericVertexAttribTypes.assign(limits.maxVertexAttribs,
583                                    webgl::AttribBaseType::Float);
584   mGenericVertexAttribTypeInvalidator.InvalidateCaches();
585 
586   static const float kDefaultGenericVertexAttribData[4] = {0, 0, 0, 1};
587   memcpy(mGenericVertexAttrib0Data, kDefaultGenericVertexAttribData,
588          sizeof(mGenericVertexAttrib0Data));
589 
590   mFakeVertexAttrib0BufferObject = 0;
591 
592   mNeedsIndexValidation =
593       !gl->IsSupported(gl::GLFeature::robust_buffer_access_behavior);
594   switch (StaticPrefs::webgl_force_index_validation()) {
595     case -1:
596       mNeedsIndexValidation = false;
597       break;
598     case 1:
599       mNeedsIndexValidation = true;
600       break;
601     default:
602       MOZ_ASSERT(StaticPrefs::webgl_force_index_validation() == 0);
603       break;
604   }
605 
606   for (auto& cur : mExtensions) {
607     cur = {};
608   }
609 
610   return true;
611 }
612 
ValidateFramebufferTarget(GLenum target) const613 bool WebGLContext::ValidateFramebufferTarget(GLenum target) const {
614   bool isValid = true;
615   switch (target) {
616     case LOCAL_GL_FRAMEBUFFER:
617       break;
618 
619     case LOCAL_GL_DRAW_FRAMEBUFFER:
620     case LOCAL_GL_READ_FRAMEBUFFER:
621       isValid = IsWebGL2();
622       break;
623 
624     default:
625       isValid = false;
626       break;
627   }
628 
629   if (MOZ_LIKELY(isValid)) {
630     return true;
631   }
632 
633   ErrorInvalidEnumArg("target", target);
634   return false;
635 }
636 
637 }  // namespace mozilla
638