1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 #include "WebGL2Context.h"
8 
9 #include "WebGLActiveInfo.h"
10 #include "WebGLContextUtils.h"
11 #include "WebGLBuffer.h"
12 #include "WebGLVertexAttribData.h"
13 #include "WebGLShader.h"
14 #include "WebGLProgram.h"
15 #include "WebGLUniformLocation.h"
16 #include "WebGLFormats.h"
17 #include "WebGLFramebuffer.h"
18 #include "WebGLRenderbuffer.h"
19 #include "WebGLShaderPrecisionFormat.h"
20 #include "WebGLTexture.h"
21 #include "WebGLExtensions.h"
22 #include "WebGLVertexArray.h"
23 
24 #include "nsDebug.h"
25 #include "nsReadableUtils.h"
26 #include "nsString.h"
27 
28 #include "gfxContext.h"
29 #include "gfxPlatform.h"
30 #include "GLContext.h"
31 
32 #include "nsContentUtils.h"
33 #include "nsError.h"
34 #include "nsLayoutUtils.h"
35 
36 #include "CanvasUtils.h"
37 #include "gfxUtils.h"
38 
39 #include "jsfriendapi.h"
40 
41 #include "WebGLTexelConversions.h"
42 #include "WebGLValidateStrings.h"
43 #include <algorithm>
44 
45 // needed to check if current OS is lower than 10.7
46 #if defined(MOZ_WIDGET_COCOA)
47 #include "nsCocoaFeatures.h"
48 #endif
49 
50 #include "mozilla/DebugOnly.h"
51 #include "mozilla/dom/BindingUtils.h"
52 #include "mozilla/dom/ImageData.h"
53 #include "mozilla/dom/ToJSValue.h"
54 #include "mozilla/EndianUtils.h"
55 #include "mozilla/RefPtr.h"
56 #include "mozilla/UniquePtrExtensions.h"
57 
58 namespace mozilla {
59 
60 bool
ValidateObject(const char * funcName,const WebGLProgram & object)61 WebGLContext::ValidateObject(const char* funcName, const WebGLProgram& object)
62 {
63     return ValidateObject(funcName, object, true);
64 }
65 
66 bool
ValidateObject(const char * funcName,const WebGLShader & object)67 WebGLContext::ValidateObject(const char* funcName, const WebGLShader& object)
68 {
69     return ValidateObject(funcName, object, true);
70 }
71 
72 using namespace mozilla::dom;
73 using namespace mozilla::gfx;
74 using namespace mozilla::gl;
75 
76 //
77 //  WebGL API
78 //
79 
80 void
ActiveTexture(GLenum texture)81 WebGLContext::ActiveTexture(GLenum texture)
82 {
83     if (IsContextLost())
84         return;
85 
86     if (texture < LOCAL_GL_TEXTURE0 ||
87         texture >= LOCAL_GL_TEXTURE0 + uint32_t(mGLMaxTextureUnits))
88     {
89         return ErrorInvalidEnum(
90             "ActiveTexture: texture unit %d out of range. "
91             "Accepted values range from TEXTURE0 to TEXTURE0 + %d. "
92             "Notice that TEXTURE0 != 0.",
93             texture, mGLMaxTextureUnits);
94     }
95 
96     MakeContextCurrent();
97     mActiveTexture = texture - LOCAL_GL_TEXTURE0;
98     gl->fActiveTexture(texture);
99 }
100 
101 void
AttachShader(WebGLProgram & program,WebGLShader & shader)102 WebGLContext::AttachShader(WebGLProgram& program, WebGLShader& shader)
103 {
104     if (IsContextLost())
105         return;
106 
107     if (!ValidateObject("attachShader: program", program) ||
108         !ValidateObject("attachShader: shader", shader))
109     {
110         return;
111     }
112 
113     program.AttachShader(&shader);
114 }
115 
116 void
BindAttribLocation(WebGLProgram & prog,GLuint location,const nsAString & name)117 WebGLContext::BindAttribLocation(WebGLProgram& prog, GLuint location,
118                                  const nsAString& name)
119 {
120     if (IsContextLost())
121         return;
122 
123     if (!ValidateObject("bindAttribLocation: program", prog))
124         return;
125 
126     prog.BindAttribLocation(location, name);
127 }
128 
129 void
BindFramebuffer(GLenum target,WebGLFramebuffer * wfb)130 WebGLContext::BindFramebuffer(GLenum target, WebGLFramebuffer* wfb)
131 {
132     if (IsContextLost())
133         return;
134 
135     if (!ValidateFramebufferTarget(target, "bindFramebuffer"))
136         return;
137 
138     if (wfb && !ValidateObject("bindFramebuffer", *wfb))
139         return;
140 
141     MakeContextCurrent();
142 
143     if (!wfb) {
144         gl->fBindFramebuffer(target, 0);
145     } else {
146         GLuint framebuffername = wfb->mGLName;
147         gl->fBindFramebuffer(target, framebuffername);
148 #ifdef ANDROID
149         wfb->mIsFB = true;
150 #endif
151     }
152 
153     switch (target) {
154     case LOCAL_GL_FRAMEBUFFER:
155         mBoundDrawFramebuffer = wfb;
156         mBoundReadFramebuffer = wfb;
157         break;
158     case LOCAL_GL_DRAW_FRAMEBUFFER:
159         mBoundDrawFramebuffer = wfb;
160         break;
161     case LOCAL_GL_READ_FRAMEBUFFER:
162         mBoundReadFramebuffer = wfb;
163         break;
164     default:
165         break;
166     }
167 }
168 
169 void
BindRenderbuffer(GLenum target,WebGLRenderbuffer * wrb)170 WebGLContext::BindRenderbuffer(GLenum target, WebGLRenderbuffer* wrb)
171 {
172     if (IsContextLost())
173         return;
174 
175     if (target != LOCAL_GL_RENDERBUFFER)
176         return ErrorInvalidEnumInfo("bindRenderbuffer: target", target);
177 
178     if (wrb && !ValidateObject("bindRenderbuffer", *wrb))
179         return;
180 
181     // Usually, we would now call into glBindRenderbuffer. However, since we have to
182     // potentially emulate packed-depth-stencil, there's not a specific renderbuffer that
183     // we know we should bind here.
184     // Instead, we do all renderbuffer binding lazily.
185 
186     if (wrb) {
187         wrb->mHasBeenBound = true;
188     }
189 
190     mBoundRenderbuffer = wrb;
191 }
192 
BlendEquation(GLenum mode)193 void WebGLContext::BlendEquation(GLenum mode)
194 {
195     if (IsContextLost())
196         return;
197 
198     if (!ValidateBlendEquationEnum(mode, "blendEquation: mode"))
199         return;
200 
201     MakeContextCurrent();
202     gl->fBlendEquation(mode);
203 }
204 
BlendEquationSeparate(GLenum modeRGB,GLenum modeAlpha)205 void WebGLContext::BlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha)
206 {
207     if (IsContextLost())
208         return;
209 
210     if (!ValidateBlendEquationEnum(modeRGB, "blendEquationSeparate: modeRGB") ||
211         !ValidateBlendEquationEnum(modeAlpha, "blendEquationSeparate: modeAlpha"))
212         return;
213 
214     MakeContextCurrent();
215     gl->fBlendEquationSeparate(modeRGB, modeAlpha);
216 }
217 
BlendFunc(GLenum sfactor,GLenum dfactor)218 void WebGLContext::BlendFunc(GLenum sfactor, GLenum dfactor)
219 {
220     if (IsContextLost())
221         return;
222 
223     if (!ValidateBlendFuncSrcEnum(sfactor, "blendFunc: sfactor") ||
224         !ValidateBlendFuncDstEnum(dfactor, "blendFunc: dfactor"))
225         return;
226 
227     if (!ValidateBlendFuncEnumsCompatibility(sfactor, dfactor, "blendFuncSeparate: srcRGB and dstRGB"))
228         return;
229 
230     MakeContextCurrent();
231     gl->fBlendFunc(sfactor, dfactor);
232 }
233 
234 void
BlendFuncSeparate(GLenum srcRGB,GLenum dstRGB,GLenum srcAlpha,GLenum dstAlpha)235 WebGLContext::BlendFuncSeparate(GLenum srcRGB, GLenum dstRGB,
236                                 GLenum srcAlpha, GLenum dstAlpha)
237 {
238     if (IsContextLost())
239         return;
240 
241     if (!ValidateBlendFuncSrcEnum(srcRGB, "blendFuncSeparate: srcRGB") ||
242         !ValidateBlendFuncSrcEnum(srcAlpha, "blendFuncSeparate: srcAlpha") ||
243         !ValidateBlendFuncDstEnum(dstRGB, "blendFuncSeparate: dstRGB") ||
244         !ValidateBlendFuncDstEnum(dstAlpha, "blendFuncSeparate: dstAlpha"))
245         return;
246 
247     // note that we only check compatibity for the RGB enums, no need to for the Alpha enums, see
248     // "Section 6.8 forgetting to mention alpha factors?" thread on the public_webgl mailing list
249     if (!ValidateBlendFuncEnumsCompatibility(srcRGB, dstRGB, "blendFuncSeparate: srcRGB and dstRGB"))
250         return;
251 
252     MakeContextCurrent();
253     gl->fBlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha);
254 }
255 
256 GLenum
CheckFramebufferStatus(GLenum target)257 WebGLContext::CheckFramebufferStatus(GLenum target)
258 {
259     const char funcName[] = "checkFramebufferStatus";
260     if (IsContextLost())
261         return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED;
262 
263     if (!ValidateFramebufferTarget(target, funcName))
264         return 0;
265 
266     WebGLFramebuffer* fb;
267     switch (target) {
268     case LOCAL_GL_FRAMEBUFFER:
269     case LOCAL_GL_DRAW_FRAMEBUFFER:
270         fb = mBoundDrawFramebuffer;
271         break;
272 
273     case LOCAL_GL_READ_FRAMEBUFFER:
274         fb = mBoundReadFramebuffer;
275         break;
276 
277     default:
278         MOZ_CRASH("GFX: Bad target.");
279     }
280 
281     if (!fb)
282         return LOCAL_GL_FRAMEBUFFER_COMPLETE;
283 
284     return fb->CheckFramebufferStatus(funcName).get();
285 }
286 
287 already_AddRefed<WebGLProgram>
CreateProgram()288 WebGLContext::CreateProgram()
289 {
290     if (IsContextLost())
291         return nullptr;
292     RefPtr<WebGLProgram> globj = new WebGLProgram(this);
293     return globj.forget();
294 }
295 
296 already_AddRefed<WebGLShader>
CreateShader(GLenum type)297 WebGLContext::CreateShader(GLenum type)
298 {
299     if (IsContextLost())
300         return nullptr;
301 
302     if (type != LOCAL_GL_VERTEX_SHADER &&
303         type != LOCAL_GL_FRAGMENT_SHADER)
304     {
305         ErrorInvalidEnumInfo("createShader: type", type);
306         return nullptr;
307     }
308 
309     RefPtr<WebGLShader> shader = new WebGLShader(this, type);
310     return shader.forget();
311 }
312 
313 void
CullFace(GLenum face)314 WebGLContext::CullFace(GLenum face)
315 {
316     if (IsContextLost())
317         return;
318 
319     if (!ValidateFaceEnum(face, "cullFace"))
320         return;
321 
322     MakeContextCurrent();
323     gl->fCullFace(face);
324 }
325 
326 void
DeleteFramebuffer(WebGLFramebuffer * fbuf)327 WebGLContext::DeleteFramebuffer(WebGLFramebuffer* fbuf)
328 {
329     if (!ValidateDeleteObject("deleteFramebuffer", fbuf))
330         return;
331 
332     fbuf->RequestDelete();
333 
334     if (mBoundReadFramebuffer == mBoundDrawFramebuffer) {
335         if (mBoundDrawFramebuffer == fbuf) {
336             BindFramebuffer(LOCAL_GL_FRAMEBUFFER,
337                             static_cast<WebGLFramebuffer*>(nullptr));
338         }
339     } else if (mBoundDrawFramebuffer == fbuf) {
340         BindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER,
341                         static_cast<WebGLFramebuffer*>(nullptr));
342     } else if (mBoundReadFramebuffer == fbuf) {
343         BindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER,
344                         static_cast<WebGLFramebuffer*>(nullptr));
345     }
346 }
347 
348 void
DeleteRenderbuffer(WebGLRenderbuffer * rbuf)349 WebGLContext::DeleteRenderbuffer(WebGLRenderbuffer* rbuf)
350 {
351     if (!ValidateDeleteObject("deleteRenderbuffer", rbuf))
352         return;
353 
354     if (mBoundDrawFramebuffer)
355         mBoundDrawFramebuffer->DetachRenderbuffer(rbuf);
356 
357     if (mBoundReadFramebuffer)
358         mBoundReadFramebuffer->DetachRenderbuffer(rbuf);
359 
360     rbuf->InvalidateStatusOfAttachedFBs();
361 
362     if (mBoundRenderbuffer == rbuf)
363         BindRenderbuffer(LOCAL_GL_RENDERBUFFER, nullptr);
364 
365     rbuf->RequestDelete();
366 }
367 
368 void
DeleteTexture(WebGLTexture * tex)369 WebGLContext::DeleteTexture(WebGLTexture* tex)
370 {
371     if (!ValidateDeleteObject("deleteTexture", tex))
372         return;
373 
374     if (mBoundDrawFramebuffer)
375         mBoundDrawFramebuffer->DetachTexture(tex);
376 
377     if (mBoundReadFramebuffer)
378         mBoundReadFramebuffer->DetachTexture(tex);
379 
380     GLuint activeTexture = mActiveTexture;
381     for (int32_t i = 0; i < mGLMaxTextureUnits; i++) {
382         if (mBound2DTextures[i] == tex ||
383             mBoundCubeMapTextures[i] == tex ||
384             mBound3DTextures[i] == tex ||
385             mBound2DArrayTextures[i] == tex)
386         {
387             ActiveTexture(LOCAL_GL_TEXTURE0 + i);
388             BindTexture(tex->Target().get(), nullptr);
389         }
390     }
391     ActiveTexture(LOCAL_GL_TEXTURE0 + activeTexture);
392 
393     tex->RequestDelete();
394 }
395 
396 void
DeleteProgram(WebGLProgram * prog)397 WebGLContext::DeleteProgram(WebGLProgram* prog)
398 {
399     if (!ValidateDeleteObject("deleteProgram", prog))
400         return;
401 
402     prog->RequestDelete();
403 }
404 
405 void
DeleteShader(WebGLShader * shader)406 WebGLContext::DeleteShader(WebGLShader* shader)
407 {
408     if (!ValidateDeleteObject("deleteShader", shader))
409         return;
410 
411     shader->RequestDelete();
412 }
413 
414 void
DetachShader(WebGLProgram & program,const WebGLShader & shader)415 WebGLContext::DetachShader(WebGLProgram& program, const WebGLShader& shader)
416 {
417     if (IsContextLost())
418         return;
419 
420     // It's valid to attempt to detach a deleted shader, since it's still a
421     // shader.
422     if (!ValidateObject("detachShader: program", program) ||
423         !ValidateObjectAllowDeleted("detachShader: shader", shader))
424     {
425         return;
426     }
427 
428     program.DetachShader(&shader);
429 }
430 
431 void
DepthFunc(GLenum func)432 WebGLContext::DepthFunc(GLenum func)
433 {
434     if (IsContextLost())
435         return;
436 
437     if (!ValidateComparisonEnum(func, "depthFunc"))
438         return;
439 
440     MakeContextCurrent();
441     gl->fDepthFunc(func);
442 }
443 
444 void
DepthRange(GLfloat zNear,GLfloat zFar)445 WebGLContext::DepthRange(GLfloat zNear, GLfloat zFar)
446 {
447     if (IsContextLost())
448         return;
449 
450     if (zNear > zFar)
451         return ErrorInvalidOperation("depthRange: the near value is greater than the far value!");
452 
453     MakeContextCurrent();
454     gl->fDepthRange(zNear, zFar);
455 }
456 
457 void
FramebufferRenderbuffer(GLenum target,GLenum attachment,GLenum rbtarget,WebGLRenderbuffer * wrb)458 WebGLContext::FramebufferRenderbuffer(GLenum target, GLenum attachment,
459                                       GLenum rbtarget, WebGLRenderbuffer* wrb)
460 {
461     const char funcName[] = "framebufferRenderbuffer";
462     if (IsContextLost())
463         return;
464 
465     if (!ValidateFramebufferTarget(target, funcName))
466         return;
467 
468     WebGLFramebuffer* fb;
469     switch (target) {
470     case LOCAL_GL_FRAMEBUFFER:
471     case LOCAL_GL_DRAW_FRAMEBUFFER:
472         fb = mBoundDrawFramebuffer;
473         break;
474 
475     case LOCAL_GL_READ_FRAMEBUFFER:
476         fb = mBoundReadFramebuffer;
477         break;
478 
479     default:
480         MOZ_CRASH("GFX: Bad target.");
481     }
482 
483     if (!fb)
484         return ErrorInvalidOperation("%s: Cannot modify framebuffer 0.", funcName);
485 
486     fb->FramebufferRenderbuffer(funcName, attachment, rbtarget, wrb);
487 }
488 
489 void
FramebufferTexture2D(GLenum target,GLenum attachment,GLenum textarget,WebGLTexture * tobj,GLint level)490 WebGLContext::FramebufferTexture2D(GLenum target,
491                                    GLenum attachment,
492                                    GLenum textarget,
493                                    WebGLTexture* tobj,
494                                    GLint level)
495 {
496     const char funcName[] = "framebufferTexture2D";
497     if (IsContextLost())
498         return;
499 
500     if (!ValidateFramebufferTarget(target, funcName))
501         return;
502 
503     WebGLFramebuffer* fb;
504     switch (target) {
505     case LOCAL_GL_FRAMEBUFFER:
506     case LOCAL_GL_DRAW_FRAMEBUFFER:
507         fb = mBoundDrawFramebuffer;
508         break;
509 
510     case LOCAL_GL_READ_FRAMEBUFFER:
511         fb = mBoundReadFramebuffer;
512         break;
513 
514     default:
515         MOZ_CRASH("GFX: Bad target.");
516     }
517 
518     if (!fb)
519         return ErrorInvalidOperation("%s: Cannot modify framebuffer 0.", funcName);
520 
521     fb->FramebufferTexture2D(funcName, attachment, textarget, tobj, level);
522 }
523 
524 void
FrontFace(GLenum mode)525 WebGLContext::FrontFace(GLenum mode)
526 {
527     if (IsContextLost())
528         return;
529 
530     switch (mode) {
531         case LOCAL_GL_CW:
532         case LOCAL_GL_CCW:
533             break;
534         default:
535             return ErrorInvalidEnumInfo("frontFace: mode", mode);
536     }
537 
538     MakeContextCurrent();
539     gl->fFrontFace(mode);
540 }
541 
542 already_AddRefed<WebGLActiveInfo>
GetActiveAttrib(const WebGLProgram & prog,GLuint index)543 WebGLContext::GetActiveAttrib(const WebGLProgram& prog, GLuint index)
544 {
545     if (IsContextLost())
546         return nullptr;
547 
548     if (!ValidateObject("getActiveAttrib: program", prog))
549         return nullptr;
550 
551     return prog.GetActiveAttrib(index);
552 }
553 
554 already_AddRefed<WebGLActiveInfo>
GetActiveUniform(const WebGLProgram & prog,GLuint index)555 WebGLContext::GetActiveUniform(const WebGLProgram& prog, GLuint index)
556 {
557     if (IsContextLost())
558         return nullptr;
559 
560     if (!ValidateObject("getActiveUniform: program", prog))
561         return nullptr;
562 
563     return prog.GetActiveUniform(index);
564 }
565 
566 void
GetAttachedShaders(const WebGLProgram & prog,dom::Nullable<nsTArray<RefPtr<WebGLShader>>> & retval)567 WebGLContext::GetAttachedShaders(const WebGLProgram& prog,
568                                  dom::Nullable<nsTArray<RefPtr<WebGLShader>>>& retval)
569 {
570     retval.SetNull();
571     if (IsContextLost())
572         return;
573 
574     if (!ValidateObject("getAttachedShaders", prog))
575         return;
576 
577     prog.GetAttachedShaders(&retval.SetValue());
578 }
579 
580 GLint
GetAttribLocation(const WebGLProgram & prog,const nsAString & name)581 WebGLContext::GetAttribLocation(const WebGLProgram& prog, const nsAString& name)
582 {
583     if (IsContextLost())
584         return -1;
585 
586     if (!ValidateObject("getAttribLocation: program", prog))
587         return -1;
588 
589     return prog.GetAttribLocation(name);
590 }
591 
592 JS::Value
GetBufferParameter(GLenum target,GLenum pname)593 WebGLContext::GetBufferParameter(GLenum target, GLenum pname)
594 {
595     const char funcName[] = "getBufferParameter";
596     if (IsContextLost())
597         return JS::NullValue();
598 
599     const auto& slot = ValidateBufferSlot(funcName, target);
600     if (!slot)
601         return JS::NullValue();
602     const auto& buffer = *slot;
603 
604     if (!buffer) {
605         ErrorInvalidOperation("%s: Buffer for `target` is null.", funcName);
606         return JS::NullValue();
607     }
608 
609     switch (pname) {
610     case LOCAL_GL_BUFFER_SIZE:
611         return JS::NumberValue(buffer->ByteLength());
612 
613     case LOCAL_GL_BUFFER_USAGE:
614         return JS::NumberValue(buffer->Usage());
615 
616     default:
617         ErrorInvalidEnumInfo("getBufferParameter: parameter", pname);
618         return JS::NullValue();
619     }
620 }
621 
622 JS::Value
GetFramebufferAttachmentParameter(JSContext * cx,GLenum target,GLenum attachment,GLenum pname,ErrorResult & rv)623 WebGLContext::GetFramebufferAttachmentParameter(JSContext* cx,
624                                                 GLenum target,
625                                                 GLenum attachment,
626                                                 GLenum pname,
627                                                 ErrorResult& rv)
628 {
629     const char funcName[] = "getFramebufferAttachmentParameter";
630 
631     if (IsContextLost())
632         return JS::NullValue();
633 
634     if (!ValidateFramebufferTarget(target, funcName))
635         return JS::NullValue();
636 
637     WebGLFramebuffer* fb;
638     switch (target) {
639     case LOCAL_GL_FRAMEBUFFER:
640     case LOCAL_GL_DRAW_FRAMEBUFFER:
641         fb = mBoundDrawFramebuffer;
642         break;
643 
644     case LOCAL_GL_READ_FRAMEBUFFER:
645         fb = mBoundReadFramebuffer;
646         break;
647 
648     default:
649         MOZ_CRASH("GFX: Bad target.");
650     }
651 
652     MakeContextCurrent();
653 
654     if (fb)
655         return fb->GetAttachmentParameter(funcName, cx, target, attachment, pname, &rv);
656 
657     ////////////////////////////////////
658 
659     if (!IsWebGL2()) {
660         ErrorInvalidOperation("%s: Querying against the default framebuffer is not"
661                               " allowed in WebGL 1.",
662                               funcName);
663         return JS::NullValue();
664     }
665 
666     switch (attachment) {
667     case LOCAL_GL_BACK:
668     case LOCAL_GL_DEPTH:
669     case LOCAL_GL_STENCIL:
670         break;
671 
672     default:
673         ErrorInvalidEnum("%s: For the default framebuffer, can only query COLOR, DEPTH,"
674                          " or STENCIL.",
675                          funcName);
676         return JS::NullValue();
677     }
678 
679     switch (pname) {
680     case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE:
681         switch (attachment) {
682         case LOCAL_GL_BACK:
683             break;
684         case LOCAL_GL_DEPTH:
685             if (!mOptions.depth) {
686               return JS::Int32Value(LOCAL_GL_NONE);
687             }
688             break;
689         case LOCAL_GL_STENCIL:
690             if (!mOptions.stencil) {
691               return JS::Int32Value(LOCAL_GL_NONE);
692             }
693             break;
694         default:
695             ErrorInvalidEnum("%s: With the default framebuffer, can only query COLOR, DEPTH,"
696                              " or STENCIL for GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE",
697                              funcName);
698             return JS::NullValue();
699         }
700         return JS::Int32Value(LOCAL_GL_FRAMEBUFFER_DEFAULT);
701 
702     ////////////////
703 
704     case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE:
705     case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE:
706     case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE:
707         if (attachment == LOCAL_GL_BACK)
708             return JS::NumberValue(8);
709         return JS::NumberValue(0);
710 
711     case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE:
712         if (attachment == LOCAL_GL_BACK) {
713             if (mOptions.alpha) {
714                 return JS::NumberValue(8);
715             }
716             ErrorInvalidOperation("The default framebuffer doesn't contain an alpha buffer");
717             return JS::NullValue();
718         }
719         return JS::NumberValue(0);
720 
721     case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE:
722         if (attachment == LOCAL_GL_DEPTH) {
723             if (mOptions.depth) {
724                 return JS::NumberValue(24);
725             }
726             ErrorInvalidOperation("The default framebuffer doesn't contain an depth buffer");
727             return JS::NullValue();
728         }
729         return JS::NumberValue(0);
730 
731     case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE:
732         if (attachment == LOCAL_GL_STENCIL) {
733             if (mOptions.stencil) {
734                 return JS::NumberValue(8);
735             }
736             ErrorInvalidOperation("The default framebuffer doesn't contain an stencil buffer");
737             return JS::NullValue();
738         }
739         return JS::NumberValue(0);
740 
741     case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE:
742         if (attachment == LOCAL_GL_STENCIL) {
743             if (mOptions.stencil) {
744                 return JS::NumberValue(LOCAL_GL_UNSIGNED_INT);
745             }
746             ErrorInvalidOperation("The default framebuffer doesn't contain an stencil buffer");
747         } else if (attachment == LOCAL_GL_DEPTH) {
748             if (mOptions.depth) {
749                 return JS::NumberValue(LOCAL_GL_UNSIGNED_NORMALIZED);
750             }
751             ErrorInvalidOperation("The default framebuffer doesn't contain an depth buffer");
752         } else { // LOCAL_GL_BACK
753             return JS::NumberValue(LOCAL_GL_UNSIGNED_NORMALIZED);
754         }
755         return JS::NullValue();
756 
757     case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING:
758         if (attachment == LOCAL_GL_STENCIL) {
759             if (!mOptions.stencil) {
760                 ErrorInvalidOperation("The default framebuffer doesn't contain an stencil buffer");
761                 return JS::NullValue();
762             }
763         } else if (attachment == LOCAL_GL_DEPTH) {
764             if (!mOptions.depth) {
765                 ErrorInvalidOperation("The default framebuffer doesn't contain an depth buffer");
766                 return JS::NullValue();
767             }
768         }
769         return JS::NumberValue(LOCAL_GL_LINEAR);
770     }
771 
772     ErrorInvalidEnum("%s: Invalid pname: 0x%04x", funcName, pname);
773     return JS::NullValue();
774 }
775 
776 JS::Value
GetRenderbufferParameter(GLenum target,GLenum pname)777 WebGLContext::GetRenderbufferParameter(GLenum target, GLenum pname)
778 {
779     if (IsContextLost())
780         return JS::NullValue();
781 
782     if (target != LOCAL_GL_RENDERBUFFER) {
783         ErrorInvalidEnumInfo("getRenderbufferParameter: target", target);
784         return JS::NullValue();
785     }
786 
787     if (!mBoundRenderbuffer) {
788         ErrorInvalidOperation("getRenderbufferParameter: no render buffer is bound");
789         return JS::NullValue();
790     }
791 
792     MakeContextCurrent();
793 
794     switch (pname) {
795     case LOCAL_GL_RENDERBUFFER_SAMPLES:
796         if (!IsWebGL2())
797             break;
798         MOZ_FALLTHROUGH;
799 
800     case LOCAL_GL_RENDERBUFFER_WIDTH:
801     case LOCAL_GL_RENDERBUFFER_HEIGHT:
802     case LOCAL_GL_RENDERBUFFER_RED_SIZE:
803     case LOCAL_GL_RENDERBUFFER_GREEN_SIZE:
804     case LOCAL_GL_RENDERBUFFER_BLUE_SIZE:
805     case LOCAL_GL_RENDERBUFFER_ALPHA_SIZE:
806     case LOCAL_GL_RENDERBUFFER_DEPTH_SIZE:
807     case LOCAL_GL_RENDERBUFFER_STENCIL_SIZE:
808     case LOCAL_GL_RENDERBUFFER_INTERNAL_FORMAT:
809     {
810         // RB emulation means we have to ask the RB itself.
811         GLint i = mBoundRenderbuffer->GetRenderbufferParameter(target, pname);
812         return JS::Int32Value(i);
813     }
814 
815     default:
816         break;
817     }
818 
819     ErrorInvalidEnumInfo("getRenderbufferParameter: parameter", pname);
820     return JS::NullValue();
821 }
822 
823 already_AddRefed<WebGLTexture>
CreateTexture()824 WebGLContext::CreateTexture()
825 {
826     if (IsContextLost())
827         return nullptr;
828 
829     GLuint tex = 0;
830     MakeContextCurrent();
831     gl->fGenTextures(1, &tex);
832 
833     RefPtr<WebGLTexture> globj = new WebGLTexture(this, tex);
834     return globj.forget();
835 }
836 
837 static GLenum
GetAndClearError(GLenum * errorVar)838 GetAndClearError(GLenum* errorVar)
839 {
840     MOZ_ASSERT(errorVar);
841     GLenum ret = *errorVar;
842     *errorVar = LOCAL_GL_NO_ERROR;
843     return ret;
844 }
845 
846 GLenum
GetError()847 WebGLContext::GetError()
848 {
849     /* WebGL 1.0: Section 5.14.3: Setting and getting state:
850      *   If the context's webgl context lost flag is set, returns
851      *   CONTEXT_LOST_WEBGL the first time this method is called.
852      *   Afterward, returns NO_ERROR until the context has been
853      *   restored.
854      *
855      * WEBGL_lose_context:
856      *   [When this extension is enabled: ] loseContext and
857      *   restoreContext are allowed to generate INVALID_OPERATION errors
858      *   even when the context is lost.
859      */
860 
861     if (IsContextLost()) {
862         if (mEmitContextLostErrorOnce) {
863             mEmitContextLostErrorOnce = false;
864             return LOCAL_GL_CONTEXT_LOST;
865         }
866         // Don't return yet, since WEBGL_lose_contexts contradicts the
867         // original spec, and allows error generation while lost.
868     }
869 
870     GLenum err = GetAndClearError(&mWebGLError);
871     if (err != LOCAL_GL_NO_ERROR)
872         return err;
873 
874     if (IsContextLost())
875         return LOCAL_GL_NO_ERROR;
876 
877     // Either no WebGL-side error, or it's already been cleared.
878     // UnderlyingGL-side errors, now.
879 
880     MakeContextCurrent();
881     GetAndFlushUnderlyingGLErrors();
882 
883     err = GetAndClearError(&mUnderlyingGLError);
884     return err;
885 }
886 
887 JS::Value
GetProgramParameter(const WebGLProgram & prog,GLenum pname)888 WebGLContext::GetProgramParameter(const WebGLProgram& prog, GLenum pname)
889 {
890     if (IsContextLost())
891         return JS::NullValue();
892 
893     if (!ValidateObjectAllowDeleted("getProgramParameter: program", prog))
894         return JS::NullValue();
895 
896     return prog.GetProgramParameter(pname);
897 }
898 
899 void
GetProgramInfoLog(const WebGLProgram & prog,nsAString & retval)900 WebGLContext::GetProgramInfoLog(const WebGLProgram& prog, nsAString& retval)
901 {
902     retval.SetIsVoid(true);
903 
904     if (IsContextLost())
905         return;
906 
907     if (!ValidateObject("getProgramInfoLog: program", prog))
908         return;
909 
910     prog.GetProgramInfoLog(&retval);
911 }
912 
913 JS::Value
GetUniform(JSContext * js,const WebGLProgram & prog,const WebGLUniformLocation & loc)914 WebGLContext::GetUniform(JSContext* js, const WebGLProgram& prog,
915                          const WebGLUniformLocation& loc)
916 {
917     if (IsContextLost())
918         return JS::NullValue();
919 
920     if (!ValidateObject("getUniform: `program`", prog))
921         return JS::NullValue();
922 
923     if (!ValidateObjectAllowDeleted("getUniform: `location`", loc))
924         return JS::NullValue();
925 
926     if (!loc.ValidateForProgram(&prog, "getUniform"))
927         return JS::NullValue();
928 
929     return loc.GetUniform(js);
930 }
931 
932 already_AddRefed<WebGLUniformLocation>
GetUniformLocation(const WebGLProgram & prog,const nsAString & name)933 WebGLContext::GetUniformLocation(const WebGLProgram& prog, const nsAString& name)
934 {
935     if (IsContextLost())
936         return nullptr;
937 
938     if (!ValidateObject("getUniformLocation: program", prog))
939         return nullptr;
940 
941     return prog.GetUniformLocation(name);
942 }
943 
944 void
Hint(GLenum target,GLenum mode)945 WebGLContext::Hint(GLenum target, GLenum mode)
946 {
947     if (IsContextLost())
948         return;
949 
950     bool isValid = false;
951 
952     switch (target) {
953     case LOCAL_GL_GENERATE_MIPMAP_HINT:
954         mGenerateMipmapHint = mode;
955 
956         // Deprecated and removed in desktop GL Core profiles.
957         if (gl->IsCoreProfile())
958             return;
959 
960         isValid = true;
961         break;
962 
963     case LOCAL_GL_FRAGMENT_SHADER_DERIVATIVE_HINT:
964         if (IsWebGL2() ||
965             IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives))
966         {
967             isValid = true;
968         }
969         break;
970     }
971 
972     if (!isValid)
973         return ErrorInvalidEnum("hint: invalid hint");
974 
975     MakeContextCurrent();
976     gl->fHint(target, mode);
977 }
978 
979 bool
IsFramebuffer(const WebGLFramebuffer * fb)980 WebGLContext::IsFramebuffer(const WebGLFramebuffer* fb)
981 {
982     if (!ValidateIsObject("isFramebuffer", fb))
983         return false;
984 
985 #ifdef ANDROID
986     if (gl->WorkAroundDriverBugs() &&
987         gl->Renderer() == GLRenderer::AndroidEmulator)
988     {
989         return fb->mIsFB;
990     }
991 #endif
992 
993     MakeContextCurrent();
994     return gl->fIsFramebuffer(fb->mGLName);
995 }
996 
997 bool
IsProgram(const WebGLProgram * prog)998 WebGLContext::IsProgram(const WebGLProgram* prog)
999 {
1000     if (!ValidateIsObject("isProgram", prog))
1001         return false;
1002 
1003     return true;
1004 }
1005 
1006 bool
IsRenderbuffer(const WebGLRenderbuffer * rb)1007 WebGLContext::IsRenderbuffer(const WebGLRenderbuffer* rb)
1008 {
1009     if (!ValidateIsObject("isRenderbuffer", rb))
1010         return false;
1011 
1012     return rb->mHasBeenBound;
1013 }
1014 
1015 bool
IsShader(const WebGLShader * shader)1016 WebGLContext::IsShader(const WebGLShader* shader)
1017 {
1018     if (!ValidateIsObject("isShader", shader))
1019         return false;
1020 
1021     return true;
1022 }
1023 
1024 void
LinkProgram(WebGLProgram & prog)1025 WebGLContext::LinkProgram(WebGLProgram& prog)
1026 {
1027     if (IsContextLost())
1028         return;
1029 
1030     if (!ValidateObject("linkProgram", prog))
1031         return;
1032 
1033     prog.LinkProgram();
1034 
1035     if (!prog.IsLinked()) {
1036         // If we failed to link, but `prog == mCurrentProgram`, we are *not* supposed to
1037         // null out mActiveProgramLinkInfo.
1038         return;
1039     }
1040 
1041     if (&prog == mCurrentProgram) {
1042         mActiveProgramLinkInfo = prog.LinkInfo();
1043 
1044         if (gl->WorkAroundDriverBugs() &&
1045             gl->Vendor() == gl::GLVendor::NVIDIA)
1046         {
1047             gl->fUseProgram(prog.mGLName);
1048         }
1049     }
1050 }
1051 
1052 void
PixelStorei(GLenum pname,GLint param)1053 WebGLContext::PixelStorei(GLenum pname, GLint param)
1054 {
1055     if (IsContextLost())
1056         return;
1057 
1058     if (IsWebGL2()) {
1059         uint32_t* pValueSlot = nullptr;
1060         switch (pname) {
1061         case LOCAL_GL_UNPACK_IMAGE_HEIGHT:
1062             pValueSlot = &mPixelStore_UnpackImageHeight;
1063             break;
1064 
1065         case LOCAL_GL_UNPACK_SKIP_IMAGES:
1066             pValueSlot = &mPixelStore_UnpackSkipImages;
1067             break;
1068 
1069         case LOCAL_GL_UNPACK_ROW_LENGTH:
1070             pValueSlot = &mPixelStore_UnpackRowLength;
1071             break;
1072 
1073         case LOCAL_GL_UNPACK_SKIP_ROWS:
1074             pValueSlot = &mPixelStore_UnpackSkipRows;
1075             break;
1076 
1077         case LOCAL_GL_UNPACK_SKIP_PIXELS:
1078             pValueSlot = &mPixelStore_UnpackSkipPixels;
1079             break;
1080 
1081         case LOCAL_GL_PACK_ROW_LENGTH:
1082             pValueSlot = &mPixelStore_PackRowLength;
1083             break;
1084 
1085         case LOCAL_GL_PACK_SKIP_ROWS:
1086             pValueSlot = &mPixelStore_PackSkipRows;
1087             break;
1088 
1089         case LOCAL_GL_PACK_SKIP_PIXELS:
1090             pValueSlot = &mPixelStore_PackSkipPixels;
1091             break;
1092         }
1093 
1094         if (pValueSlot) {
1095             if (param < 0) {
1096                 ErrorInvalidValue("pixelStorei: param must be >= 0.");
1097                 return;
1098             }
1099 
1100             MakeContextCurrent();
1101             gl->fPixelStorei(pname, param);
1102             *pValueSlot = param;
1103             return;
1104         }
1105     }
1106 
1107     switch (pname) {
1108     case UNPACK_FLIP_Y_WEBGL:
1109         mPixelStore_FlipY = bool(param);
1110         return;
1111 
1112     case UNPACK_PREMULTIPLY_ALPHA_WEBGL:
1113         mPixelStore_PremultiplyAlpha = bool(param);
1114         return;
1115 
1116     case UNPACK_COLORSPACE_CONVERSION_WEBGL:
1117         switch (param) {
1118         case LOCAL_GL_NONE:
1119         case BROWSER_DEFAULT_WEBGL:
1120             mPixelStore_ColorspaceConversion = param;
1121             return;
1122 
1123         default:
1124             ErrorInvalidEnumInfo("pixelStorei: colorspace conversion parameter",
1125                                  param);
1126             return;
1127         }
1128 
1129     case LOCAL_GL_PACK_ALIGNMENT:
1130     case LOCAL_GL_UNPACK_ALIGNMENT:
1131         switch (param) {
1132         case 1:
1133         case 2:
1134         case 4:
1135         case 8:
1136             if (pname == LOCAL_GL_PACK_ALIGNMENT)
1137                 mPixelStore_PackAlignment = param;
1138             else if (pname == LOCAL_GL_UNPACK_ALIGNMENT)
1139                 mPixelStore_UnpackAlignment = param;
1140 
1141             MakeContextCurrent();
1142             gl->fPixelStorei(pname, param);
1143             return;
1144 
1145         default:
1146             ErrorInvalidValue("pixelStorei: invalid pack/unpack alignment value");
1147             return;
1148         }
1149 
1150 
1151 
1152     default:
1153         break;
1154     }
1155 
1156     ErrorInvalidEnumInfo("pixelStorei: parameter", pname);
1157 }
1158 
1159 bool
DoReadPixelsAndConvert(const webgl::FormatInfo * srcFormat,GLint x,GLint y,GLsizei width,GLsizei height,GLenum format,GLenum destType,void * dest,uint32_t destSize,uint32_t rowStride)1160 WebGLContext::DoReadPixelsAndConvert(const webgl::FormatInfo* srcFormat, GLint x, GLint y,
1161                                      GLsizei width, GLsizei height, GLenum format,
1162                                      GLenum destType, void* dest, uint32_t destSize,
1163                                      uint32_t rowStride)
1164 {
1165     // On at least Win+NV, we'll get PBO errors if we don't have at least
1166     // `rowStride * height` bytes available to read into.
1167     const auto naiveBytesNeeded = CheckedUint32(rowStride) * height;
1168     const bool isDangerCloseToEdge = (!naiveBytesNeeded.isValid() ||
1169                                       naiveBytesNeeded.value() > destSize);
1170     const bool useParanoidHandling = (gl->WorkAroundDriverBugs() &&
1171                                       isDangerCloseToEdge &&
1172                                       mBoundPixelPackBuffer);
1173     if (!useParanoidHandling) {
1174         gl->fReadPixels(x, y, width, height, format, destType, dest);
1175         return true;
1176     }
1177 
1178     // Read everything but the last row.
1179     const auto bodyHeight = height - 1;
1180     if (bodyHeight) {
1181         gl->fReadPixels(x, y, width, bodyHeight, format, destType, dest);
1182     }
1183 
1184     // Now read the last row.
1185     gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, 1);
1186     gl->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH, 0);
1187     gl->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, 0);
1188 
1189     const auto tailRowOffset = (char*)dest + rowStride * bodyHeight;
1190     gl->fReadPixels(x, y+bodyHeight, width, 1, format, destType, tailRowOffset);
1191 
1192     gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, mPixelStore_PackAlignment);
1193     gl->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH, mPixelStore_PackRowLength);
1194     gl->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, mPixelStore_PackSkipRows);
1195     return true;
1196 }
1197 
1198 static bool
GetJSScalarFromGLType(GLenum type,js::Scalar::Type * const out_scalarType)1199 GetJSScalarFromGLType(GLenum type, js::Scalar::Type* const out_scalarType)
1200 {
1201     switch (type) {
1202     case LOCAL_GL_BYTE:
1203         *out_scalarType = js::Scalar::Int8;
1204         return true;
1205 
1206     case LOCAL_GL_UNSIGNED_BYTE:
1207         *out_scalarType = js::Scalar::Uint8;
1208         return true;
1209 
1210     case LOCAL_GL_SHORT:
1211         *out_scalarType = js::Scalar::Int16;
1212         return true;
1213 
1214     case LOCAL_GL_HALF_FLOAT:
1215     case LOCAL_GL_HALF_FLOAT_OES:
1216     case LOCAL_GL_UNSIGNED_SHORT:
1217     case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
1218     case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
1219     case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
1220         *out_scalarType = js::Scalar::Uint16;
1221         return true;
1222 
1223     case LOCAL_GL_UNSIGNED_INT:
1224     case LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV:
1225     case LOCAL_GL_UNSIGNED_INT_5_9_9_9_REV:
1226     case LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV:
1227     case LOCAL_GL_UNSIGNED_INT_24_8:
1228         *out_scalarType = js::Scalar::Uint32;
1229         return true;
1230     case LOCAL_GL_INT:
1231         *out_scalarType = js::Scalar::Int32;
1232         return true;
1233 
1234     case LOCAL_GL_FLOAT:
1235         *out_scalarType = js::Scalar::Float32;
1236         return true;
1237 
1238     default:
1239         return false;
1240     }
1241 }
1242 
1243 bool
ReadPixels_SharedPrecheck(ErrorResult * const out_error)1244 WebGLContext::ReadPixels_SharedPrecheck(ErrorResult* const out_error)
1245 {
1246     if (IsContextLost())
1247         return false;
1248 
1249     if (mCanvasElement &&
1250         mCanvasElement->IsWriteOnly() &&
1251         !nsContentUtils::IsCallerChrome())
1252     {
1253         GenerateWarning("readPixels: Not allowed");
1254         out_error->Throw(NS_ERROR_DOM_SECURITY_ERR);
1255         return false;
1256     }
1257 
1258     return true;
1259 }
1260 
1261 bool
ValidatePackSize(const char * funcName,uint32_t width,uint32_t height,uint8_t bytesPerPixel,uint32_t * const out_rowStride,uint32_t * const out_endOffset)1262 WebGLContext::ValidatePackSize(const char* funcName, uint32_t width, uint32_t height,
1263                                uint8_t bytesPerPixel, uint32_t* const out_rowStride,
1264                                uint32_t* const out_endOffset)
1265 {
1266     if (!width || !height) {
1267         *out_rowStride = 0;
1268         *out_endOffset = 0;
1269         return true;
1270     }
1271 
1272     // GLES 3.0.4, p116 (PACK_ functions like UNPACK_)
1273 
1274     const auto rowLength = (mPixelStore_PackRowLength ? mPixelStore_PackRowLength
1275                                                       : width);
1276     const auto skipPixels = mPixelStore_PackSkipPixels;
1277     const auto skipRows = mPixelStore_PackSkipRows;
1278     const auto alignment = mPixelStore_PackAlignment;
1279 
1280     const auto usedPixelsPerRow = CheckedUint32(skipPixels) + width;
1281     const auto usedRowsPerImage = CheckedUint32(skipRows) + height;
1282 
1283     if (!usedPixelsPerRow.isValid() || usedPixelsPerRow.value() > rowLength) {
1284         ErrorInvalidOperation("%s: SKIP_PIXELS + width > ROW_LENGTH.", funcName);
1285         return false;
1286     }
1287 
1288     const auto rowLengthBytes = CheckedUint32(rowLength) * bytesPerPixel;
1289     const auto rowStride = RoundUpToMultipleOf(rowLengthBytes, alignment);
1290 
1291     const auto usedBytesPerRow = usedPixelsPerRow * bytesPerPixel;
1292     const auto usedBytesPerImage = (usedRowsPerImage - 1) * rowStride + usedBytesPerRow;
1293 
1294     if (!rowStride.isValid() || !usedBytesPerImage.isValid()) {
1295         ErrorInvalidOperation("%s: Invalid UNPACK_ params.", funcName);
1296         return false;
1297     }
1298 
1299     *out_rowStride = rowStride.value();
1300     *out_endOffset = usedBytesPerImage.value();
1301     return true;
1302 }
1303 
1304 void
ReadPixels(GLint x,GLint y,GLsizei width,GLsizei height,GLenum format,GLenum type,const dom::ArrayBufferView & dstView,GLuint dstElemOffset,ErrorResult & out_error)1305 WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format,
1306                          GLenum type, const dom::ArrayBufferView& dstView,
1307                          GLuint dstElemOffset, ErrorResult& out_error)
1308 {
1309     const char funcName[] = "readPixels";
1310     if (!ReadPixels_SharedPrecheck(&out_error))
1311         return;
1312 
1313     if (mBoundPixelPackBuffer) {
1314         ErrorInvalidOperation("%s: PIXEL_PACK_BUFFER must be null.", funcName);
1315         return;
1316     }
1317 
1318     ////
1319 
1320     js::Scalar::Type reqScalarType;
1321     if (!GetJSScalarFromGLType(type, &reqScalarType)) {
1322         ErrorInvalidEnum("%s: Bad `type`.", funcName);
1323         return;
1324     }
1325 
1326     const auto& viewElemType = dstView.Type();
1327     if (viewElemType != reqScalarType) {
1328         ErrorInvalidOperation("%s: `pixels` type does not match `type`.", funcName);
1329         return;
1330     }
1331 
1332     ////
1333 
1334     uint8_t* bytes;
1335     size_t byteLen;
1336     if (!ValidateArrayBufferView(funcName, dstView, dstElemOffset, 0, &bytes, &byteLen))
1337         return;
1338 
1339     ////
1340 
1341     ReadPixelsImpl(x, y, width, height, format, type, bytes, byteLen);
1342 }
1343 
1344 void
ReadPixels(GLint x,GLint y,GLsizei width,GLsizei height,GLenum format,GLenum type,WebGLsizeiptr offset,ErrorResult & out_error)1345 WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format,
1346                          GLenum type, WebGLsizeiptr offset, ErrorResult& out_error)
1347 {
1348     const char funcName[] = "readPixels";
1349     if (!ReadPixels_SharedPrecheck(&out_error))
1350         return;
1351 
1352     const auto& buffer = ValidateBufferSelection(funcName, LOCAL_GL_PIXEL_PACK_BUFFER);
1353     if (!buffer)
1354         return;
1355 
1356     //////
1357 
1358     if (!ValidateNonNegative(funcName, "offset", offset))
1359         return;
1360 
1361     {
1362         const auto bytesPerType = webgl::BytesPerPixel({LOCAL_GL_RED, type});
1363 
1364         if (offset % bytesPerType != 0) {
1365             ErrorInvalidOperation("%s: `offset` must be divisible by the size of `type`"
1366                                   " in bytes.",
1367                                   funcName);
1368             return;
1369         }
1370     }
1371 
1372     //////
1373 
1374     const auto bytesAvailable = buffer->ByteLength();
1375     const auto checkedBytesAfterOffset = CheckedUint32(bytesAvailable) - offset;
1376 
1377     uint32_t bytesAfterOffset = 0;
1378     if (checkedBytesAfterOffset.isValid()) {
1379         bytesAfterOffset = checkedBytesAfterOffset.value();
1380     }
1381 
1382     gl->MakeCurrent();
1383     const ScopedLazyBind lazyBind(gl, LOCAL_GL_PIXEL_PACK_BUFFER, buffer);
1384 
1385     ReadPixelsImpl(x, y, width, height, format, type, (void*)offset, bytesAfterOffset);
1386 }
1387 
1388 static webgl::PackingInfo
DefaultReadPixelPI(const webgl::FormatUsageInfo * usage)1389 DefaultReadPixelPI(const webgl::FormatUsageInfo* usage)
1390 {
1391     MOZ_ASSERT(usage->IsRenderable());
1392 
1393     switch (usage->format->componentType) {
1394     case webgl::ComponentType::NormUInt:
1395         return { LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE };
1396 
1397     case webgl::ComponentType::Int:
1398         return { LOCAL_GL_RGBA_INTEGER, LOCAL_GL_INT };
1399 
1400     case webgl::ComponentType::UInt:
1401         return { LOCAL_GL_RGBA_INTEGER, LOCAL_GL_UNSIGNED_INT };
1402 
1403     case webgl::ComponentType::Float:
1404         return { LOCAL_GL_RGBA, LOCAL_GL_FLOAT };
1405 
1406     default:
1407         MOZ_CRASH();
1408     }
1409 }
1410 
1411 static bool
ArePossiblePackEnums(const WebGLContext * webgl,const webgl::PackingInfo & pi)1412 ArePossiblePackEnums(const WebGLContext* webgl, const webgl::PackingInfo& pi)
1413 {
1414     // OpenGL ES 2.0 $4.3.1 - IMPLEMENTATION_COLOR_READ_{TYPE/FORMAT} is a valid
1415     // combination for glReadPixels()...
1416 
1417     // So yeah, we are actually checking that these are valid as /unpack/ formats, instead
1418     // of /pack/ formats here, but it should cover the INVALID_ENUM cases.
1419     if (!webgl->mFormatUsage->AreUnpackEnumsValid(pi.format, pi.type))
1420         return false;
1421 
1422     // Only valid when pulled from:
1423     // * GLES 2.0.25 p105:
1424     //   "table 3.4, excluding formats LUMINANCE and LUMINANCE_ALPHA."
1425     // * GLES 3.0.4 p193:
1426     //   "table 3.2, excluding formats DEPTH_COMPONENT and DEPTH_STENCIL."
1427     switch (pi.format) {
1428     case LOCAL_GL_LUMINANCE:
1429     case LOCAL_GL_LUMINANCE_ALPHA:
1430     case LOCAL_GL_DEPTH_COMPONENT:
1431     case LOCAL_GL_DEPTH_STENCIL:
1432         return false;
1433     }
1434 
1435     if (pi.type == LOCAL_GL_UNSIGNED_INT_24_8)
1436         return false;
1437 
1438     return true;
1439 }
1440 
1441 webgl::PackingInfo
ValidImplementationColorReadPI(const webgl::FormatUsageInfo * usage) const1442 WebGLContext::ValidImplementationColorReadPI(const webgl::FormatUsageInfo* usage) const
1443 {
1444     const auto defaultPI = DefaultReadPixelPI(usage);
1445 
1446     // ES2_compatibility always returns RGBA/UNSIGNED_BYTE, so branch on actual IsGLES().
1447     // Also OSX+NV generates an error here.
1448     if (!gl->IsGLES())
1449         return defaultPI;
1450 
1451     webgl::PackingInfo implPI;
1452     gl->fGetIntegerv(LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT, (GLint*)&implPI.format);
1453     gl->fGetIntegerv(LOCAL_GL_IMPLEMENTATION_COLOR_READ_TYPE, (GLint*)&implPI.type);
1454 
1455     if (!ArePossiblePackEnums(this, implPI))
1456         return defaultPI;
1457 
1458     return implPI;
1459 }
1460 
1461 static bool
ValidateReadPixelsFormatAndType(const webgl::FormatUsageInfo * srcUsage,const webgl::PackingInfo & pi,gl::GLContext * gl,WebGLContext * webgl)1462 ValidateReadPixelsFormatAndType(const webgl::FormatUsageInfo* srcUsage,
1463                                 const webgl::PackingInfo& pi, gl::GLContext* gl,
1464                                 WebGLContext* webgl)
1465 {
1466     const char funcName[] = "readPixels";
1467 
1468     if (!ArePossiblePackEnums(webgl, pi)) {
1469         webgl->ErrorInvalidEnum("%s: Unexpected format or type.", funcName);
1470         return false;
1471     }
1472 
1473     const auto defaultPI = DefaultReadPixelPI(srcUsage);
1474     if (pi == defaultPI)
1475         return true;
1476 
1477     ////
1478 
1479     // OpenGL ES 3.0.4 p194 - When the internal format of the rendering surface is
1480     // RGB10_A2, a third combination of format RGBA and type UNSIGNED_INT_2_10_10_10_REV
1481     // is accepted.
1482 
1483     if (webgl->IsWebGL2() &&
1484         srcUsage->format->effectiveFormat == webgl::EffectiveFormat::RGB10_A2 &&
1485         pi.format == LOCAL_GL_RGBA &&
1486         pi.type == LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV)
1487     {
1488         return true;
1489     }
1490 
1491     ////
1492 
1493     MOZ_ASSERT(gl->IsCurrent());
1494     const auto implPI = webgl->ValidImplementationColorReadPI(srcUsage);
1495     if (pi == implPI)
1496         return true;
1497 
1498     ////
1499 
1500     webgl->ErrorInvalidOperation("%s: Incompatible format or type.", funcName);
1501     return false;
1502 }
1503 
1504 void
ReadPixelsImpl(GLint x,GLint y,GLsizei rawWidth,GLsizei rawHeight,GLenum packFormat,GLenum packType,void * dest,uint32_t dataLen)1505 WebGLContext::ReadPixelsImpl(GLint x, GLint y, GLsizei rawWidth, GLsizei rawHeight,
1506                              GLenum packFormat, GLenum packType, void* dest,
1507                              uint32_t dataLen)
1508 {
1509     if (rawWidth < 0 || rawHeight < 0) {
1510         ErrorInvalidValue("readPixels: negative size passed");
1511         return;
1512     }
1513 
1514     const uint32_t width(rawWidth);
1515     const uint32_t height(rawHeight);
1516 
1517     //////
1518 
1519     MakeContextCurrent();
1520 
1521     const webgl::FormatUsageInfo* srcFormat;
1522     uint32_t srcWidth;
1523     uint32_t srcHeight;
1524     if (!ValidateCurFBForRead("readPixels", &srcFormat, &srcWidth, &srcHeight))
1525         return;
1526 
1527     //////
1528 
1529     const webgl::PackingInfo pi = {packFormat, packType};
1530     if (!ValidateReadPixelsFormatAndType(srcFormat, pi, gl, this))
1531         return;
1532 
1533     uint8_t bytesPerPixel;
1534     if (!webgl::GetBytesPerPixel(pi, &bytesPerPixel)) {
1535         ErrorInvalidOperation("readPixels: Unsupported format and type.");
1536         return;
1537     }
1538 
1539     //////
1540 
1541     uint32_t rowStride;
1542     uint32_t bytesNeeded;
1543     if (!ValidatePackSize("readPixels", width, height, bytesPerPixel, &rowStride,
1544                           &bytesNeeded))
1545     {
1546         return;
1547     }
1548 
1549     if (bytesNeeded > dataLen) {
1550         ErrorInvalidOperation("readPixels: buffer too small");
1551         return;
1552     }
1553 
1554     ////
1555 
1556     int32_t readX, readY;
1557     int32_t writeX, writeY;
1558     int32_t rwWidth, rwHeight;
1559     if (!Intersect(srcWidth, x, width, &readX, &writeX, &rwWidth) ||
1560         !Intersect(srcHeight, y, height, &readY, &writeY, &rwHeight))
1561     {
1562         ErrorOutOfMemory("readPixels: Bad subrect selection.");
1563         return;
1564     }
1565 
1566     ////////////////
1567     // Now that the errors are out of the way, on to actually reading!
1568 
1569     OnBeforeReadCall();
1570 
1571     if (!rwWidth || !rwHeight) {
1572         // Disjoint rects, so we're done already.
1573         DummyReadFramebufferOperation("readPixels");
1574         return;
1575     }
1576 
1577     if (uint32_t(rwWidth) == width &&
1578         uint32_t(rwHeight) == height)
1579     {
1580         DoReadPixelsAndConvert(srcFormat->format, x, y, width, height, packFormat,
1581                                packType, dest, dataLen, rowStride);
1582         return;
1583     }
1584 
1585     // Read request contains out-of-bounds pixels. Unfortunately:
1586     // GLES 3.0.4 p194 "Obtaining Pixels from the Framebuffer":
1587     // "If any of these pixels lies outside of the window allocated to the current GL
1588     //  context, or outside of the image attached to the currently bound framebuffer
1589     //  object, then the values obtained for those pixels are undefined."
1590 
1591     // This is a slow-path, so warn people away!
1592     GenerateWarning("readPixels: Out-of-bounds reads with readPixels are deprecated, and"
1593                     " may be slow.");
1594 
1595     ////////////////////////////////////
1596     // Read only the in-bounds pixels.
1597 
1598     if (IsWebGL2()) {
1599         if (!mPixelStore_PackRowLength) {
1600             gl->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH,
1601                              mPixelStore_PackSkipPixels + width);
1602         }
1603         gl->fPixelStorei(LOCAL_GL_PACK_SKIP_PIXELS, mPixelStore_PackSkipPixels + writeX);
1604         gl->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, mPixelStore_PackSkipRows + writeY);
1605 
1606         DoReadPixelsAndConvert(srcFormat->format, readX, readY, rwWidth, rwHeight,
1607                                packFormat, packType, dest, dataLen, rowStride);
1608 
1609         gl->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH, mPixelStore_PackRowLength);
1610         gl->fPixelStorei(LOCAL_GL_PACK_SKIP_PIXELS, mPixelStore_PackSkipPixels);
1611         gl->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, mPixelStore_PackSkipRows);
1612     } else {
1613         // I *did* say "hilariously slow".
1614 
1615         uint8_t* row = (uint8_t*)dest + writeX * bytesPerPixel;
1616         row += writeY * rowStride;
1617         for (uint32_t j = 0; j < uint32_t(rwHeight); j++) {
1618             DoReadPixelsAndConvert(srcFormat->format, readX, readY+j, rwWidth, 1,
1619                                    packFormat, packType, row, dataLen, rowStride);
1620             row += rowStride;
1621         }
1622     }
1623 }
1624 
1625 void
RenderbufferStorage_base(const char * funcName,GLenum target,GLsizei samples,GLenum internalFormat,GLsizei width,GLsizei height)1626 WebGLContext::RenderbufferStorage_base(const char* funcName, GLenum target,
1627                                        GLsizei samples, GLenum internalFormat,
1628                                        GLsizei width, GLsizei height)
1629 {
1630     if (IsContextLost())
1631         return;
1632 
1633     if (target != LOCAL_GL_RENDERBUFFER) {
1634         ErrorInvalidEnumInfo("`target`", funcName, target);
1635         return;
1636     }
1637 
1638     if (!mBoundRenderbuffer) {
1639         ErrorInvalidOperation("%s: Called on renderbuffer 0.", funcName);
1640         return;
1641     }
1642 
1643     if (samples < 0) {
1644         ErrorInvalidValue("%s: `samples` must be >= 0.", funcName);
1645         return;
1646     }
1647 
1648     if (width < 0 || height < 0) {
1649         ErrorInvalidValue("%s: `width` and `height` must be >= 0.", funcName);
1650         return;
1651     }
1652 
1653     mBoundRenderbuffer->RenderbufferStorage(funcName, uint32_t(samples), internalFormat,
1654                                             uint32_t(width), uint32_t(height));
1655 }
1656 
1657 void
RenderbufferStorage(GLenum target,GLenum internalFormat,GLsizei width,GLsizei height)1658 WebGLContext::RenderbufferStorage(GLenum target, GLenum internalFormat, GLsizei width, GLsizei height)
1659 {
1660     RenderbufferStorage_base("renderbufferStorage", target, 0, internalFormat, width,
1661                              height);
1662 }
1663 
1664 void
Scissor(GLint x,GLint y,GLsizei width,GLsizei height)1665 WebGLContext::Scissor(GLint x, GLint y, GLsizei width, GLsizei height)
1666 {
1667     if (IsContextLost())
1668         return;
1669 
1670     if (width < 0 || height < 0)
1671         return ErrorInvalidValue("scissor: negative size");
1672 
1673     MakeContextCurrent();
1674     gl->fScissor(x, y, width, height);
1675 }
1676 
1677 void
StencilFunc(GLenum func,GLint ref,GLuint mask)1678 WebGLContext::StencilFunc(GLenum func, GLint ref, GLuint mask)
1679 {
1680     if (IsContextLost())
1681         return;
1682 
1683     if (!ValidateComparisonEnum(func, "stencilFunc: func"))
1684         return;
1685 
1686     mStencilRefFront = ref;
1687     mStencilRefBack = ref;
1688     mStencilValueMaskFront = mask;
1689     mStencilValueMaskBack = mask;
1690 
1691     MakeContextCurrent();
1692     gl->fStencilFunc(func, ref, mask);
1693 }
1694 
1695 void
StencilFuncSeparate(GLenum face,GLenum func,GLint ref,GLuint mask)1696 WebGLContext::StencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask)
1697 {
1698     if (IsContextLost())
1699         return;
1700 
1701     if (!ValidateFaceEnum(face, "stencilFuncSeparate: face") ||
1702         !ValidateComparisonEnum(func, "stencilFuncSeparate: func"))
1703         return;
1704 
1705     switch (face) {
1706         case LOCAL_GL_FRONT_AND_BACK:
1707             mStencilRefFront = ref;
1708             mStencilRefBack = ref;
1709             mStencilValueMaskFront = mask;
1710             mStencilValueMaskBack = mask;
1711             break;
1712         case LOCAL_GL_FRONT:
1713             mStencilRefFront = ref;
1714             mStencilValueMaskFront = mask;
1715             break;
1716         case LOCAL_GL_BACK:
1717             mStencilRefBack = ref;
1718             mStencilValueMaskBack = mask;
1719             break;
1720     }
1721 
1722     MakeContextCurrent();
1723     gl->fStencilFuncSeparate(face, func, ref, mask);
1724 }
1725 
1726 void
StencilOp(GLenum sfail,GLenum dpfail,GLenum dppass)1727 WebGLContext::StencilOp(GLenum sfail, GLenum dpfail, GLenum dppass)
1728 {
1729     if (IsContextLost())
1730         return;
1731 
1732     if (!ValidateStencilOpEnum(sfail, "stencilOp: sfail") ||
1733         !ValidateStencilOpEnum(dpfail, "stencilOp: dpfail") ||
1734         !ValidateStencilOpEnum(dppass, "stencilOp: dppass"))
1735         return;
1736 
1737     MakeContextCurrent();
1738     gl->fStencilOp(sfail, dpfail, dppass);
1739 }
1740 
1741 void
StencilOpSeparate(GLenum face,GLenum sfail,GLenum dpfail,GLenum dppass)1742 WebGLContext::StencilOpSeparate(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass)
1743 {
1744     if (IsContextLost())
1745         return;
1746 
1747     if (!ValidateFaceEnum(face, "stencilOpSeparate: face") ||
1748         !ValidateStencilOpEnum(sfail, "stencilOpSeparate: sfail") ||
1749         !ValidateStencilOpEnum(dpfail, "stencilOpSeparate: dpfail") ||
1750         !ValidateStencilOpEnum(dppass, "stencilOpSeparate: dppass"))
1751         return;
1752 
1753     MakeContextCurrent();
1754     gl->fStencilOpSeparate(face, sfail, dpfail, dppass);
1755 }
1756 
1757 ////////////////////////////////////////////////////////////////////////////////
1758 // Uniform setters.
1759 
1760 class ValidateIfSampler
1761 {
1762     const WebGLUniformLocation* const mLoc;
1763     const size_t mDataCount;
1764     const GLint* const mData;
1765     bool mIsValidatedSampler;
1766 
1767 public:
ValidateIfSampler(WebGLContext * webgl,const char * funcName,WebGLUniformLocation * loc,size_t dataCount,const GLint * data,bool * const out_error)1768     ValidateIfSampler(WebGLContext* webgl, const char* funcName,
1769                       WebGLUniformLocation* loc, size_t dataCount, const GLint* data,
1770                       bool* const out_error)
1771         : mLoc(loc)
1772         , mDataCount(dataCount)
1773         , mData(data)
1774         , mIsValidatedSampler(false)
1775     {
1776         if (!mLoc->mInfo->mSamplerTexList) {
1777             *out_error = false;
1778             return;
1779         }
1780 
1781         for (size_t i = 0; i < mDataCount; i++) {
1782             const auto& val = mData[i];
1783             if (val < 0 || uint32_t(val) >= webgl->GLMaxTextureUnits()) {
1784                 webgl->ErrorInvalidValue("%s: This uniform location is a sampler, but %d"
1785                                          " is not a valid texture unit.",
1786                                          funcName, val);
1787                 *out_error = true;
1788                 return;
1789             }
1790         }
1791 
1792         mIsValidatedSampler = true;
1793         *out_error = false;
1794     }
1795 
~ValidateIfSampler()1796     ~ValidateIfSampler() {
1797         if (!mIsValidatedSampler)
1798             return;
1799 
1800         auto& samplerValues = mLoc->mInfo->mSamplerValues;
1801 
1802         for (size_t i = 0; i < mDataCount; i++) {
1803             const size_t curIndex = mLoc->mArrayIndex + i;
1804             if (curIndex >= samplerValues.size())
1805                 break;
1806 
1807             samplerValues[curIndex] = mData[i];
1808         }
1809     }
1810 };
1811 
1812 ////////////////////
1813 
1814 void
Uniform1i(WebGLUniformLocation * loc,GLint a1)1815 WebGLContext::Uniform1i(WebGLUniformLocation* loc, GLint a1)
1816 {
1817     const char funcName[] = "uniform1i";
1818     if (!ValidateUniformSetter(loc, 1, LOCAL_GL_INT, funcName))
1819         return;
1820 
1821     bool error;
1822     const ValidateIfSampler validate(this, funcName, loc, 1, &a1, &error);
1823     if (error)
1824         return;
1825 
1826     MakeContextCurrent();
1827     gl->fUniform1i(loc->mLoc, a1);
1828 }
1829 
1830 void
Uniform2i(WebGLUniformLocation * loc,GLint a1,GLint a2)1831 WebGLContext::Uniform2i(WebGLUniformLocation* loc, GLint a1, GLint a2)
1832 {
1833     const char funcName[] = "uniform2i";
1834     if (!ValidateUniformSetter(loc, 2, LOCAL_GL_INT, funcName))
1835         return;
1836 
1837     MakeContextCurrent();
1838     gl->fUniform2i(loc->mLoc, a1, a2);
1839 }
1840 
1841 void
Uniform3i(WebGLUniformLocation * loc,GLint a1,GLint a2,GLint a3)1842 WebGLContext::Uniform3i(WebGLUniformLocation* loc, GLint a1, GLint a2, GLint a3)
1843 {
1844     const char funcName[] = "uniform3i";
1845     if (!ValidateUniformSetter(loc, 3, LOCAL_GL_INT, funcName))
1846         return;
1847 
1848     MakeContextCurrent();
1849     gl->fUniform3i(loc->mLoc, a1, a2, a3);
1850 }
1851 
1852 void
Uniform4i(WebGLUniformLocation * loc,GLint a1,GLint a2,GLint a3,GLint a4)1853 WebGLContext::Uniform4i(WebGLUniformLocation* loc, GLint a1, GLint a2, GLint a3,
1854                         GLint a4)
1855 {
1856     const char funcName[] = "uniform4i";
1857     if (!ValidateUniformSetter(loc, 4, LOCAL_GL_INT, funcName))
1858         return;
1859 
1860     MakeContextCurrent();
1861     gl->fUniform4i(loc->mLoc, a1, a2, a3, a4);
1862 }
1863 
1864 //////////
1865 
1866 void
Uniform1f(WebGLUniformLocation * loc,GLfloat a1)1867 WebGLContext::Uniform1f(WebGLUniformLocation* loc, GLfloat a1)
1868 {
1869     const char funcName[] = "uniform1f";
1870     if (!ValidateUniformSetter(loc, 1, LOCAL_GL_FLOAT, funcName))
1871         return;
1872 
1873     MakeContextCurrent();
1874     gl->fUniform1f(loc->mLoc, a1);
1875 }
1876 
1877 void
Uniform2f(WebGLUniformLocation * loc,GLfloat a1,GLfloat a2)1878 WebGLContext::Uniform2f(WebGLUniformLocation* loc, GLfloat a1, GLfloat a2)
1879 {
1880     const char funcName[] = "uniform2f";
1881     if (!ValidateUniformSetter(loc, 2, LOCAL_GL_FLOAT, funcName))
1882         return;
1883 
1884     MakeContextCurrent();
1885     gl->fUniform2f(loc->mLoc, a1, a2);
1886 }
1887 
1888 void
Uniform3f(WebGLUniformLocation * loc,GLfloat a1,GLfloat a2,GLfloat a3)1889 WebGLContext::Uniform3f(WebGLUniformLocation* loc, GLfloat a1, GLfloat a2,
1890                         GLfloat a3)
1891 {
1892     const char funcName[] = "uniform3f";
1893     if (!ValidateUniformSetter(loc, 3, LOCAL_GL_FLOAT, funcName))
1894         return;
1895 
1896     MakeContextCurrent();
1897     gl->fUniform3f(loc->mLoc, a1, a2, a3);
1898 }
1899 
1900 void
Uniform4f(WebGLUniformLocation * loc,GLfloat a1,GLfloat a2,GLfloat a3,GLfloat a4)1901 WebGLContext::Uniform4f(WebGLUniformLocation* loc, GLfloat a1, GLfloat a2,
1902                         GLfloat a3, GLfloat a4)
1903 {
1904     const char funcName[] = "uniform4f";
1905     if (!ValidateUniformSetter(loc, 4, LOCAL_GL_FLOAT, funcName))
1906         return;
1907 
1908     MakeContextCurrent();
1909     gl->fUniform4f(loc->mLoc, a1, a2, a3, a4);
1910 }
1911 
1912 ////////////////////////////////////////
1913 // Array
1914 
1915 static bool
ValidateArrOffsetAndCount(WebGLContext * webgl,const char * funcName,size_t elemsAvail,GLuint elemOffset,GLuint elemCountOverride,size_t * const out_elemCount)1916 ValidateArrOffsetAndCount(WebGLContext* webgl, const char* funcName, size_t elemsAvail,
1917                           GLuint elemOffset, GLuint elemCountOverride,
1918                           size_t* const out_elemCount)
1919 {
1920     if (elemOffset > elemsAvail) {
1921         webgl->ErrorInvalidValue("%s: Bad offset into list.", funcName);
1922         return false;
1923     }
1924     elemsAvail -= elemOffset;
1925 
1926     if (elemCountOverride) {
1927         if (elemCountOverride > elemsAvail) {
1928             webgl->ErrorInvalidValue("%s: Bad count override for sub-list.", funcName);
1929             return false;
1930         }
1931         elemsAvail = elemCountOverride;
1932     }
1933 
1934     *out_elemCount = elemsAvail;
1935     return true;
1936 }
1937 
1938 void
UniformNiv(const char * funcName,uint8_t N,WebGLUniformLocation * loc,const Int32Arr & arr,GLuint elemOffset,GLuint elemCountOverride)1939 WebGLContext::UniformNiv(const char* funcName, uint8_t N, WebGLUniformLocation* loc,
1940                          const Int32Arr& arr, GLuint elemOffset, GLuint elemCountOverride)
1941 {
1942     size_t elemCount;
1943     if (!ValidateArrOffsetAndCount(this, funcName, arr.elemCount, elemOffset,
1944                                    elemCountOverride, &elemCount))
1945     {
1946         return;
1947     }
1948     const auto elemBytes = arr.elemBytes + elemOffset;
1949 
1950     uint32_t numElementsToUpload;
1951     if (!ValidateUniformArraySetter(loc, N, LOCAL_GL_INT, elemCount, funcName,
1952                                     &numElementsToUpload))
1953     {
1954         return;
1955     }
1956 
1957     bool error;
1958     const ValidateIfSampler samplerValidator(this, funcName, loc, numElementsToUpload,
1959                                              elemBytes, &error);
1960     if (error)
1961         return;
1962 
1963     static const decltype(&gl::GLContext::fUniform1iv) kFuncList[] = {
1964         &gl::GLContext::fUniform1iv,
1965         &gl::GLContext::fUniform2iv,
1966         &gl::GLContext::fUniform3iv,
1967         &gl::GLContext::fUniform4iv
1968     };
1969     const auto func = kFuncList[N-1];
1970 
1971     MakeContextCurrent();
1972     (gl->*func)(loc->mLoc, numElementsToUpload, elemBytes);
1973 }
1974 
1975 void
UniformNuiv(const char * funcName,uint8_t N,WebGLUniformLocation * loc,const Uint32Arr & arr,GLuint elemOffset,GLuint elemCountOverride)1976 WebGLContext::UniformNuiv(const char* funcName, uint8_t N, WebGLUniformLocation* loc,
1977                           const Uint32Arr& arr, GLuint elemOffset,
1978                           GLuint elemCountOverride)
1979 {
1980     size_t elemCount;
1981     if (!ValidateArrOffsetAndCount(this, funcName, arr.elemCount, elemOffset,
1982                                    elemCountOverride, &elemCount))
1983     {
1984         return;
1985     }
1986     const auto elemBytes = arr.elemBytes + elemOffset;
1987 
1988     uint32_t numElementsToUpload;
1989     if (!ValidateUniformArraySetter(loc, N, LOCAL_GL_UNSIGNED_INT, elemCount, funcName,
1990                                     &numElementsToUpload))
1991     {
1992         return;
1993     }
1994     MOZ_ASSERT(!loc->mInfo->mSamplerTexList, "Should not be a sampler.");
1995 
1996     static const decltype(&gl::GLContext::fUniform1uiv) kFuncList[] = {
1997         &gl::GLContext::fUniform1uiv,
1998         &gl::GLContext::fUniform2uiv,
1999         &gl::GLContext::fUniform3uiv,
2000         &gl::GLContext::fUniform4uiv
2001     };
2002     const auto func = kFuncList[N-1];
2003 
2004     MakeContextCurrent();
2005     (gl->*func)(loc->mLoc, numElementsToUpload, elemBytes);
2006 }
2007 
2008 void
UniformNfv(const char * funcName,uint8_t N,WebGLUniformLocation * loc,const Float32Arr & arr,GLuint elemOffset,GLuint elemCountOverride)2009 WebGLContext::UniformNfv(const char* funcName, uint8_t N, WebGLUniformLocation* loc,
2010                          const Float32Arr& arr, GLuint elemOffset,
2011                          GLuint elemCountOverride)
2012 {
2013     size_t elemCount;
2014     if (!ValidateArrOffsetAndCount(this, funcName, arr.elemCount, elemOffset,
2015                                    elemCountOverride, &elemCount))
2016     {
2017         return;
2018     }
2019     const auto elemBytes = arr.elemBytes + elemOffset;
2020 
2021     uint32_t numElementsToUpload;
2022     if (!ValidateUniformArraySetter(loc, N, LOCAL_GL_FLOAT, elemCount, funcName,
2023                                     &numElementsToUpload))
2024     {
2025         return;
2026     }
2027     MOZ_ASSERT(!loc->mInfo->mSamplerTexList, "Should not be a sampler.");
2028 
2029     static const decltype(&gl::GLContext::fUniform1fv) kFuncList[] = {
2030         &gl::GLContext::fUniform1fv,
2031         &gl::GLContext::fUniform2fv,
2032         &gl::GLContext::fUniform3fv,
2033         &gl::GLContext::fUniform4fv
2034     };
2035     const auto func = kFuncList[N-1];
2036 
2037     MakeContextCurrent();
2038     (gl->*func)(loc->mLoc, numElementsToUpload, elemBytes);
2039 }
2040 
2041 void
UniformMatrixAxBfv(const char * funcName,uint8_t A,uint8_t B,WebGLUniformLocation * loc,bool transpose,const Float32Arr & arr,GLuint elemOffset,GLuint elemCountOverride)2042 WebGLContext::UniformMatrixAxBfv(const char* funcName, uint8_t A, uint8_t B,
2043                                  WebGLUniformLocation* loc, bool transpose,
2044                                  const Float32Arr& arr, GLuint elemOffset,
2045                                  GLuint elemCountOverride)
2046 {
2047     size_t elemCount;
2048     if (!ValidateArrOffsetAndCount(this, funcName, arr.elemCount, elemOffset,
2049                                    elemCountOverride, &elemCount))
2050     {
2051         return;
2052     }
2053     const auto elemBytes = arr.elemBytes + elemOffset;
2054 
2055     uint32_t numElementsToUpload;
2056     if (!ValidateUniformMatrixArraySetter(loc, A, B, LOCAL_GL_FLOAT, elemCount,
2057                                           transpose, funcName, &numElementsToUpload))
2058     {
2059         return;
2060     }
2061     MOZ_ASSERT(!loc->mInfo->mSamplerTexList, "Should not be a sampler.");
2062 
2063     static const decltype(&gl::GLContext::fUniformMatrix2fv) kFuncList[] = {
2064         &gl::GLContext::fUniformMatrix2fv,
2065         &gl::GLContext::fUniformMatrix2x3fv,
2066         &gl::GLContext::fUniformMatrix2x4fv,
2067 
2068         &gl::GLContext::fUniformMatrix3x2fv,
2069         &gl::GLContext::fUniformMatrix3fv,
2070         &gl::GLContext::fUniformMatrix3x4fv,
2071 
2072         &gl::GLContext::fUniformMatrix4x2fv,
2073         &gl::GLContext::fUniformMatrix4x3fv,
2074         &gl::GLContext::fUniformMatrix4fv
2075     };
2076     const auto func = kFuncList[3*(A-2) + (B-2)];
2077 
2078     MakeContextCurrent();
2079     (gl->*func)(loc->mLoc, numElementsToUpload, transpose, elemBytes);
2080 }
2081 
2082 ////////////////////////////////////////////////////////////////////////////////
2083 
2084 void
UseProgram(WebGLProgram * prog)2085 WebGLContext::UseProgram(WebGLProgram* prog)
2086 {
2087     if (IsContextLost())
2088         return;
2089 
2090     if (!prog) {
2091         mCurrentProgram = nullptr;
2092         mActiveProgramLinkInfo = nullptr;
2093         return;
2094     }
2095 
2096     if (!ValidateObject("useProgram", *prog))
2097         return;
2098 
2099     if (prog->UseProgram()) {
2100         mCurrentProgram = prog;
2101         mActiveProgramLinkInfo = mCurrentProgram->LinkInfo();
2102     }
2103 }
2104 
2105 void
ValidateProgram(const WebGLProgram & prog)2106 WebGLContext::ValidateProgram(const WebGLProgram& prog)
2107 {
2108     if (IsContextLost())
2109         return;
2110 
2111     if (!ValidateObject("validateProgram", prog))
2112         return;
2113 
2114     prog.ValidateProgram();
2115 }
2116 
2117 already_AddRefed<WebGLFramebuffer>
CreateFramebuffer()2118 WebGLContext::CreateFramebuffer()
2119 {
2120     if (IsContextLost())
2121         return nullptr;
2122 
2123     GLuint fbo = 0;
2124     MakeContextCurrent();
2125     gl->fGenFramebuffers(1, &fbo);
2126 
2127     RefPtr<WebGLFramebuffer> globj = new WebGLFramebuffer(this, fbo);
2128     return globj.forget();
2129 }
2130 
2131 already_AddRefed<WebGLRenderbuffer>
CreateRenderbuffer()2132 WebGLContext::CreateRenderbuffer()
2133 {
2134     if (IsContextLost())
2135         return nullptr;
2136 
2137     MakeContextCurrent();
2138     RefPtr<WebGLRenderbuffer> globj = new WebGLRenderbuffer(this);
2139     return globj.forget();
2140 }
2141 
2142 void
Viewport(GLint x,GLint y,GLsizei width,GLsizei height)2143 WebGLContext::Viewport(GLint x, GLint y, GLsizei width, GLsizei height)
2144 {
2145     if (IsContextLost())
2146         return;
2147 
2148     if (width < 0 || height < 0)
2149         return ErrorInvalidValue("viewport: negative size");
2150 
2151     width = std::min(width, (GLsizei)mImplMaxViewportDims[0]);
2152     height = std::min(height, (GLsizei)mImplMaxViewportDims[1]);
2153 
2154     MakeContextCurrent();
2155     gl->fViewport(x, y, width, height);
2156 
2157     mViewportX = x;
2158     mViewportY = y;
2159     mViewportWidth = width;
2160     mViewportHeight = height;
2161 }
2162 
2163 void
CompileShader(WebGLShader & shader)2164 WebGLContext::CompileShader(WebGLShader& shader)
2165 {
2166     if (IsContextLost())
2167         return;
2168 
2169     if (!ValidateObject("compileShader", shader))
2170         return;
2171 
2172     shader.CompileShader();
2173 }
2174 
2175 JS::Value
GetShaderParameter(const WebGLShader & shader,GLenum pname)2176 WebGLContext::GetShaderParameter(const WebGLShader& shader, GLenum pname)
2177 {
2178     if (IsContextLost())
2179         return JS::NullValue();
2180 
2181     if (!ValidateObjectAllowDeleted("getShaderParameter: shader", shader))
2182         return JS::NullValue();
2183 
2184     return shader.GetShaderParameter(pname);
2185 }
2186 
2187 void
GetShaderInfoLog(const WebGLShader & shader,nsAString & retval)2188 WebGLContext::GetShaderInfoLog(const WebGLShader& shader, nsAString& retval)
2189 {
2190     retval.SetIsVoid(true);
2191 
2192     if (IsContextLost())
2193         return;
2194 
2195     if (!ValidateObject("getShaderInfoLog: shader", shader))
2196         return;
2197 
2198     shader.GetShaderInfoLog(&retval);
2199 }
2200 
2201 already_AddRefed<WebGLShaderPrecisionFormat>
GetShaderPrecisionFormat(GLenum shadertype,GLenum precisiontype)2202 WebGLContext::GetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype)
2203 {
2204     if (IsContextLost())
2205         return nullptr;
2206 
2207     switch (shadertype) {
2208         case LOCAL_GL_FRAGMENT_SHADER:
2209         case LOCAL_GL_VERTEX_SHADER:
2210             break;
2211         default:
2212             ErrorInvalidEnumInfo("getShaderPrecisionFormat: shadertype", shadertype);
2213             return nullptr;
2214     }
2215 
2216     switch (precisiontype) {
2217         case LOCAL_GL_LOW_FLOAT:
2218         case LOCAL_GL_MEDIUM_FLOAT:
2219         case LOCAL_GL_HIGH_FLOAT:
2220         case LOCAL_GL_LOW_INT:
2221         case LOCAL_GL_MEDIUM_INT:
2222         case LOCAL_GL_HIGH_INT:
2223             break;
2224         default:
2225             ErrorInvalidEnumInfo("getShaderPrecisionFormat: precisiontype", precisiontype);
2226             return nullptr;
2227     }
2228 
2229     MakeContextCurrent();
2230     GLint range[2], precision;
2231 
2232     if (mDisableFragHighP &&
2233         shadertype == LOCAL_GL_FRAGMENT_SHADER &&
2234         (precisiontype == LOCAL_GL_HIGH_FLOAT ||
2235          precisiontype == LOCAL_GL_HIGH_INT))
2236     {
2237       precision = 0;
2238       range[0] = 0;
2239       range[1] = 0;
2240     } else {
2241       gl->fGetShaderPrecisionFormat(shadertype, precisiontype, range, &precision);
2242     }
2243 
2244     RefPtr<WebGLShaderPrecisionFormat> retShaderPrecisionFormat
2245         = new WebGLShaderPrecisionFormat(this, range[0], range[1], precision);
2246     return retShaderPrecisionFormat.forget();
2247 }
2248 
2249 void
GetShaderSource(const WebGLShader & shader,nsAString & retval)2250 WebGLContext::GetShaderSource(const WebGLShader& shader, nsAString& retval)
2251 {
2252     retval.SetIsVoid(true);
2253 
2254     if (IsContextLost())
2255         return;
2256 
2257     if (!ValidateObject("getShaderSource: shader", shader))
2258         return;
2259 
2260     shader.GetShaderSource(&retval);
2261 }
2262 
2263 void
ShaderSource(WebGLShader & shader,const nsAString & source)2264 WebGLContext::ShaderSource(WebGLShader& shader, const nsAString& source)
2265 {
2266     if (IsContextLost())
2267         return;
2268 
2269     if (!ValidateObject("shaderSource: shader", shader))
2270         return;
2271 
2272     shader.ShaderSource(source);
2273 }
2274 
2275 void
LoseContext()2276 WebGLContext::LoseContext()
2277 {
2278     if (IsContextLost())
2279         return ErrorInvalidOperation("loseContext: Context is already lost.");
2280 
2281     ForceLoseContext(true);
2282 }
2283 
2284 void
RestoreContext()2285 WebGLContext::RestoreContext()
2286 {
2287     if (!IsContextLost())
2288         return ErrorInvalidOperation("restoreContext: Context is not lost.");
2289 
2290     if (!mLastLossWasSimulated) {
2291         return ErrorInvalidOperation("restoreContext: Context loss was not simulated."
2292                                      " Cannot simulate restore.");
2293     }
2294     // If we're currently lost, and the last loss was simulated, then
2295     // we're currently only simulated-lost, allowing us to call
2296     // restoreContext().
2297 
2298     if (!mAllowContextRestore)
2299         return ErrorInvalidOperation("restoreContext: Context cannot be restored.");
2300 
2301     ForceRestoreContext();
2302 }
2303 
2304 void
BlendColor(GLfloat r,GLfloat g,GLfloat b,GLfloat a)2305 WebGLContext::BlendColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) {
2306     if (IsContextLost())
2307         return;
2308     MakeContextCurrent();
2309     gl->fBlendColor(r, g, b, a);
2310 }
2311 
2312 void
Flush()2313 WebGLContext::Flush() {
2314     if (IsContextLost())
2315         return;
2316     MakeContextCurrent();
2317     gl->fFlush();
2318 }
2319 
2320 void
Finish()2321 WebGLContext::Finish() {
2322     if (IsContextLost())
2323         return;
2324     MakeContextCurrent();
2325     gl->fFinish();
2326 }
2327 
2328 void
LineWidth(GLfloat width)2329 WebGLContext::LineWidth(GLfloat width)
2330 {
2331     if (IsContextLost())
2332         return;
2333 
2334     // Doing it this way instead of `if (width <= 0.0)` handles NaNs.
2335     const bool isValid = width > 0.0;
2336     if (!isValid) {
2337         ErrorInvalidValue("lineWidth: `width` must be positive and non-zero.");
2338         return;
2339     }
2340 
2341     mLineWidth = width;
2342 
2343     if (gl->IsCoreProfile() && width > 1.0) {
2344         width = 1.0;
2345     }
2346 
2347     MakeContextCurrent();
2348     gl->fLineWidth(width);
2349 }
2350 
2351 void
PolygonOffset(GLfloat factor,GLfloat units)2352 WebGLContext::PolygonOffset(GLfloat factor, GLfloat units) {
2353     if (IsContextLost())
2354         return;
2355     MakeContextCurrent();
2356     gl->fPolygonOffset(factor, units);
2357 }
2358 
2359 void
SampleCoverage(GLclampf value,WebGLboolean invert)2360 WebGLContext::SampleCoverage(GLclampf value, WebGLboolean invert) {
2361     if (IsContextLost())
2362         return;
2363     MakeContextCurrent();
2364     gl->fSampleCoverage(value, invert);
2365 }
2366 
2367 } // namespace mozilla
2368