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 "ClientWebGLContext.h"
7 
8 #include <bitset>
9 
10 #include "ClientWebGLExtensions.h"
11 #include "Layers.h"
12 #include "gfxCrashReporterUtils.h"
13 #include "HostWebGLContext.h"
14 #include "js/PropertyAndElement.h"  // JS_DefineElement
15 #include "js/ScalarType.h"          // js::Scalar::Type
16 #include "mozilla/dom/Document.h"
17 #include "mozilla/dom/ToJSValue.h"
18 #include "mozilla/dom/WebGLContextEvent.h"
19 #include "mozilla/dom/WorkerCommon.h"
20 #include "mozilla/EnumeratedRange.h"
21 #include "mozilla/gfx/gfxVars.h"
22 #include "mozilla/gfx/CanvasManagerChild.h"
23 #include "mozilla/ipc/Shmem.h"
24 #include "mozilla/gfx/Swizzle.h"
25 #include "mozilla/layers/CompositorBridgeChild.h"
26 #include "mozilla/layers/ImageBridgeChild.h"
27 #include "mozilla/layers/OOPCanvasRenderer.h"
28 #include "mozilla/layers/TextureClientSharedSurface.h"
29 #include "mozilla/layers/WebRenderUserData.h"
30 #include "mozilla/layers/WebRenderCanvasRenderer.h"
31 #include "mozilla/Preferences.h"
32 #include "mozilla/ResultVariant.h"
33 #include "mozilla/ScopeExit.h"
34 #include "mozilla/StaticPrefs_webgl.h"
35 #include "nsContentUtils.h"
36 #include "nsDisplayList.h"
37 #include "TexUnpackBlob.h"
38 #include "WebGLMethodDispatcher.h"
39 #include "WebGLChild.h"
40 #include "WebGLValidateStrings.h"
41 
42 namespace mozilla {
43 
44 namespace webgl {
45 std::string SanitizeRenderer(const std::string&);
46 }  // namespace webgl
47 
48 // -
49 
NotLostData(ClientWebGLContext & _context)50 webgl::NotLostData::NotLostData(ClientWebGLContext& _context)
51     : context(_context) {}
52 
~NotLostData()53 webgl::NotLostData::~NotLostData() {
54   if (outOfProcess) {
55     Unused << dom::WebGLChild::Send__delete__(outOfProcess.get());
56   }
57 }
58 
59 // -
60 
ValidateForContext(const ClientWebGLContext & targetContext,const char * const argName) const61 bool webgl::ObjectJS::ValidateForContext(
62     const ClientWebGLContext& targetContext, const char* const argName) const {
63   if (!IsForContext(targetContext)) {
64     targetContext.EnqueueError(
65         LOCAL_GL_INVALID_OPERATION,
66         "`%s` is from a different (or lost) WebGL context.", argName);
67     return false;
68   }
69   return true;
70 }
71 
WarnInvalidUse(const ClientWebGLContext & targetContext,const char * const argName) const72 void webgl::ObjectJS::WarnInvalidUse(const ClientWebGLContext& targetContext,
73                                      const char* const argName) const {
74   if (!ValidateForContext(targetContext, argName)) return;
75 
76   const auto errEnum = ErrorOnDeleted();
77   targetContext.EnqueueError(errEnum, "Object `%s` is already deleted.",
78                              argName);
79 }
80 
GetJSScalarFromGLType(GLenum type,js::Scalar::Type * const out_scalarType)81 static bool GetJSScalarFromGLType(GLenum type,
82                                   js::Scalar::Type* const out_scalarType) {
83   switch (type) {
84     case LOCAL_GL_BYTE:
85       *out_scalarType = js::Scalar::Int8;
86       return true;
87 
88     case LOCAL_GL_UNSIGNED_BYTE:
89       *out_scalarType = js::Scalar::Uint8;
90       return true;
91 
92     case LOCAL_GL_SHORT:
93       *out_scalarType = js::Scalar::Int16;
94       return true;
95 
96     case LOCAL_GL_HALF_FLOAT:
97     case LOCAL_GL_HALF_FLOAT_OES:
98     case LOCAL_GL_UNSIGNED_SHORT:
99     case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
100     case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
101     case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
102       *out_scalarType = js::Scalar::Uint16;
103       return true;
104 
105     case LOCAL_GL_UNSIGNED_INT:
106     case LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV:
107     case LOCAL_GL_UNSIGNED_INT_5_9_9_9_REV:
108     case LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV:
109     case LOCAL_GL_UNSIGNED_INT_24_8:
110       *out_scalarType = js::Scalar::Uint32;
111       return true;
112     case LOCAL_GL_INT:
113       *out_scalarType = js::Scalar::Int32;
114       return true;
115 
116     case LOCAL_GL_FLOAT:
117       *out_scalarType = js::Scalar::Float32;
118       return true;
119 
120     default:
121       return false;
122   }
123 }
124 
ClientWebGLContext(const bool webgl2)125 ClientWebGLContext::ClientWebGLContext(const bool webgl2)
126     : mIsWebGL2(webgl2),
127       mExtLoseContext(new ClientWebGLExtensionLoseContext(*this)) {}
128 
~ClientWebGLContext()129 ClientWebGLContext::~ClientWebGLContext() { RemovePostRefreshObserver(); }
130 
JsWarning(const std::string & utf8) const131 void ClientWebGLContext::JsWarning(const std::string& utf8) const {
132   nsIGlobalObject* global = nullptr;
133   if (mCanvasElement) {
134     mozilla::dom::Document* doc = mCanvasElement->OwnerDoc();
135     if (doc) {
136       global = doc->GetScopeObject();
137     }
138   } else if (mOffscreenCanvas) {
139     global = mOffscreenCanvas->GetOwnerGlobal();
140   }
141 
142   dom::AutoJSAPI api;
143   if (!api.Init(global)) {
144     return;
145   }
146   const auto& cx = api.cx();
147   JS::WarnUTF8(cx, "%s", utf8.c_str());
148 }
149 
AutoJsWarning(const std::string & utf8)150 void AutoJsWarning(const std::string& utf8) {
151   if (NS_IsMainThread()) {
152     const AutoJSContext cx;
153     JS::WarnUTF8(cx, "%s", utf8.c_str());
154     return;
155   }
156 
157   JSContext* cx = dom::GetCurrentWorkerThreadJSContext();
158   if (NS_WARN_IF(!cx)) {
159     return;
160   }
161 
162   JS::WarnUTF8(cx, "%s", utf8.c_str());
163 }
164 
165 // ---------
166 
DispatchEvent(const nsAString & eventName) const167 bool ClientWebGLContext::DispatchEvent(const nsAString& eventName) const {
168   const auto kCanBubble = CanBubble::eYes;
169   const auto kIsCancelable = Cancelable::eYes;
170   bool useDefaultHandler = true;
171 
172   if (mCanvasElement) {
173     nsContentUtils::DispatchTrustedEvent(
174         mCanvasElement->OwnerDoc(), static_cast<nsIContent*>(mCanvasElement),
175         eventName, kCanBubble, kIsCancelable, &useDefaultHandler);
176   } else if (mOffscreenCanvas) {
177     // OffscreenCanvas case
178     RefPtr<dom::Event> event =
179         new dom::Event(mOffscreenCanvas, nullptr, nullptr);
180     event->InitEvent(eventName, kCanBubble, kIsCancelable);
181     event->SetTrusted(true);
182     useDefaultHandler = mOffscreenCanvas->DispatchEvent(
183         *event, dom::CallerType::System, IgnoreErrors());
184   }
185   return useDefaultHandler;
186 }
187 
188 // -
189 
EmulateLoseContext() const190 void ClientWebGLContext::EmulateLoseContext() const {
191   const FuncScope funcScope(*this, "loseContext");
192   if (mLossStatus != webgl::LossStatus::Ready) {
193     JsWarning("loseContext: Already lost.");
194     if (!mNextError) {
195       mNextError = LOCAL_GL_INVALID_OPERATION;
196     }
197     return;
198   }
199   OnContextLoss(webgl::ContextLossReason::Manual);
200 }
201 
OnContextLoss(const webgl::ContextLossReason reason) const202 void ClientWebGLContext::OnContextLoss(
203     const webgl::ContextLossReason reason) const {
204   JsWarning("WebGL context was lost.");
205 
206   if (mNotLost) {
207     for (const auto& ext : mNotLost->extensions) {
208       if (!ext) continue;
209       ext->mContext = nullptr;  // Detach.
210     }
211     mNotLost = {};  // Lost now!
212     mNextError = LOCAL_GL_CONTEXT_LOST_WEBGL;
213   }
214 
215   switch (reason) {
216     case webgl::ContextLossReason::Guilty:
217       mLossStatus = webgl::LossStatus::LostForever;
218       break;
219 
220     case webgl::ContextLossReason::None:
221       mLossStatus = webgl::LossStatus::Lost;
222       break;
223 
224     case webgl::ContextLossReason::Manual:
225       mLossStatus = webgl::LossStatus::LostManually;
226       break;
227   }
228 
229   const auto weak = WeakPtr<const ClientWebGLContext>(this);
230   const auto fnRun = [weak]() {
231     const auto strong = RefPtr<const ClientWebGLContext>(weak);
232     if (!strong) return;
233     strong->Event_webglcontextlost();
234   };
235   already_AddRefed<mozilla::CancelableRunnable> runnable =
236       NS_NewCancelableRunnableFunction("enqueue Event_webglcontextlost", fnRun);
237   NS_DispatchToCurrentThread(std::move(runnable));
238 }
239 
Event_webglcontextlost() const240 void ClientWebGLContext::Event_webglcontextlost() const {
241   const bool useDefaultHandler = DispatchEvent(u"webglcontextlost"_ns);
242   if (useDefaultHandler) {
243     mLossStatus = webgl::LossStatus::LostForever;
244   }
245 
246   if (mLossStatus == webgl::LossStatus::Lost) {
247     RestoreContext(webgl::LossStatus::Lost);
248   }
249 }
250 
RestoreContext(const webgl::LossStatus requiredStatus) const251 void ClientWebGLContext::RestoreContext(
252     const webgl::LossStatus requiredStatus) const {
253   if (requiredStatus != mLossStatus) {
254     JsWarning(
255         "restoreContext: Only valid iff context lost with loseContext().");
256     if (!mNextError) {
257       mNextError = LOCAL_GL_INVALID_OPERATION;
258     }
259     return;
260   }
261   MOZ_RELEASE_ASSERT(mLossStatus == webgl::LossStatus::Lost ||
262                      mLossStatus == webgl::LossStatus::LostManually);
263 
264   if (mAwaitingRestore) return;
265   mAwaitingRestore = true;
266 
267   const auto weak = WeakPtr<const ClientWebGLContext>(this);
268   const auto fnRun = [weak]() {
269     const auto strong = RefPtr<const ClientWebGLContext>(weak);
270     if (!strong) return;
271     strong->Event_webglcontextrestored();
272   };
273   already_AddRefed<mozilla::CancelableRunnable> runnable =
274       NS_NewCancelableRunnableFunction("enqueue Event_webglcontextrestored",
275                                        fnRun);
276   NS_DispatchToCurrentThread(std::move(runnable));
277 }
278 
Event_webglcontextrestored() const279 void ClientWebGLContext::Event_webglcontextrestored() const {
280   mAwaitingRestore = false;
281   mLossStatus = webgl::LossStatus::Ready;
282   mNextError = 0;
283 
284   uvec2 requestSize;
285   if (mCanvasElement) {
286     requestSize = {mCanvasElement->Width(), mCanvasElement->Height()};
287   } else if (mOffscreenCanvas) {
288     requestSize = {mOffscreenCanvas->Width(), mOffscreenCanvas->Height()};
289   } else {
290     MOZ_ASSERT_UNREACHABLE("no HTMLCanvasElement or OffscreenCanvas!");
291     return;
292   }
293 
294   if (!requestSize.x) {
295     requestSize.x = 1;
296   }
297   if (!requestSize.y) {
298     requestSize.y = 1;
299   }
300 
301   const auto mutThis = const_cast<ClientWebGLContext*>(
302       this);  // TODO: Make context loss non-mutable.
303   if (!mutThis->CreateHostContext(requestSize)) {
304     mLossStatus = webgl::LossStatus::LostForever;
305     return;
306   }
307 
308   (void)DispatchEvent(u"webglcontextrestored"_ns);
309 }
310 
311 // ---------
312 
ThrowEvent_WebGLContextCreationError(const std::string & text) const313 void ClientWebGLContext::ThrowEvent_WebGLContextCreationError(
314     const std::string& text) const {
315   nsCString msg;
316   msg.AppendPrintf("Failed to create WebGL context: %s", text.c_str());
317   JsWarning(msg.BeginReading());
318 
319   RefPtr<dom::EventTarget> target = mCanvasElement;
320   if (!target && mOffscreenCanvas) {
321     target = mOffscreenCanvas;
322   } else if (!target) {
323     return;
324   }
325 
326   const auto kEventName = u"webglcontextcreationerror"_ns;
327 
328   dom::WebGLContextEventInit eventInit;
329   // eventInit.mCancelable = true; // The spec says this, but it's silly.
330   eventInit.mStatusMessage = NS_ConvertASCIItoUTF16(text.c_str());
331 
332   const RefPtr<dom::WebGLContextEvent> event =
333       dom::WebGLContextEvent::Constructor(target, kEventName, eventInit);
334   event->SetTrusted(true);
335 
336   target->DispatchEvent(*event);
337 }
338 
339 // -
340 
341 // If we are running WebGL in this process then call the HostWebGLContext
342 // method directly.  Otherwise, dispatch over IPC.
343 template <typename MethodType, MethodType method, typename... Args>
Run(Args &&...args) const344 void ClientWebGLContext::Run(Args&&... args) const {
345   const auto notLost =
346       mNotLost;  // Hold a strong-ref to prevent LoseContext=>UAF.
347   if (IsContextLost()) return;
348 
349   const auto& inProcess = notLost->inProcess;
350   if (inProcess) {
351     return (inProcess.get()->*method)(std::forward<Args>(args)...);
352   }
353 
354   const auto& child = notLost->outOfProcess;
355 
356   const auto id = IdByMethod<MethodType, method>();
357 
358   const auto size = webgl::SerializedSize(id, args...);
359   const auto maybeDest = child->AllocPendingCmdBytes(size);
360   if (!maybeDest) {
361     JsWarning("Failed to allocate internal command buffer.");
362     OnContextLoss(webgl::ContextLossReason::None);
363     return;
364   }
365   const auto& destBytes = *maybeDest;
366   webgl::Serialize(destBytes, id, args...);
367 }
368 
369 // -------------------------------------------------------------------------
370 // Client-side helper methods.  Dispatch to a Host method.
371 // -------------------------------------------------------------------------
372 
373 #define RPROC(_METHOD) \
374   decltype(&HostWebGLContext::_METHOD), &HostWebGLContext::_METHOD
375 
376 // ------------------------- Composition, etc -------------------------
377 
OnBeforePaintTransaction()378 void ClientWebGLContext::OnBeforePaintTransaction() { Present(nullptr); }
379 
EndComposition()380 void ClientWebGLContext::EndComposition() {
381   // Mark ourselves as no longer invalidated.
382   MarkContextClean();
383 }
384 
385 // -
386 
GetTexTypeForSwapChain() const387 layers::TextureType ClientWebGLContext::GetTexTypeForSwapChain() const {
388   const RefPtr<layers::ImageBridgeChild> imageBridge =
389       layers::ImageBridgeChild::GetSingleton();
390   return layers::TexTypeForWebgl(imageBridge);
391 }
392 
Present(WebGLFramebufferJS * const xrFb,const bool webvr)393 void ClientWebGLContext::Present(WebGLFramebufferJS* const xrFb,
394                                  const bool webvr) {
395   const auto texType = GetTexTypeForSwapChain();
396   Present(xrFb, texType, webvr);
397 }
398 
Present(WebGLFramebufferJS * const xrFb,const layers::TextureType type,const bool webvr)399 void ClientWebGLContext::Present(WebGLFramebufferJS* const xrFb,
400                                  const layers::TextureType type,
401                                  const bool webvr) {
402   if (!mIsCanvasDirty && !xrFb) return;
403   if (!xrFb) {
404     mIsCanvasDirty = false;
405   }
406   CancelAutoFlush();
407   Run<RPROC(Present)>(xrFb ? xrFb->mId : 0, type, webvr);
408 }
409 
CopyToSwapChain(WebGLFramebufferJS * const fb,const webgl::SwapChainOptions & options)410 void ClientWebGLContext::CopyToSwapChain(
411     WebGLFramebufferJS* const fb, const webgl::SwapChainOptions& options) {
412   CancelAutoFlush();
413   const auto texType = GetTexTypeForSwapChain();
414   Run<RPROC(CopyToSwapChain)>(fb ? fb->mId : 0, texType, options);
415 }
416 
EndOfFrame()417 void ClientWebGLContext::EndOfFrame() {
418   CancelAutoFlush();
419   Run<RPROC(EndOfFrame)>();
420 }
421 
GetFrontBuffer(WebGLFramebufferJS * const fb,bool vr)422 Maybe<layers::SurfaceDescriptor> ClientWebGLContext::GetFrontBuffer(
423     WebGLFramebufferJS* const fb, bool vr) {
424   const auto notLost = mNotLost;
425   if (IsContextLost()) return {};
426 
427   const auto& inProcess = mNotLost->inProcess;
428   if (inProcess) {
429     return inProcess->GetFrontBuffer(fb ? fb->mId : 0, vr);
430   }
431 
432   const auto& child = mNotLost->outOfProcess;
433   child->FlushPendingCmds();
434   Maybe<layers::SurfaceDescriptor> ret;
435   if (!child->SendGetFrontBuffer(fb ? fb->mId : 0, vr, &ret)) return {};
436   return ret;
437 }
438 
PresentFrontBuffer(WebGLFramebufferJS * const fb,const layers::TextureType type,bool webvr)439 Maybe<layers::SurfaceDescriptor> ClientWebGLContext::PresentFrontBuffer(
440     WebGLFramebufferJS* const fb, const layers::TextureType type, bool webvr) {
441   Present(fb, type, webvr);
442   return GetFrontBuffer(fb, webvr);
443 }
444 
ClearVRSwapChain()445 void ClientWebGLContext::ClearVRSwapChain() { Run<RPROC(ClearVRSwapChain)>(); }
446 
447 // -
448 
UpdateWebRenderCanvasData(nsDisplayListBuilder * aBuilder,WebRenderCanvasData * aCanvasData)449 bool ClientWebGLContext::UpdateWebRenderCanvasData(
450     nsDisplayListBuilder* aBuilder, WebRenderCanvasData* aCanvasData) {
451   CanvasRenderer* renderer = aCanvasData->GetCanvasRenderer();
452 
453   if (!mResetLayer && renderer) {
454     return true;
455   }
456 
457   renderer = aCanvasData->CreateCanvasRenderer();
458   if (!InitializeCanvasRenderer(aBuilder, renderer)) {
459     // Clear CanvasRenderer of WebRenderCanvasData
460     aCanvasData->ClearCanvasRenderer();
461     return false;
462   }
463 
464   MOZ_ASSERT(renderer);
465   mResetLayer = false;
466   return true;
467 }
468 
InitializeCanvasRenderer(nsDisplayListBuilder * aBuilder,CanvasRenderer * aRenderer)469 bool ClientWebGLContext::InitializeCanvasRenderer(
470     nsDisplayListBuilder* aBuilder, CanvasRenderer* aRenderer) {
471   const FuncScope funcScope(*this, "<InitializeCanvasRenderer>");
472   if (IsContextLost()) return false;
473 
474   layers::CanvasRendererData data;
475   data.mContext = this;
476   data.mOriginPos = gl::OriginPos::BottomLeft;
477 
478   const auto& options = *mInitialOptions;
479   const auto& size = DrawingBufferSize();
480   data.mIsOpaque = !options.alpha;
481   data.mIsAlphaPremult = !options.alpha || options.premultipliedAlpha;
482   data.mSize = {size.x, size.y};
483 
484   if (aBuilder->IsPaintingToWindow() && mCanvasElement) {
485     data.mDoPaintCallbacks = true;
486   }
487 
488   aRenderer->Initialize(data);
489   aRenderer->SetDirty();
490   return true;
491 }
492 
UpdateCanvasParameters()493 void ClientWebGLContext::UpdateCanvasParameters() {
494   if (!mOffscreenCanvas) {
495     return;
496   }
497 
498   const auto& options = *mInitialOptions;
499   const auto& size = DrawingBufferSize();
500 
501   mozilla::dom::OffscreenCanvasDisplayData data;
502   data.mOriginPos = gl::OriginPos::BottomLeft;
503   data.mIsOpaque = !options.alpha;
504   data.mIsAlphaPremult = !options.alpha || options.premultipliedAlpha;
505   data.mSize = {size.x, size.y};
506   data.mDoPaintCallbacks = false;
507 
508   mOffscreenCanvas->UpdateDisplayData(data);
509 }
510 
GetCompositorBackendType() const511 layers::LayersBackend ClientWebGLContext::GetCompositorBackendType() const {
512   if (mCanvasElement) {
513     return mCanvasElement->GetCompositorBackendType();
514   } else if (mOffscreenCanvas) {
515     return mOffscreenCanvas->GetCompositorBackendType();
516   }
517 
518   return layers::LayersBackend::LAYERS_NONE;
519 }
520 
GetOwnerDoc() const521 mozilla::dom::Document* ClientWebGLContext::GetOwnerDoc() const {
522   MOZ_ASSERT(mCanvasElement);
523   if (!mCanvasElement) {
524     return nullptr;
525   }
526   return mCanvasElement->OwnerDoc();
527 }
528 
Commit()529 void ClientWebGLContext::Commit() {
530   if (mOffscreenCanvas) {
531     mOffscreenCanvas->CommitFrameToCompositor();
532   }
533 }
534 
GetCanvas(dom::Nullable<dom::OwningHTMLCanvasElementOrOffscreenCanvas> & retval)535 void ClientWebGLContext::GetCanvas(
536     dom::Nullable<dom::OwningHTMLCanvasElementOrOffscreenCanvas>& retval) {
537   if (mCanvasElement) {
538     MOZ_RELEASE_ASSERT(!mOffscreenCanvas, "GFX: Canvas is offscreen.");
539 
540     if (mCanvasElement->IsInNativeAnonymousSubtree()) {
541       retval.SetNull();
542     } else {
543       retval.SetValue().SetAsHTMLCanvasElement() = mCanvasElement;
544     }
545   } else if (mOffscreenCanvas) {
546     retval.SetValue().SetAsOffscreenCanvas() = mOffscreenCanvas;
547   } else {
548     retval.SetNull();
549   }
550 }
551 
GetContextAttributes(dom::Nullable<dom::WebGLContextAttributes> & retval)552 void ClientWebGLContext::GetContextAttributes(
553     dom::Nullable<dom::WebGLContextAttributes>& retval) {
554   retval.SetNull();
555   const FuncScope funcScope(*this, "getContextAttributes");
556   if (IsContextLost()) return;
557 
558   dom::WebGLContextAttributes& result = retval.SetValue();
559 
560   const auto& options = mNotLost->info.options;
561 
562   result.mAlpha.Construct(options.alpha);
563   result.mDepth = options.depth;
564   result.mStencil = options.stencil;
565   result.mAntialias.Construct(options.antialias);
566   result.mPremultipliedAlpha = options.premultipliedAlpha;
567   result.mPreserveDrawingBuffer = options.preserveDrawingBuffer;
568   result.mFailIfMajorPerformanceCaveat = options.failIfMajorPerformanceCaveat;
569   result.mPowerPreference = options.powerPreference;
570 }
571 
572 // -----------------------
573 
574 NS_IMETHODIMP
SetDimensions(const int32_t signedWidth,const int32_t signedHeight)575 ClientWebGLContext::SetDimensions(const int32_t signedWidth,
576                                   const int32_t signedHeight) {
577   const FuncScope funcScope(*this, "<SetDimensions>");
578   MOZ_ASSERT(mInitialOptions);
579 
580   if (mLossStatus != webgl::LossStatus::Ready) {
581     // Attempted resize of a lost context.
582     return NS_OK;
583   }
584 
585   uvec2 size = {static_cast<uint32_t>(signedWidth),
586                 static_cast<uint32_t>(signedHeight)};
587   if (!size.x) {
588     size.x = 1;
589   }
590   if (!size.y) {
591     size.y = 1;
592   }
593   const auto prevRequestedSize = mRequestedSize;
594   mRequestedSize = size;
595 
596   mResetLayer = true;  // Always treat this as resize.
597 
598   if (mNotLost) {
599     auto& state = State();
600 
601     auto curSize = prevRequestedSize;
602     if (state.mDrawingBufferSize) {
603       curSize = *state.mDrawingBufferSize;
604     }
605     if (size == curSize) return NS_OK;  // MUST skip no-op resize
606 
607     state.mDrawingBufferSize = Nothing();
608     Run<RPROC(Resize)>(size);
609 
610     UpdateCanvasParameters();
611     MarkCanvasDirty();
612     return NS_OK;
613   }
614 
615   // -
616   // Context (re-)creation
617 
618   if (!CreateHostContext(size)) {
619     return NS_ERROR_FAILURE;
620   }
621   return NS_OK;
622 }
623 
IsWebglOutOfProcessEnabled()624 static bool IsWebglOutOfProcessEnabled() {
625   if (StaticPrefs::webgl_out_of_process_force()) {
626     return true;
627   }
628   if (!gfx::gfxVars::AllowWebglOop()) {
629     return false;
630   }
631   if (!NS_IsMainThread()) {
632     return StaticPrefs::webgl_out_of_process_worker();
633   }
634   return StaticPrefs::webgl_out_of_process();
635 }
636 
StartsWith(const std::string & haystack,const std::string & needle)637 static inline bool StartsWith(const std::string& haystack,
638                               const std::string& needle) {
639   return haystack.find(needle) == 0;
640 }
641 
CreateHostContext(const uvec2 & requestedSize)642 bool ClientWebGLContext::CreateHostContext(const uvec2& requestedSize) {
643   const auto pNotLost = std::make_shared<webgl::NotLostData>(*this);
644   auto& notLost = *pNotLost;
645 
646   auto res = [&]() -> Result<Ok, std::string> {
647     auto options = *mInitialOptions;
648     if (StaticPrefs::webgl_disable_fail_if_major_performance_caveat()) {
649       options.failIfMajorPerformanceCaveat = false;
650     }
651 
652     if (options.failIfMajorPerformanceCaveat) {
653       const auto backend = GetCompositorBackendType();
654       bool isCompositorSlow = false;
655       isCompositorSlow |= (backend == layers::LayersBackend::LAYERS_WR &&
656                            gfx::gfxVars::UseSoftwareWebRender());
657 
658       if (isCompositorSlow) {
659         return Err(
660             "failIfMajorPerformanceCaveat: Compositor is not"
661             " hardware-accelerated.");
662       }
663     }
664 
665     const bool resistFingerprinting = ShouldResistFingerprinting();
666     const auto principalKey = GetPrincipalHashValue();
667     const auto initDesc = webgl::InitContextDesc{
668         mIsWebGL2, resistFingerprinting, requestedSize, options, principalKey};
669 
670     // -
671 
672     auto useOop = IsWebglOutOfProcessEnabled();
673     if (XRE_IsParentProcess()) {
674       useOop = false;
675     }
676 
677     if (!useOop) {
678       notLost.inProcess =
679           HostWebGLContext::Create({this, nullptr}, initDesc, &notLost.info);
680       return Ok();
681     }
682 
683     // -
684 
685     ScopedGfxFeatureReporter reporter("IpcWebGL");
686 
687     auto* const cm = gfx::CanvasManagerChild::Get();
688     MOZ_ASSERT(cm);
689     if (!cm) {
690       return Err("!CanvasManagerChild::Get()");
691     }
692 
693     RefPtr<dom::WebGLChild> outOfProcess = new dom::WebGLChild(*this);
694     outOfProcess =
695         static_cast<dom::WebGLChild*>(cm->SendPWebGLConstructor(outOfProcess));
696     if (!outOfProcess) {
697       return Err("SendPWebGLConstructor failed");
698     }
699 
700     if (!outOfProcess->SendInitialize(initDesc, &notLost.info)) {
701       return Err("WebGL actor Initialize failed");
702     }
703 
704     notLost.outOfProcess = outOfProcess;
705     reporter.SetSuccessful();
706     return Ok();
707   }();
708   if (!res.isOk()) {
709     auto str = res.unwrapErr();
710     if (StartsWith(str, "failIfMajorPerformanceCaveat")) {
711       str +=
712           " (about:config override available:"
713           " webgl.disable-fail-if-major-performance-caveat)";
714     }
715     notLost.info.error = str;
716   }
717   if (!notLost.info.error.empty()) {
718     ThrowEvent_WebGLContextCreationError(notLost.info.error);
719     return false;
720   }
721   mNotLost = pNotLost;
722   UpdateCanvasParameters();
723   MarkCanvasDirty();
724 
725   // Init state
726   const auto& limits = Limits();
727   auto& state = State();
728   state.mDefaultTfo = new WebGLTransformFeedbackJS(*this);
729   state.mDefaultVao = new WebGLVertexArrayJS(*this);
730 
731   state.mBoundTfo = state.mDefaultTfo;
732   state.mBoundVao = state.mDefaultVao;
733 
734   (void)state.mBoundBufferByTarget[LOCAL_GL_ARRAY_BUFFER];
735 
736   state.mTexUnits.resize(limits.maxTexUnits);
737   state.mBoundUbos.resize(limits.maxUniformBufferBindings);
738 
739   {
740     webgl::TypedQuad initVal;
741     const float fData[4] = {0, 0, 0, 1};
742     memcpy(initVal.data, fData, sizeof(initVal.data));
743     state.mGenericVertexAttribs.resize(limits.maxVertexAttribs, initVal);
744   }
745 
746   const auto& size = DrawingBufferSize();
747   state.mViewport = {0, 0, static_cast<int32_t>(size.x),
748                      static_cast<int32_t>(size.y)};
749   state.mScissor = state.mViewport;
750 
751   if (mIsWebGL2) {
752     // Insert keys to enable slots:
753     (void)state.mBoundBufferByTarget[LOCAL_GL_COPY_READ_BUFFER];
754     (void)state.mBoundBufferByTarget[LOCAL_GL_COPY_WRITE_BUFFER];
755     (void)state.mBoundBufferByTarget[LOCAL_GL_PIXEL_PACK_BUFFER];
756     (void)state.mBoundBufferByTarget[LOCAL_GL_PIXEL_UNPACK_BUFFER];
757     (void)state.mBoundBufferByTarget[LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER];
758     (void)state.mBoundBufferByTarget[LOCAL_GL_UNIFORM_BUFFER];
759 
760     (void)state.mCurrentQueryByTarget[LOCAL_GL_ANY_SAMPLES_PASSED];
761     //(void)state.mCurrentQueryByTarget[LOCAL_GL_ANY_SAMPLES_PASSED_CONSERVATIVE];
762     //// Same slot as ANY_SAMPLES_PASSED.
763     (void)state
764         .mCurrentQueryByTarget[LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN];
765   }
766 
767   return true;
768 }
769 
770 // -------
771 
DrawingBufferSize()772 uvec2 ClientWebGLContext::DrawingBufferSize() {
773   if (IsContextLost()) return {};
774   const auto notLost =
775       mNotLost;  // Hold a strong-ref to prevent LoseContext=>UAF.
776   auto& state = State();
777   auto& size = state.mDrawingBufferSize;
778 
779   if (!size) {
780     const auto& inProcess = mNotLost->inProcess;
781     if (inProcess) {
782       size = Some(inProcess->DrawingBufferSize());
783     } else {
784       const auto& child = mNotLost->outOfProcess;
785       child->FlushPendingCmds();
786       uvec2 actual = {};
787       if (!child->SendDrawingBufferSize(&actual)) return {};
788       size = Some(actual);
789     }
790   }
791 
792   return *size;
793 }
794 
OnMemoryPressure()795 void ClientWebGLContext::OnMemoryPressure() {
796   if (IsContextLost()) return;
797 
798   const auto& inProcess = mNotLost->inProcess;
799   if (inProcess) {
800     return inProcess->OnMemoryPressure();
801   }
802   const auto& child = mNotLost->outOfProcess;
803   (void)child->SendOnMemoryPressure();
804 }
805 
806 NS_IMETHODIMP
SetContextOptions(JSContext * cx,JS::Handle<JS::Value> options,ErrorResult & aRvForDictionaryInit)807 ClientWebGLContext::SetContextOptions(JSContext* cx,
808                                       JS::Handle<JS::Value> options,
809                                       ErrorResult& aRvForDictionaryInit) {
810   if (mInitialOptions && options.isNullOrUndefined()) return NS_OK;
811 
812   dom::WebGLContextAttributes attributes;
813   if (!attributes.Init(cx, options)) {
814     aRvForDictionaryInit.Throw(NS_ERROR_UNEXPECTED);
815     return NS_ERROR_UNEXPECTED;
816   }
817 
818   WebGLContextOptions newOpts;
819 
820   newOpts.stencil = attributes.mStencil;
821   newOpts.depth = attributes.mDepth;
822   newOpts.premultipliedAlpha = attributes.mPremultipliedAlpha;
823   newOpts.preserveDrawingBuffer = attributes.mPreserveDrawingBuffer;
824   newOpts.failIfMajorPerformanceCaveat =
825       attributes.mFailIfMajorPerformanceCaveat;
826   newOpts.xrCompatible = attributes.mXrCompatible;
827   newOpts.powerPreference = attributes.mPowerPreference;
828   newOpts.enableDebugRendererInfo =
829       StaticPrefs::webgl_enable_debug_renderer_info();
830   MOZ_ASSERT(mCanvasElement || mOffscreenCanvas);
831   newOpts.shouldResistFingerprinting = ShouldResistFingerprinting();
832 
833   if (attributes.mAlpha.WasPassed()) {
834     newOpts.alpha = attributes.mAlpha.Value();
835   }
836   if (attributes.mAntialias.WasPassed()) {
837     newOpts.antialias = attributes.mAntialias.Value();
838   }
839 
840   // Don't do antialiasing if we've disabled MSAA.
841   if (!StaticPrefs::webgl_msaa_samples()) {
842     newOpts.antialias = false;
843   }
844 
845   if (mInitialOptions && *mInitialOptions != newOpts) {
846     // Err if the options asked for aren't the same as what they were
847     // originally.
848     return NS_ERROR_FAILURE;
849   }
850 
851   mXRCompatible = attributes.mXrCompatible;
852 
853   mInitialOptions.emplace(newOpts);
854   return NS_OK;
855 }
856 
DidRefresh()857 void ClientWebGLContext::DidRefresh() { Run<RPROC(DidRefresh)>(); }
858 
GetSurfaceSnapshot(gfxAlphaType * const out_alphaType)859 already_AddRefed<gfx::SourceSurface> ClientWebGLContext::GetSurfaceSnapshot(
860     gfxAlphaType* const out_alphaType) {
861   const FuncScope funcScope(*this, "<GetSurfaceSnapshot>");
862   if (IsContextLost()) return nullptr;
863   const auto notLost =
864       mNotLost;  // Hold a strong-ref to prevent LoseContext=>UAF.
865 
866   auto ret = BackBufferSnapshot();
867   if (!ret) return nullptr;
868 
869   // -
870 
871   const auto& options = mNotLost->info.options;
872 
873   auto srcAlphaType = gfxAlphaType::Opaque;
874   if (options.alpha) {
875     if (options.premultipliedAlpha) {
876       srcAlphaType = gfxAlphaType::Premult;
877     } else {
878       srcAlphaType = gfxAlphaType::NonPremult;
879     }
880   }
881 
882   if (out_alphaType) {
883     *out_alphaType = srcAlphaType;
884   } else {
885     // Expects Opaque or Premult
886     if (srcAlphaType == gfxAlphaType::NonPremult) {
887       const gfx::DataSourceSurface::ScopedMap map(
888           ret, gfx::DataSourceSurface::READ_WRITE);
889       MOZ_RELEASE_ASSERT(map.IsMapped(), "Failed to map snapshot surface!");
890 
891       const auto& size = ret->GetSize();
892       const auto format = ret->GetFormat();
893       bool rv =
894           gfx::PremultiplyData(map.GetData(), map.GetStride(), format,
895                                map.GetData(), map.GetStride(), format, size);
896       MOZ_RELEASE_ASSERT(rv, "PremultiplyData failed!");
897     }
898   }
899 
900   return ret.forget();
901 }
902 
GetFrontBufferSnapshot(const bool requireAlphaPremult)903 RefPtr<gfx::SourceSurface> ClientWebGLContext::GetFrontBufferSnapshot(
904     const bool requireAlphaPremult) {
905   const FuncScope funcScope(*this, "<GetSurfaceSnapshot>");
906   if (IsContextLost()) return nullptr;
907   const auto notLost =
908       mNotLost;  // Hold a strong-ref to prevent LoseContext=>UAF.
909 
910   const auto& options = mNotLost->info.options;
911 
912   const auto surfFormat = options.alpha ? gfx::SurfaceFormat::B8G8R8A8
913                                         : gfx::SurfaceFormat::B8G8R8X8;
914 
915   const auto fnNewSurf = [&](const uvec2 size) {
916     const auto stride = size.x * 4;
917     return RefPtr<gfx::DataSourceSurface>(
918         gfx::Factory::CreateDataSourceSurfaceWithStride({size.x, size.y},
919                                                         surfFormat, stride,
920                                                         /*zero=*/true));
921   };
922 
923   const auto& inProcess = mNotLost->inProcess;
924   if (inProcess) {
925     const auto maybeSize = inProcess->FrontBufferSnapshotInto({});
926     if (!maybeSize) return nullptr;
927     const auto& surfSize = *maybeSize;
928     const auto stride = surfSize.x * 4;
929     const auto byteSize = stride * surfSize.y;
930     const auto surf = fnNewSurf(surfSize);
931     if (!surf) return nullptr;
932     {
933       const gfx::DataSourceSurface::ScopedMap map(
934           surf, gfx::DataSourceSurface::READ_WRITE);
935       if (!map.IsMapped()) {
936         MOZ_ASSERT(false);
937         return nullptr;
938       }
939       MOZ_RELEASE_ASSERT(map.GetStride() == static_cast<int64_t>(stride));
940       auto range = Range<uint8_t>{map.GetData(), byteSize};
941       if (!inProcess->FrontBufferSnapshotInto(Some(range))) {
942         gfxCriticalNote << "ClientWebGLContext::GetFrontBufferSnapshot: "
943                            "FrontBufferSnapshotInto(some) failed after "
944                            "FrontBufferSnapshotInto(none)";
945         return nullptr;
946       }
947       if (requireAlphaPremult && options.alpha && !options.premultipliedAlpha) {
948         bool rv = gfx::PremultiplyData(
949             map.GetData(), map.GetStride(), gfx::SurfaceFormat::R8G8B8A8,
950             map.GetData(), map.GetStride(), gfx::SurfaceFormat::B8G8R8A8,
951             surf->GetSize());
952         MOZ_RELEASE_ASSERT(rv, "PremultiplyData failed!");
953       } else {
954         bool rv = gfx::SwizzleData(
955             map.GetData(), map.GetStride(), gfx::SurfaceFormat::R8G8B8A8,
956             map.GetData(), map.GetStride(), gfx::SurfaceFormat::B8G8R8A8,
957             surf->GetSize());
958         MOZ_RELEASE_ASSERT(rv, "SwizzleData failed!");
959       }
960     }
961     return surf;
962   }
963   const auto& child = mNotLost->outOfProcess;
964   child->FlushPendingCmds();
965   webgl::FrontBufferSnapshotIpc res;
966   if (!child->SendGetFrontBufferSnapshot(&res)) {
967     res = {};
968   }
969   if (!res.shmem) return nullptr;
970 
971   const auto& surfSize = res.surfSize;
972   const webgl::RaiiShmem shmem{child, res.shmem.ref()};
973   if (!shmem) return nullptr;
974   const auto& shmemBytes = shmem.ByteRange();
975   if (!surfSize.x) return nullptr;  // Zero means failure.
976 
977   const auto stride = surfSize.x * 4;
978   const auto byteSize = stride * surfSize.y;
979 
980   const auto surf = fnNewSurf(surfSize);
981   if (!surf) return nullptr;
982 
983   {
984     const gfx::DataSourceSurface::ScopedMap map(
985         surf, gfx::DataSourceSurface::READ_WRITE);
986     if (!map.IsMapped()) {
987       MOZ_ASSERT(false);
988       return nullptr;
989     }
990     MOZ_RELEASE_ASSERT(shmemBytes.length() == byteSize);
991     if (requireAlphaPremult && options.alpha && !options.premultipliedAlpha) {
992       bool rv = gfx::PremultiplyData(
993           shmemBytes.begin().get(), stride, gfx::SurfaceFormat::R8G8B8A8,
994           map.GetData(), map.GetStride(), gfx::SurfaceFormat::B8G8R8A8,
995           surf->GetSize());
996       MOZ_RELEASE_ASSERT(rv, "PremultiplyData failed!");
997     } else {
998       bool rv = gfx::SwizzleData(shmemBytes.begin().get(), stride,
999                                  gfx::SurfaceFormat::R8G8B8A8, map.GetData(),
1000                                  map.GetStride(), gfx::SurfaceFormat::B8G8R8A8,
1001                                  surf->GetSize());
1002       MOZ_RELEASE_ASSERT(rv, "SwizzleData failed!");
1003     }
1004   }
1005   return surf;
1006 }
1007 
BackBufferSnapshot()1008 RefPtr<gfx::DataSourceSurface> ClientWebGLContext::BackBufferSnapshot() {
1009   if (IsContextLost()) return nullptr;
1010   const auto notLost =
1011       mNotLost;  // Hold a strong-ref to prevent LoseContext=>UAF.
1012 
1013   const auto& options = mNotLost->info.options;
1014   const auto& state = State();
1015 
1016   const auto drawFbWas = state.mBoundDrawFb;
1017   const auto readFbWas = state.mBoundReadFb;
1018   const auto pboWas =
1019       Find(state.mBoundBufferByTarget, LOCAL_GL_PIXEL_PACK_BUFFER);
1020 
1021   const auto size = DrawingBufferSize();
1022 
1023   // -
1024 
1025   BindFramebuffer(LOCAL_GL_FRAMEBUFFER, nullptr);
1026   if (pboWas) {
1027     BindBuffer(LOCAL_GL_PIXEL_PACK_BUFFER, nullptr);
1028   }
1029 
1030   auto reset = MakeScopeExit([&] {
1031     if (drawFbWas == readFbWas) {
1032       BindFramebuffer(LOCAL_GL_FRAMEBUFFER, drawFbWas);
1033     } else {
1034       BindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, drawFbWas);
1035       BindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, readFbWas);
1036     }
1037     if (pboWas) {
1038       BindBuffer(LOCAL_GL_PIXEL_PACK_BUFFER, pboWas);
1039     }
1040   });
1041 
1042   const auto surfFormat = options.alpha ? gfx::SurfaceFormat::B8G8R8A8
1043                                         : gfx::SurfaceFormat::B8G8R8X8;
1044   const auto stride = size.x * 4;
1045   RefPtr<gfx::DataSourceSurface> surf =
1046       gfx::Factory::CreateDataSourceSurfaceWithStride(
1047           {size.x, size.y}, surfFormat, stride, /*zero=*/true);
1048   if (NS_WARN_IF(!surf)) {
1049     // Was this an OOM or alloc-limit? (500MB is our default resource size
1050     // limit)
1051     surf = gfx::Factory::CreateDataSourceSurfaceWithStride({1, 1}, surfFormat,
1052                                                            4, /*zero=*/true);
1053     if (!surf) {
1054       // Still failed for a 1x1 size.
1055       gfxCriticalError() << "CreateDataSourceSurfaceWithStride(surfFormat="
1056                          << surfFormat << ") failed.";
1057     }
1058     return nullptr;
1059   }
1060 
1061   {
1062     const gfx::DataSourceSurface::ScopedMap map(
1063         surf, gfx::DataSourceSurface::READ_WRITE);
1064     if (!map.IsMapped()) {
1065       MOZ_ASSERT(false);
1066       return nullptr;
1067     }
1068     MOZ_ASSERT(static_cast<uint32_t>(map.GetStride()) == stride);
1069 
1070     const auto desc = webgl::ReadPixelsDesc{{0, 0}, size};
1071     const auto range = Range<uint8_t>(map.GetData(), stride * size.y);
1072     if (!DoReadPixels(desc, range)) return nullptr;
1073 
1074     const auto begin = range.begin().get();
1075 
1076     std::vector<uint8_t> temp;
1077     temp.resize(stride);
1078     for (const auto i : IntegerRange(size.y / 2)) {
1079       const auto top = begin + stride * i;
1080       const auto bottom = begin + stride * (size.y - 1 - i);
1081       memcpy(temp.data(), top, stride);
1082       memcpy(top, bottom, stride);
1083       gfxUtils::ConvertBGRAtoRGBA(top, stride);
1084 
1085       memcpy(bottom, temp.data(), stride);
1086       gfxUtils::ConvertBGRAtoRGBA(bottom, stride);
1087     }
1088 
1089     if (size.y % 2) {
1090       const auto middle = begin + stride * (size.y / 2);
1091       gfxUtils::ConvertBGRAtoRGBA(middle, stride);
1092     }
1093   }
1094 
1095   return surf;
1096 }
1097 
GetImageBuffer(int32_t * out_format)1098 UniquePtr<uint8_t[]> ClientWebGLContext::GetImageBuffer(int32_t* out_format) {
1099   *out_format = 0;
1100 
1101   // Use GetSurfaceSnapshot() to make sure that appropriate y-flip gets applied
1102   gfxAlphaType any;
1103   RefPtr<gfx::SourceSurface> snapshot = GetSurfaceSnapshot(&any);
1104   if (!snapshot) return nullptr;
1105 
1106   RefPtr<gfx::DataSourceSurface> dataSurface = snapshot->GetDataSurface();
1107 
1108   const auto& premultAlpha = mNotLost->info.options.premultipliedAlpha;
1109   return gfxUtils::GetImageBuffer(dataSurface, premultAlpha, out_format);
1110 }
1111 
1112 NS_IMETHODIMP
GetInputStream(const char * mimeType,const nsAString & encoderOptions,nsIInputStream ** out_stream)1113 ClientWebGLContext::GetInputStream(const char* mimeType,
1114                                    const nsAString& encoderOptions,
1115                                    nsIInputStream** out_stream) {
1116   // Use GetSurfaceSnapshot() to make sure that appropriate y-flip gets applied
1117   gfxAlphaType any;
1118   RefPtr<gfx::SourceSurface> snapshot = GetSurfaceSnapshot(&any);
1119   if (!snapshot) return NS_ERROR_FAILURE;
1120 
1121   RefPtr<gfx::DataSourceSurface> dataSurface = snapshot->GetDataSurface();
1122   const auto& premultAlpha = mNotLost->info.options.premultipliedAlpha;
1123   return gfxUtils::GetInputStream(dataSurface, premultAlpha, mimeType,
1124                                   encoderOptions, out_stream);
1125 }
1126 
1127 // ------------------------- Client WebGL Objects -------------------------
1128 // ------------------------- Create/Destroy/Is -------------------------
1129 
1130 template <typename T>
AsAddRefed(T * ptr)1131 static already_AddRefed<T> AsAddRefed(T* ptr) {
1132   RefPtr<T> rp = ptr;
1133   return rp.forget();
1134 }
1135 
1136 template <typename T>
AsRefPtr(T * ptr)1137 static RefPtr<T> AsRefPtr(T* ptr) {
1138   return {ptr};
1139 }
1140 
CreateBuffer() const1141 already_AddRefed<WebGLBufferJS> ClientWebGLContext::CreateBuffer() const {
1142   const FuncScope funcScope(*this, "createBuffer");
1143   if (IsContextLost()) return nullptr;
1144 
1145   auto ret = AsRefPtr(new WebGLBufferJS(*this));
1146   Run<RPROC(CreateBuffer)>(ret->mId);
1147   return ret.forget();
1148 }
1149 
CreateFramebuffer() const1150 already_AddRefed<WebGLFramebufferJS> ClientWebGLContext::CreateFramebuffer()
1151     const {
1152   const FuncScope funcScope(*this, "createFramebuffer");
1153   if (IsContextLost()) return nullptr;
1154 
1155   auto ret = AsRefPtr(new WebGLFramebufferJS(*this));
1156   Run<RPROC(CreateFramebuffer)>(ret->mId);
1157   return ret.forget();
1158 }
1159 
1160 already_AddRefed<WebGLFramebufferJS>
CreateOpaqueFramebuffer(const webgl::OpaqueFramebufferOptions & options) const1161 ClientWebGLContext::CreateOpaqueFramebuffer(
1162     const webgl::OpaqueFramebufferOptions& options) const {
1163   const FuncScope funcScope(*this, "createOpaqueFramebuffer");
1164   if (IsContextLost()) return nullptr;
1165 
1166   auto ret = AsRefPtr(new WebGLFramebufferJS(*this, true));
1167 
1168   const auto& inProcess = mNotLost->inProcess;
1169   if (inProcess) {
1170     if (!inProcess->CreateOpaqueFramebuffer(ret->mId, options)) {
1171       ret = nullptr;
1172     }
1173     return ret.forget();
1174   }
1175   const auto& child = mNotLost->outOfProcess;
1176   child->FlushPendingCmds();
1177   bool ok = false;
1178   if (!child->SendCreateOpaqueFramebuffer(ret->mId, options, &ok))
1179     return nullptr;
1180   if (!ok) return nullptr;
1181   return ret.forget();
1182 }
1183 
CreateProgram() const1184 already_AddRefed<WebGLProgramJS> ClientWebGLContext::CreateProgram() const {
1185   const FuncScope funcScope(*this, "createProgram");
1186   if (IsContextLost()) return nullptr;
1187 
1188   auto ret = AsRefPtr(new WebGLProgramJS(*this));
1189   Run<RPROC(CreateProgram)>(ret->mId);
1190   return ret.forget();
1191 }
1192 
CreateQuery() const1193 already_AddRefed<WebGLQueryJS> ClientWebGLContext::CreateQuery() const {
1194   const FuncScope funcScope(*this, "createQuery");
1195   if (IsContextLost()) return nullptr;
1196 
1197   auto ret = AsRefPtr(new WebGLQueryJS(*this));
1198   Run<RPROC(CreateQuery)>(ret->mId);
1199   return ret.forget();
1200 }
1201 
CreateRenderbuffer() const1202 already_AddRefed<WebGLRenderbufferJS> ClientWebGLContext::CreateRenderbuffer()
1203     const {
1204   const FuncScope funcScope(*this, "createRenderbuffer");
1205   if (IsContextLost()) return nullptr;
1206 
1207   auto ret = AsRefPtr(new WebGLRenderbufferJS(*this));
1208   Run<RPROC(CreateRenderbuffer)>(ret->mId);
1209   return ret.forget();
1210 }
1211 
CreateSampler() const1212 already_AddRefed<WebGLSamplerJS> ClientWebGLContext::CreateSampler() const {
1213   const FuncScope funcScope(*this, "createSampler");
1214   if (IsContextLost()) return nullptr;
1215 
1216   auto ret = AsRefPtr(new WebGLSamplerJS(*this));
1217   Run<RPROC(CreateSampler)>(ret->mId);
1218   return ret.forget();
1219 }
1220 
CreateShader(const GLenum type) const1221 already_AddRefed<WebGLShaderJS> ClientWebGLContext::CreateShader(
1222     const GLenum type) const {
1223   const FuncScope funcScope(*this, "createShader");
1224   if (IsContextLost()) return nullptr;
1225 
1226   switch (type) {
1227     case LOCAL_GL_VERTEX_SHADER:
1228     case LOCAL_GL_FRAGMENT_SHADER:
1229       break;
1230     default:
1231       EnqueueError_ArgEnum("type", type);
1232       return nullptr;
1233   }
1234 
1235   auto ret = AsRefPtr(new WebGLShaderJS(*this, type));
1236   Run<RPROC(CreateShader)>(ret->mId, ret->mType);
1237   return ret.forget();
1238 }
1239 
FenceSync(const GLenum condition,const GLbitfield flags) const1240 already_AddRefed<WebGLSyncJS> ClientWebGLContext::FenceSync(
1241     const GLenum condition, const GLbitfield flags) const {
1242   const FuncScope funcScope(*this, "fenceSync");
1243   if (IsContextLost()) return nullptr;
1244 
1245   if (condition != LOCAL_GL_SYNC_GPU_COMMANDS_COMPLETE) {
1246     EnqueueError_ArgEnum("condition", condition);
1247     return nullptr;
1248   }
1249 
1250   if (flags) {
1251     EnqueueError(LOCAL_GL_INVALID_VALUE, "`flags` must be 0.");
1252     return nullptr;
1253   }
1254 
1255   auto ret = AsRefPtr(new WebGLSyncJS(*this));
1256   Run<RPROC(CreateSync)>(ret->mId);
1257 
1258   auto& availRunnable = EnsureAvailabilityRunnable();
1259   availRunnable.mSyncs.push_back(ret.get());
1260   ret->mCanBeAvailable = false;
1261 
1262   return ret.forget();
1263 }
1264 
CreateTexture() const1265 already_AddRefed<WebGLTextureJS> ClientWebGLContext::CreateTexture() const {
1266   const FuncScope funcScope(*this, "createTexture");
1267   if (IsContextLost()) return nullptr;
1268 
1269   auto ret = AsRefPtr(new WebGLTextureJS(*this));
1270   Run<RPROC(CreateTexture)>(ret->mId);
1271   return ret.forget();
1272 }
1273 
1274 already_AddRefed<WebGLTransformFeedbackJS>
CreateTransformFeedback() const1275 ClientWebGLContext::CreateTransformFeedback() const {
1276   const FuncScope funcScope(*this, "createTransformFeedback");
1277   if (IsContextLost()) return nullptr;
1278 
1279   auto ret = AsRefPtr(new WebGLTransformFeedbackJS(*this));
1280   Run<RPROC(CreateTransformFeedback)>(ret->mId);
1281   return ret.forget();
1282 }
1283 
CreateVertexArray() const1284 already_AddRefed<WebGLVertexArrayJS> ClientWebGLContext::CreateVertexArray()
1285     const {
1286   const FuncScope funcScope(*this, "createVertexArray");
1287   if (IsContextLost()) return nullptr;
1288 
1289   auto ret = AsRefPtr(new WebGLVertexArrayJS(*this));
1290   Run<RPROC(CreateVertexArray)>(ret->mId);
1291   return ret.forget();
1292 }
1293 
1294 // -
1295 
ValidateOrSkipForDelete(const ClientWebGLContext & context,const webgl::ObjectJS * const obj)1296 static bool ValidateOrSkipForDelete(const ClientWebGLContext& context,
1297                                     const webgl::ObjectJS* const obj) {
1298   if (!obj) return false;
1299   if (!obj->ValidateForContext(context, "obj")) return false;
1300   if (obj->IsDeleted()) return false;
1301   return true;
1302 }
1303 
DeleteBuffer(WebGLBufferJS * const obj)1304 void ClientWebGLContext::DeleteBuffer(WebGLBufferJS* const obj) {
1305   const FuncScope funcScope(*this, "deleteBuffer");
1306   if (IsContextLost()) return;
1307   if (!ValidateOrSkipForDelete(*this, obj)) return;
1308   auto& state = State();
1309 
1310   // Unbind from all bind points and bound containers
1311 
1312   // UBOs
1313   for (const auto i : IntegerRange(state.mBoundUbos.size())) {
1314     if (state.mBoundUbos[i] == obj) {
1315       BindBufferBase(LOCAL_GL_UNIFORM_BUFFER, i, nullptr);
1316     }
1317   }
1318 
1319   // TFO only if not active
1320   if (!state.mBoundTfo->mActiveOrPaused) {
1321     const auto& buffers = state.mBoundTfo->mAttribBuffers;
1322     for (const auto i : IntegerRange(buffers.size())) {
1323       if (buffers[i] == obj) {
1324         BindBufferBase(LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER, i, nullptr);
1325       }
1326     }
1327   }
1328 
1329   // Generic/global bind points
1330   for (const auto& pair : state.mBoundBufferByTarget) {
1331     if (pair.second == obj) {
1332       BindBuffer(pair.first, nullptr);
1333     }
1334   }
1335 
1336   // VAO attachments
1337   if (state.mBoundVao->mIndexBuffer == obj) {
1338     BindBuffer(LOCAL_GL_ELEMENT_ARRAY_BUFFER, nullptr);
1339   }
1340 
1341   const auto& vaoBuffers = state.mBoundVao->mAttribBuffers;
1342   Maybe<WebGLBufferJS*> toRestore;
1343   for (const auto i : IntegerRange(vaoBuffers.size())) {
1344     if (vaoBuffers[i] == obj) {
1345       if (!toRestore) {
1346         toRestore =
1347             Some(state.mBoundBufferByTarget[LOCAL_GL_ARRAY_BUFFER].get());
1348         if (*toRestore) {
1349           BindBuffer(LOCAL_GL_ARRAY_BUFFER, nullptr);
1350         }
1351       }
1352       VertexAttribPointer(i, 4, LOCAL_GL_FLOAT, false, 0, 0);
1353     }
1354   }
1355   if (toRestore && *toRestore) {
1356     BindBuffer(LOCAL_GL_ARRAY_BUFFER, *toRestore);
1357   }
1358 
1359   // -
1360 
1361   obj->mDeleteRequested = true;
1362   Run<RPROC(DeleteBuffer)>(obj->mId);
1363 }
1364 
DeleteFramebuffer(WebGLFramebufferJS * const obj,bool canDeleteOpaque)1365 void ClientWebGLContext::DeleteFramebuffer(WebGLFramebufferJS* const obj,
1366                                            bool canDeleteOpaque) {
1367   const FuncScope funcScope(*this, "deleteFramebuffer");
1368   if (IsContextLost()) return;
1369   if (!ValidateOrSkipForDelete(*this, obj)) return;
1370   if (!canDeleteOpaque && obj->mOpaque) {
1371     EnqueueError(
1372         LOCAL_GL_INVALID_OPERATION,
1373         "An opaque framebuffer's attachments cannot be inspected or changed.");
1374     return;
1375   }
1376   const auto& state = State();
1377 
1378   // Unbind
1379   const auto fnDetach = [&](const GLenum target,
1380                             const WebGLFramebufferJS* const fb) {
1381     if (obj == fb) {
1382       BindFramebuffer(target, nullptr);
1383     }
1384   };
1385   if (state.mBoundDrawFb == state.mBoundReadFb) {
1386     fnDetach(LOCAL_GL_FRAMEBUFFER, state.mBoundDrawFb.get());
1387   } else {
1388     fnDetach(LOCAL_GL_DRAW_FRAMEBUFFER, state.mBoundDrawFb.get());
1389     fnDetach(LOCAL_GL_READ_FRAMEBUFFER, state.mBoundReadFb.get());
1390   }
1391 
1392   obj->mDeleteRequested = true;
1393   Run<RPROC(DeleteFramebuffer)>(obj->mId);
1394 }
1395 
DeleteProgram(WebGLProgramJS * const obj) const1396 void ClientWebGLContext::DeleteProgram(WebGLProgramJS* const obj) const {
1397   const FuncScope funcScope(*this, "deleteProgram");
1398   if (IsContextLost()) return;
1399   if (!ValidateOrSkipForDelete(*this, obj)) return;
1400 
1401   // Don't unbind
1402 
1403   obj->mKeepAlive = nullptr;
1404 }
1405 
~ProgramKeepAlive()1406 webgl::ProgramKeepAlive::~ProgramKeepAlive() {
1407   if (!mParent) return;
1408   const auto& context = mParent->Context();
1409   if (!context) return;
1410   context->DoDeleteProgram(*mParent);
1411 }
1412 
DoDeleteProgram(WebGLProgramJS & obj) const1413 void ClientWebGLContext::DoDeleteProgram(WebGLProgramJS& obj) const {
1414   obj.mNextLink_Shaders = {};
1415   Run<RPROC(DeleteProgram)>(obj.mId);
1416 }
1417 
1418 static GLenum QuerySlotTarget(const GLenum specificTarget);
1419 
DeleteQuery(WebGLQueryJS * const obj)1420 void ClientWebGLContext::DeleteQuery(WebGLQueryJS* const obj) {
1421   const FuncScope funcScope(*this, "deleteQuery");
1422   if (IsContextLost()) return;
1423   if (!ValidateOrSkipForDelete(*this, obj)) return;
1424   const auto& state = State();
1425 
1426   // Unbind if current
1427 
1428   if (obj->mTarget) {
1429     // Despite mTarget being set, we may not have called BeginQuery on this
1430     // object. QueryCounter may also set mTarget.
1431     const auto slotTarget = QuerySlotTarget(obj->mTarget);
1432     const auto curForTarget =
1433         MaybeFind(state.mCurrentQueryByTarget, slotTarget);
1434 
1435     if (curForTarget && *curForTarget == obj) {
1436       EndQuery(obj->mTarget);
1437     }
1438   }
1439 
1440   obj->mDeleteRequested = true;
1441   Run<RPROC(DeleteQuery)>(obj->mId);
1442 }
1443 
DeleteRenderbuffer(WebGLRenderbufferJS * const obj)1444 void ClientWebGLContext::DeleteRenderbuffer(WebGLRenderbufferJS* const obj) {
1445   const FuncScope funcScope(*this, "deleteRenderbuffer");
1446   if (IsContextLost()) return;
1447   if (!ValidateOrSkipForDelete(*this, obj)) return;
1448   const auto& state = State();
1449 
1450   // Unbind
1451   if (state.mBoundRb == obj) {
1452     BindRenderbuffer(LOCAL_GL_RENDERBUFFER, nullptr);
1453   }
1454 
1455   // Unbind from bound FBs
1456   const auto fnDetach = [&](const GLenum target,
1457                             const WebGLFramebufferJS* const fb) {
1458     if (!fb) return;
1459     for (const auto& pair : fb->mAttachments) {
1460       if (pair.second.rb == obj) {
1461         FramebufferRenderbuffer(target, pair.first, LOCAL_GL_RENDERBUFFER,
1462                                 nullptr);
1463       }
1464     }
1465   };
1466   if (state.mBoundDrawFb == state.mBoundReadFb) {
1467     fnDetach(LOCAL_GL_FRAMEBUFFER, state.mBoundDrawFb.get());
1468   } else {
1469     fnDetach(LOCAL_GL_DRAW_FRAMEBUFFER, state.mBoundDrawFb.get());
1470     fnDetach(LOCAL_GL_READ_FRAMEBUFFER, state.mBoundReadFb.get());
1471   }
1472 
1473   obj->mDeleteRequested = true;
1474   Run<RPROC(DeleteRenderbuffer)>(obj->mId);
1475 }
1476 
DeleteSampler(WebGLSamplerJS * const obj)1477 void ClientWebGLContext::DeleteSampler(WebGLSamplerJS* const obj) {
1478   const FuncScope funcScope(*this, "deleteSampler");
1479   if (IsContextLost()) return;
1480   if (!ValidateOrSkipForDelete(*this, obj)) return;
1481   const auto& state = State();
1482 
1483   // Unbind
1484   for (const auto i : IntegerRange(state.mTexUnits.size())) {
1485     if (state.mTexUnits[i].sampler == obj) {
1486       BindSampler(i, nullptr);
1487     }
1488   }
1489 
1490   obj->mDeleteRequested = true;
1491   Run<RPROC(DeleteSampler)>(obj->mId);
1492 }
1493 
DeleteShader(WebGLShaderJS * const obj) const1494 void ClientWebGLContext::DeleteShader(WebGLShaderJS* const obj) const {
1495   const FuncScope funcScope(*this, "deleteShader");
1496   if (IsContextLost()) return;
1497   if (!ValidateOrSkipForDelete(*this, obj)) return;
1498 
1499   // Don't unbind
1500 
1501   obj->mKeepAlive = nullptr;
1502 }
1503 
~ShaderKeepAlive()1504 webgl::ShaderKeepAlive::~ShaderKeepAlive() {
1505   if (!mParent) return;
1506   const auto& context = mParent->Context();
1507   if (!context) return;
1508   context->DoDeleteShader(*mParent);
1509 }
1510 
DoDeleteShader(const WebGLShaderJS & obj) const1511 void ClientWebGLContext::DoDeleteShader(const WebGLShaderJS& obj) const {
1512   Run<RPROC(DeleteShader)>(obj.mId);
1513 }
1514 
DeleteSync(WebGLSyncJS * const obj) const1515 void ClientWebGLContext::DeleteSync(WebGLSyncJS* const obj) const {
1516   const FuncScope funcScope(*this, "deleteSync");
1517   if (IsContextLost()) return;
1518   if (!ValidateOrSkipForDelete(*this, obj)) return;
1519 
1520   // Nothing to unbind
1521 
1522   obj->mDeleteRequested = true;
1523   Run<RPROC(DeleteSync)>(obj->mId);
1524 }
1525 
DeleteTexture(WebGLTextureJS * const obj)1526 void ClientWebGLContext::DeleteTexture(WebGLTextureJS* const obj) {
1527   const FuncScope funcScope(*this, "deleteTexture");
1528   if (IsContextLost()) return;
1529   if (!ValidateOrSkipForDelete(*this, obj)) return;
1530   auto& state = State();
1531 
1532   // Unbind
1533   const auto& target = obj->mTarget;
1534   if (target) {
1535     // Unbind from tex units
1536     Maybe<uint32_t> restoreTexUnit;
1537     for (const auto i : IntegerRange(state.mTexUnits.size())) {
1538       if (state.mTexUnits[i].texByTarget[target] == obj) {
1539         if (!restoreTexUnit) {
1540           restoreTexUnit = Some(state.mActiveTexUnit);
1541         }
1542         ActiveTexture(LOCAL_GL_TEXTURE0 + i);
1543         BindTexture(target, nullptr);
1544       }
1545     }
1546     if (restoreTexUnit) {
1547       ActiveTexture(LOCAL_GL_TEXTURE0 + *restoreTexUnit);
1548     }
1549 
1550     // Unbind from bound FBs
1551     const auto fnDetach = [&](const GLenum target,
1552                               const WebGLFramebufferJS* const fb) {
1553       if (!fb) return;
1554       for (const auto& pair : fb->mAttachments) {
1555         if (pair.second.tex == obj) {
1556           FramebufferRenderbuffer(target, pair.first, LOCAL_GL_RENDERBUFFER,
1557                                   nullptr);
1558         }
1559       }
1560     };
1561     if (state.mBoundDrawFb == state.mBoundReadFb) {
1562       fnDetach(LOCAL_GL_FRAMEBUFFER, state.mBoundDrawFb.get());
1563     } else {
1564       fnDetach(LOCAL_GL_DRAW_FRAMEBUFFER, state.mBoundDrawFb.get());
1565       fnDetach(LOCAL_GL_READ_FRAMEBUFFER, state.mBoundReadFb.get());
1566     }
1567   }
1568 
1569   obj->mDeleteRequested = true;
1570   Run<RPROC(DeleteTexture)>(obj->mId);
1571 }
1572 
DeleteTransformFeedback(WebGLTransformFeedbackJS * const obj)1573 void ClientWebGLContext::DeleteTransformFeedback(
1574     WebGLTransformFeedbackJS* const obj) {
1575   const FuncScope funcScope(*this, "deleteTransformFeedback");
1576   if (IsContextLost()) return;
1577   if (!ValidateOrSkipForDelete(*this, obj)) return;
1578   const auto& state = State();
1579 
1580   if (obj->mActiveOrPaused) {
1581     EnqueueError(LOCAL_GL_INVALID_OPERATION,
1582                  "Transform Feedback object still active or paused.");
1583     return;
1584   }
1585 
1586   // Unbind
1587   if (state.mBoundTfo == obj) {
1588     BindTransformFeedback(LOCAL_GL_TRANSFORM_FEEDBACK, nullptr);
1589   }
1590 
1591   obj->mDeleteRequested = true;
1592   Run<RPROC(DeleteTransformFeedback)>(obj->mId);
1593 }
1594 
DeleteVertexArray(WebGLVertexArrayJS * const obj)1595 void ClientWebGLContext::DeleteVertexArray(WebGLVertexArrayJS* const obj) {
1596   const FuncScope funcScope(*this, "deleteVertexArray");
1597   if (IsContextLost()) return;
1598   if (!ValidateOrSkipForDelete(*this, obj)) return;
1599   const auto& state = State();
1600 
1601   // Unbind
1602   if (state.mBoundVao == obj) {
1603     BindVertexArray(nullptr);
1604   }
1605 
1606   obj->mDeleteRequested = true;
1607   Run<RPROC(DeleteVertexArray)>(obj->mId);
1608 }
1609 
1610 // -
1611 
IsBuffer(const WebGLBufferJS * const obj) const1612 bool ClientWebGLContext::IsBuffer(const WebGLBufferJS* const obj) const {
1613   const FuncScope funcScope(*this, "isBuffer");
1614   if (IsContextLost()) return false;
1615 
1616   return obj && obj->IsUsable(*this) &&
1617          obj->mKind != webgl::BufferKind::Undefined;
1618 }
1619 
IsFramebuffer(const WebGLFramebufferJS * const obj) const1620 bool ClientWebGLContext::IsFramebuffer(
1621     const WebGLFramebufferJS* const obj) const {
1622   const FuncScope funcScope(*this, "isFramebuffer");
1623   if (IsContextLost()) return false;
1624 
1625   return obj && obj->IsUsable(*this) && obj->mHasBeenBound;
1626 }
1627 
IsProgram(const WebGLProgramJS * const obj) const1628 bool ClientWebGLContext::IsProgram(const WebGLProgramJS* const obj) const {
1629   const FuncScope funcScope(*this, "isProgram");
1630   if (IsContextLost()) return false;
1631 
1632   return obj && obj->IsUsable(*this);
1633 }
1634 
IsQuery(const WebGLQueryJS * const obj) const1635 bool ClientWebGLContext::IsQuery(const WebGLQueryJS* const obj) const {
1636   const FuncScope funcScope(*this, "isQuery");
1637   if (IsContextLost()) return false;
1638 
1639   return obj && obj->IsUsable(*this) && obj->mTarget;
1640 }
1641 
IsRenderbuffer(const WebGLRenderbufferJS * const obj) const1642 bool ClientWebGLContext::IsRenderbuffer(
1643     const WebGLRenderbufferJS* const obj) const {
1644   const FuncScope funcScope(*this, "isRenderbuffer");
1645   if (IsContextLost()) return false;
1646 
1647   return obj && obj->IsUsable(*this) && obj->mHasBeenBound;
1648 }
1649 
IsSampler(const WebGLSamplerJS * const obj) const1650 bool ClientWebGLContext::IsSampler(const WebGLSamplerJS* const obj) const {
1651   const FuncScope funcScope(*this, "isSampler");
1652   if (IsContextLost()) return false;
1653 
1654   return obj && obj->IsUsable(*this);
1655 }
1656 
IsShader(const WebGLShaderJS * const obj) const1657 bool ClientWebGLContext::IsShader(const WebGLShaderJS* const obj) const {
1658   const FuncScope funcScope(*this, "isShader");
1659   if (IsContextLost()) return false;
1660 
1661   return obj && obj->IsUsable(*this);
1662 }
1663 
IsSync(const WebGLSyncJS * const obj) const1664 bool ClientWebGLContext::IsSync(const WebGLSyncJS* const obj) const {
1665   const FuncScope funcScope(*this, "isSync");
1666   if (IsContextLost()) return false;
1667 
1668   return obj && obj->IsUsable(*this);
1669 }
1670 
IsTexture(const WebGLTextureJS * const obj) const1671 bool ClientWebGLContext::IsTexture(const WebGLTextureJS* const obj) const {
1672   const FuncScope funcScope(*this, "isTexture");
1673   if (IsContextLost()) return false;
1674 
1675   return obj && obj->IsUsable(*this) && obj->mTarget;
1676 }
1677 
IsTransformFeedback(const WebGLTransformFeedbackJS * const obj) const1678 bool ClientWebGLContext::IsTransformFeedback(
1679     const WebGLTransformFeedbackJS* const obj) const {
1680   const FuncScope funcScope(*this, "isTransformFeedback");
1681   if (IsContextLost()) return false;
1682 
1683   return obj && obj->IsUsable(*this) && obj->mHasBeenBound;
1684 }
1685 
IsVertexArray(const WebGLVertexArrayJS * const obj) const1686 bool ClientWebGLContext::IsVertexArray(
1687     const WebGLVertexArrayJS* const obj) const {
1688   const FuncScope funcScope(*this, "isVertexArray");
1689   if (IsContextLost()) return false;
1690 
1691   return obj && obj->IsUsable(*this) && obj->mHasBeenBound;
1692 }
1693 
1694 // ------------------------- GL State -------------------------
1695 
SetEnabledI(GLenum cap,Maybe<GLuint> i,bool val) const1696 void ClientWebGLContext::SetEnabledI(GLenum cap, Maybe<GLuint> i,
1697                                      bool val) const {
1698   Run<RPROC(SetEnabled)>(cap, i, val);
1699 }
1700 
IsEnabled(GLenum cap) const1701 bool ClientWebGLContext::IsEnabled(GLenum cap) const {
1702   const FuncScope funcScope(*this, "isEnabled");
1703   const auto notLost = mNotLost;
1704   if (IsContextLost()) return false;
1705 
1706   const auto& inProcess = notLost->inProcess;
1707   if (inProcess) {
1708     return inProcess->IsEnabled(cap);
1709   }
1710   const auto& child = notLost->outOfProcess;
1711   child->FlushPendingCmds();
1712   bool ret = {};
1713   if (!child->SendIsEnabled(cap, &ret)) return false;
1714   return ret;
1715 }
1716 
GetInternalformatParameter(JSContext * cx,GLenum target,GLenum internalformat,GLenum pname,JS::MutableHandle<JS::Value> retval,ErrorResult & rv)1717 void ClientWebGLContext::GetInternalformatParameter(
1718     JSContext* cx, GLenum target, GLenum internalformat, GLenum pname,
1719     JS::MutableHandle<JS::Value> retval, ErrorResult& rv) {
1720   const FuncScope funcScope(*this, "getInternalformatParameter");
1721   retval.set(JS::NullValue());
1722   const auto notLost =
1723       mNotLost;  // Hold a strong-ref to prevent LoseContext=>UAF.
1724   if (IsContextLost()) return;
1725 
1726   const auto& inProcessContext = notLost->inProcess;
1727   Maybe<std::vector<int32_t>> maybe;
1728   if (inProcessContext) {
1729     maybe = inProcessContext->GetInternalformatParameter(target, internalformat,
1730                                                          pname);
1731   } else {
1732     const auto& child = notLost->outOfProcess;
1733     child->FlushPendingCmds();
1734     if (!child->SendGetInternalformatParameter(target, internalformat, pname,
1735                                                &maybe)) {
1736       return;
1737     }
1738   }
1739 
1740   if (!maybe) {
1741     return;
1742   }
1743   // zero-length array indicates out-of-memory
1744   JSObject* obj =
1745       dom::Int32Array::Create(cx, this, maybe->size(), maybe->data());
1746   if (!obj) {
1747     rv = NS_ERROR_OUT_OF_MEMORY;
1748   }
1749   retval.setObjectOrNull(obj);
1750 }
1751 
StringValue(JSContext * cx,const std::string & str,ErrorResult & er)1752 static JS::Value StringValue(JSContext* cx, const std::string& str,
1753                              ErrorResult& er) {
1754   JSString* jsStr = JS_NewStringCopyN(cx, str.data(), str.size());
1755   if (!jsStr) {
1756     er.Throw(NS_ERROR_OUT_OF_MEMORY);
1757     return JS::NullValue();
1758   }
1759 
1760   return JS::StringValue(jsStr);
1761 }
1762 
1763 template <typename T>
ToJSValueOrNull(JSContext * const cx,const RefPtr<T> & ptr,JS::MutableHandle<JS::Value> retval)1764 bool ToJSValueOrNull(JSContext* const cx, const RefPtr<T>& ptr,
1765                      JS::MutableHandle<JS::Value> retval) {
1766   if (!ptr) {
1767     retval.set(JS::NullValue());
1768     return true;
1769   }
1770   return dom::ToJSValue(cx, ptr, retval);
1771 }
1772 
1773 template <typename T, typename U, typename S>
CreateAs(JSContext * cx,nsWrapperCache * creator,const S & src,ErrorResult & rv)1774 static JS::Value CreateAs(JSContext* cx, nsWrapperCache* creator, const S& src,
1775                           ErrorResult& rv) {
1776   const auto obj =
1777       T::Create(cx, creator, src.size(), reinterpret_cast<U>(src.data()));
1778   if (!obj) {
1779     rv = NS_ERROR_OUT_OF_MEMORY;
1780   }
1781   return JS::ObjectOrNullValue(obj);
1782 }
1783 
1784 template <typename T, typename S>
Create(JSContext * cx,nsWrapperCache * creator,const S & src,ErrorResult & rv)1785 static JS::Value Create(JSContext* cx, nsWrapperCache* creator, const S& src,
1786                         ErrorResult& rv) {
1787   return CreateAs<T, decltype(&src[0]), S>(cx, creator, src, rv);
1788 }
1789 
GetNumber(const GLenum pname)1790 Maybe<double> ClientWebGLContext::GetNumber(const GLenum pname) {
1791   MOZ_ASSERT(!IsContextLost());
1792 
1793   const auto& inProcess = mNotLost->inProcess;
1794   if (inProcess) {
1795     return inProcess->GetNumber(pname);
1796   }
1797 
1798   const auto& child = mNotLost->outOfProcess;
1799   child->FlushPendingCmds();
1800 
1801   Maybe<double> ret;
1802   if (!child->SendGetNumber(pname, &ret)) {
1803     ret.reset();
1804   }
1805   return ret;
1806 }
1807 
GetString(const GLenum pname)1808 Maybe<std::string> ClientWebGLContext::GetString(const GLenum pname) {
1809   MOZ_ASSERT(!IsContextLost());
1810 
1811   const auto& inProcess = mNotLost->inProcess;
1812   if (inProcess) {
1813     return inProcess->GetString(pname);
1814   }
1815 
1816   const auto& child = mNotLost->outOfProcess;
1817   child->FlushPendingCmds();
1818 
1819   Maybe<std::string> ret;
1820   if (!child->SendGetString(pname, &ret)) {
1821     ret.reset();
1822   }
1823   return ret;
1824 }
1825 
GetParameter(JSContext * cx,GLenum pname,JS::MutableHandle<JS::Value> retval,ErrorResult & rv,const bool debug)1826 void ClientWebGLContext::GetParameter(JSContext* cx, GLenum pname,
1827                                       JS::MutableHandle<JS::Value> retval,
1828                                       ErrorResult& rv, const bool debug) {
1829   retval.set(JS::NullValue());
1830   const FuncScope funcScope(*this, "getParameter");
1831   if (IsContextLost()) return;
1832   const auto& limits = Limits();
1833   const auto& state = State();
1834 
1835   // -
1836 
1837   const auto fnSetRetval_Buffer = [&](const GLenum target) {
1838     const auto buffer = *MaybeFind(state.mBoundBufferByTarget, target);
1839     (void)ToJSValueOrNull(cx, buffer, retval);
1840   };
1841   const auto fnSetRetval_Tex = [&](const GLenum texTarget) {
1842     const auto& texUnit = state.mTexUnits[state.mActiveTexUnit];
1843     const auto tex = Find(texUnit.texByTarget, texTarget, nullptr);
1844     (void)ToJSValueOrNull(cx, tex, retval);
1845   };
1846 
1847   switch (pname) {
1848     case LOCAL_GL_ARRAY_BUFFER_BINDING:
1849       fnSetRetval_Buffer(LOCAL_GL_ARRAY_BUFFER);
1850       return;
1851 
1852     case LOCAL_GL_CURRENT_PROGRAM:
1853       (void)ToJSValueOrNull(cx, state.mCurrentProgram, retval);
1854       return;
1855 
1856     case LOCAL_GL_ELEMENT_ARRAY_BUFFER_BINDING:
1857       (void)ToJSValueOrNull(cx, state.mBoundVao->mIndexBuffer, retval);
1858       return;
1859 
1860     case LOCAL_GL_FRAMEBUFFER_BINDING:
1861       (void)ToJSValueOrNull(cx, state.mBoundDrawFb, retval);
1862       return;
1863 
1864     case LOCAL_GL_RENDERBUFFER_BINDING:
1865       (void)ToJSValueOrNull(cx, state.mBoundRb, retval);
1866       return;
1867 
1868     case LOCAL_GL_TEXTURE_BINDING_2D:
1869       fnSetRetval_Tex(LOCAL_GL_TEXTURE_2D);
1870       return;
1871 
1872     case LOCAL_GL_TEXTURE_BINDING_CUBE_MAP:
1873       fnSetRetval_Tex(LOCAL_GL_TEXTURE_CUBE_MAP);
1874       return;
1875 
1876     case LOCAL_GL_VERTEX_ARRAY_BINDING: {
1877       if (!mIsWebGL2 &&
1878           !IsExtensionEnabled(WebGLExtensionID::OES_vertex_array_object))
1879         break;
1880 
1881       auto ret = state.mBoundVao;
1882       if (ret == state.mDefaultVao) {
1883         ret = nullptr;
1884       }
1885       (void)ToJSValueOrNull(cx, ret, retval);
1886       return;
1887     }
1888 
1889     case LOCAL_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS:
1890       retval.set(JS::NumberValue(limits.maxTexUnits));
1891       return;
1892     case LOCAL_GL_MAX_TEXTURE_SIZE:
1893       retval.set(JS::NumberValue(limits.maxTex2dSize));
1894       return;
1895     case LOCAL_GL_MAX_CUBE_MAP_TEXTURE_SIZE:
1896       retval.set(JS::NumberValue(limits.maxTexCubeSize));
1897       return;
1898     case LOCAL_GL_MAX_VERTEX_ATTRIBS:
1899       retval.set(JS::NumberValue(limits.maxVertexAttribs));
1900       return;
1901 
1902     case LOCAL_GL_MAX_VIEWS_OVR:
1903       if (IsExtensionEnabled(WebGLExtensionID::OVR_multiview2)) {
1904         retval.set(JS::NumberValue(limits.maxMultiviewLayers));
1905         return;
1906       }
1907       break;
1908 
1909     case LOCAL_GL_PACK_ALIGNMENT:
1910       retval.set(JS::NumberValue(state.mPixelPackState.alignmentInTypeElems));
1911       return;
1912     case LOCAL_GL_UNPACK_ALIGNMENT:
1913       retval.set(JS::NumberValue(state.mPixelUnpackState.alignmentInTypeElems));
1914       return;
1915 
1916     case dom::WebGLRenderingContext_Binding::UNPACK_FLIP_Y_WEBGL:
1917       retval.set(JS::BooleanValue(state.mPixelUnpackState.flipY));
1918       return;
1919     case dom::WebGLRenderingContext_Binding::UNPACK_PREMULTIPLY_ALPHA_WEBGL:
1920       retval.set(JS::BooleanValue(state.mPixelUnpackState.premultiplyAlpha));
1921       return;
1922     case dom::WebGLRenderingContext_Binding::UNPACK_COLORSPACE_CONVERSION_WEBGL:
1923       retval.set(JS::NumberValue(state.mPixelUnpackState.colorspaceConversion));
1924       return;
1925 
1926     // -
1927     // Array returns
1928 
1929     // 2 floats
1930     case LOCAL_GL_DEPTH_RANGE:
1931       retval.set(Create<dom::Float32Array>(cx, this, state.mDepthRange, rv));
1932       return;
1933 
1934     case LOCAL_GL_ALIASED_POINT_SIZE_RANGE:
1935       retval.set(
1936           Create<dom::Float32Array>(cx, this, limits.pointSizeRange, rv));
1937       return;
1938 
1939     case LOCAL_GL_ALIASED_LINE_WIDTH_RANGE:
1940       retval.set(
1941           Create<dom::Float32Array>(cx, this, limits.lineWidthRange, rv));
1942       return;
1943 
1944     // 4 floats
1945     case LOCAL_GL_COLOR_CLEAR_VALUE:
1946       retval.set(Create<dom::Float32Array>(cx, this, state.mClearColor, rv));
1947       return;
1948 
1949     case LOCAL_GL_BLEND_COLOR:
1950       retval.set(Create<dom::Float32Array>(cx, this, state.mBlendColor, rv));
1951       return;
1952 
1953     // 2 ints
1954     case LOCAL_GL_MAX_VIEWPORT_DIMS: {
1955       const auto dims =
1956           std::array<uint32_t, 2>{limits.maxViewportDim, limits.maxViewportDim};
1957       retval.set(CreateAs<dom::Int32Array, const int32_t*>(cx, this, dims, rv));
1958       return;
1959     }
1960 
1961     // 4 ints
1962     case LOCAL_GL_SCISSOR_BOX:
1963       retval.set(Create<dom::Int32Array>(cx, this, state.mScissor, rv));
1964       return;
1965 
1966     case LOCAL_GL_VIEWPORT:
1967       retval.set(Create<dom::Int32Array>(cx, this, state.mViewport, rv));
1968       return;
1969 
1970     // any
1971     case LOCAL_GL_COMPRESSED_TEXTURE_FORMATS:
1972       retval.set(Create<dom::Uint32Array>(cx, this,
1973                                           state.mCompressedTextureFormats, rv));
1974       return;
1975   }
1976 
1977   if (mIsWebGL2) {
1978     switch (pname) {
1979       case LOCAL_GL_COPY_READ_BUFFER_BINDING:
1980         fnSetRetval_Buffer(LOCAL_GL_COPY_READ_BUFFER);
1981         return;
1982 
1983       case LOCAL_GL_COPY_WRITE_BUFFER_BINDING:
1984         fnSetRetval_Buffer(LOCAL_GL_COPY_WRITE_BUFFER);
1985         return;
1986 
1987       case LOCAL_GL_DRAW_FRAMEBUFFER_BINDING:
1988         (void)ToJSValueOrNull(cx, state.mBoundDrawFb, retval);
1989         return;
1990 
1991       case LOCAL_GL_PIXEL_PACK_BUFFER_BINDING:
1992         fnSetRetval_Buffer(LOCAL_GL_PIXEL_PACK_BUFFER);
1993         return;
1994 
1995       case LOCAL_GL_PIXEL_UNPACK_BUFFER_BINDING:
1996         fnSetRetval_Buffer(LOCAL_GL_PIXEL_UNPACK_BUFFER);
1997         return;
1998 
1999       case LOCAL_GL_READ_FRAMEBUFFER_BINDING:
2000         (void)ToJSValueOrNull(cx, state.mBoundReadFb, retval);
2001         return;
2002 
2003       case LOCAL_GL_SAMPLER_BINDING: {
2004         const auto& texUnit = state.mTexUnits[state.mActiveTexUnit];
2005         (void)ToJSValueOrNull(cx, texUnit.sampler, retval);
2006         return;
2007       }
2008 
2009       case LOCAL_GL_TEXTURE_BINDING_2D_ARRAY:
2010         fnSetRetval_Tex(LOCAL_GL_TEXTURE_2D_ARRAY);
2011         return;
2012 
2013       case LOCAL_GL_TEXTURE_BINDING_3D:
2014         fnSetRetval_Tex(LOCAL_GL_TEXTURE_3D);
2015         return;
2016 
2017       case LOCAL_GL_TRANSFORM_FEEDBACK_BINDING: {
2018         auto ret = state.mBoundTfo;
2019         if (ret == state.mDefaultTfo) {
2020           ret = nullptr;
2021         }
2022         (void)ToJSValueOrNull(cx, ret, retval);
2023         return;
2024       }
2025 
2026       case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_BINDING:
2027         fnSetRetval_Buffer(LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER);
2028         return;
2029 
2030       case LOCAL_GL_UNIFORM_BUFFER_BINDING:
2031         fnSetRetval_Buffer(LOCAL_GL_UNIFORM_BUFFER);
2032         return;
2033 
2034       case LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS:
2035         retval.set(
2036             JS::NumberValue(webgl::kMaxTransformFeedbackSeparateAttribs));
2037         return;
2038       case LOCAL_GL_MAX_UNIFORM_BUFFER_BINDINGS:
2039         retval.set(JS::NumberValue(limits.maxUniformBufferBindings));
2040         return;
2041       case LOCAL_GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT:
2042         retval.set(JS::NumberValue(limits.uniformBufferOffsetAlignment));
2043         return;
2044       case LOCAL_GL_MAX_3D_TEXTURE_SIZE:
2045         retval.set(JS::NumberValue(limits.maxTex3dSize));
2046         return;
2047       case LOCAL_GL_MAX_ARRAY_TEXTURE_LAYERS:
2048         retval.set(JS::NumberValue(limits.maxTexArrayLayers));
2049         return;
2050 
2051       case LOCAL_GL_PACK_ROW_LENGTH:
2052         retval.set(JS::NumberValue(state.mPixelPackState.rowLength));
2053         return;
2054       case LOCAL_GL_PACK_SKIP_PIXELS:
2055         retval.set(JS::NumberValue(state.mPixelPackState.skipPixels));
2056         return;
2057       case LOCAL_GL_PACK_SKIP_ROWS:
2058         retval.set(JS::NumberValue(state.mPixelPackState.skipRows));
2059         return;
2060 
2061       case LOCAL_GL_UNPACK_IMAGE_HEIGHT:
2062         retval.set(JS::NumberValue(state.mPixelUnpackState.imageHeight));
2063         return;
2064       case LOCAL_GL_UNPACK_ROW_LENGTH:
2065         retval.set(JS::NumberValue(state.mPixelUnpackState.rowLength));
2066         return;
2067       case LOCAL_GL_UNPACK_SKIP_IMAGES:
2068         retval.set(JS::NumberValue(state.mPixelUnpackState.skipImages));
2069         return;
2070       case LOCAL_GL_UNPACK_SKIP_PIXELS:
2071         retval.set(JS::NumberValue(state.mPixelUnpackState.skipPixels));
2072         return;
2073       case LOCAL_GL_UNPACK_SKIP_ROWS:
2074         retval.set(JS::NumberValue(state.mPixelUnpackState.skipRows));
2075         return;
2076     }  // switch pname
2077   }    // if webgl2
2078 
2079   // -
2080 
2081   if (!debug) {
2082     const auto GetPref = [&](const char* const name) {
2083       Maybe<std::string> ret;
2084       nsCString overrideVal;
2085       const auto res = Preferences::GetCString(name, overrideVal);
2086       if (NS_SUCCEEDED(res) && overrideVal.Length() > 0) {
2087         ret = Some(ToString(overrideVal));
2088       }
2089       return ret;
2090     };
2091 
2092     const auto GetUnmaskedRenderer = [&]() {
2093       auto ret = GetPref("webgl.override-unmasked-renderer");
2094       if (!ret) {
2095         ret = GetString(LOCAL_GL_RENDERER);
2096       }
2097       return ret;
2098     };
2099 
2100     const auto GetUnmaskedVendor = [&]() {
2101       auto ret = GetPref("webgl.override-unmasked-vendor");
2102       if (!ret) {
2103         ret = GetString(LOCAL_GL_VENDOR);
2104       }
2105       return ret;
2106     };
2107 
2108     // -
2109 
2110     Maybe<std::string> ret;
2111 
2112     switch (pname) {
2113       case LOCAL_GL_VENDOR:
2114         ret = Some(std::string{"Mozilla"});
2115         break;
2116 
2117       case LOCAL_GL_RENDERER: {
2118         bool allowRenderer = StaticPrefs::webgl_enable_renderer_query();
2119         if (nsContentUtils::ShouldResistFingerprinting()) {
2120           allowRenderer = false;
2121         }
2122         if (allowRenderer) {
2123           ret = GetUnmaskedRenderer();
2124           if (ret) {
2125             ret = Some(webgl::SanitizeRenderer(*ret));
2126           }
2127         }
2128         if (!ret) {
2129           ret = Some(std::string{"Mozilla"});
2130         }
2131         break;
2132       }
2133 
2134       case LOCAL_GL_VERSION:
2135         if (mIsWebGL2) {
2136           ret = Some(std::string{"WebGL 2.0"});
2137         } else {
2138           ret = Some(std::string{"WebGL 1.0"});
2139         }
2140         break;
2141 
2142       case LOCAL_GL_SHADING_LANGUAGE_VERSION:
2143         if (mIsWebGL2) {
2144           ret = Some(std::string{"WebGL GLSL ES 3.00"});
2145         } else {
2146           ret = Some(std::string{"WebGL GLSL ES 1.0"});
2147         }
2148         break;
2149 
2150       case dom::WEBGL_debug_renderer_info_Binding::UNMASKED_VENDOR_WEBGL:
2151       case dom::WEBGL_debug_renderer_info_Binding::UNMASKED_RENDERER_WEBGL: {
2152         if (!IsExtensionEnabled(WebGLExtensionID::WEBGL_debug_renderer_info)) {
2153           EnqueueError_ArgEnum("pname", pname);
2154           return;
2155         }
2156 
2157         switch (pname) {
2158           case dom::WEBGL_debug_renderer_info_Binding::UNMASKED_RENDERER_WEBGL:
2159             ret = GetUnmaskedRenderer();
2160             if (ret && StaticPrefs::webgl_sanitize_unmasked_renderer()) {
2161               *ret = webgl::SanitizeRenderer(*ret);
2162             }
2163             break;
2164 
2165           case dom::WEBGL_debug_renderer_info_Binding::UNMASKED_VENDOR_WEBGL:
2166             ret = GetUnmaskedVendor();
2167             break;
2168 
2169           default:
2170             MOZ_CRASH();
2171         }
2172         break;
2173       }
2174 
2175       default:
2176         break;
2177     }
2178 
2179     if (ret) {
2180       retval.set(StringValue(cx, *ret, rv));
2181       return;
2182     }
2183   }  // if (!debug)
2184 
2185   // -
2186 
2187   bool debugOnly = false;
2188   bool asString = false;
2189 
2190   switch (pname) {
2191     case LOCAL_GL_EXTENSIONS:
2192     case LOCAL_GL_RENDERER:
2193     case LOCAL_GL_VENDOR:
2194     case LOCAL_GL_VERSION:
2195     case dom::MOZ_debug_Binding::WSI_INFO:
2196       debugOnly = true;
2197       asString = true;
2198       break;
2199 
2200     case dom::MOZ_debug_Binding::DOES_INDEX_VALIDATION:
2201       debugOnly = true;
2202       break;
2203 
2204     default:
2205       break;
2206   }
2207 
2208   if (debugOnly && !debug) {
2209     EnqueueError_ArgEnum("pname", pname);
2210     return;
2211   }
2212 
2213   // -
2214 
2215   if (asString) {
2216     const auto maybe = GetString(pname);
2217     if (maybe) {
2218       auto str = *maybe;
2219       if (pname == dom::MOZ_debug_Binding::WSI_INFO) {
2220         nsPrintfCString more("\nIsWebglOutOfProcessEnabled: %i",
2221                              int(IsWebglOutOfProcessEnabled()));
2222         str += more.BeginReading();
2223       }
2224       retval.set(StringValue(cx, str.c_str(), rv));
2225     }
2226   } else {
2227     const auto maybe = GetNumber(pname);
2228     if (maybe) {
2229       switch (pname) {
2230         // WebGL 1:
2231         case LOCAL_GL_BLEND:
2232         case LOCAL_GL_CULL_FACE:
2233         case LOCAL_GL_DEPTH_TEST:
2234         case LOCAL_GL_DEPTH_WRITEMASK:
2235         case LOCAL_GL_DITHER:
2236         case LOCAL_GL_POLYGON_OFFSET_FILL:
2237         case LOCAL_GL_SAMPLE_ALPHA_TO_COVERAGE:
2238         case LOCAL_GL_SAMPLE_COVERAGE:
2239         case LOCAL_GL_SAMPLE_COVERAGE_INVERT:
2240         case LOCAL_GL_SCISSOR_TEST:
2241         case LOCAL_GL_STENCIL_TEST:
2242         // WebGL 2:
2243         case LOCAL_GL_RASTERIZER_DISCARD:
2244         case LOCAL_GL_TRANSFORM_FEEDBACK_ACTIVE:
2245         case LOCAL_GL_TRANSFORM_FEEDBACK_PAUSED:
2246           retval.set(JS::BooleanValue(*maybe));
2247           break;
2248 
2249         // 4 bools
2250         case LOCAL_GL_COLOR_WRITEMASK: {
2251           const auto mask = uint8_t(*maybe);
2252           const auto bs = std::bitset<4>(mask);
2253           const auto src = std::array<bool, 4>{bs[0], bs[1], bs[2], bs[3]};
2254           JS::Rooted<JS::Value> arr(cx);
2255           if (!dom::ToJSValue(cx, src.data(), src.size(), &arr)) {
2256             rv = NS_ERROR_OUT_OF_MEMORY;
2257           }
2258           retval.set(arr);
2259           return;
2260         }
2261 
2262         default:
2263           retval.set(JS::NumberValue(*maybe));
2264           break;
2265       }
2266     }
2267   }
2268 }
2269 
GetBufferParameter(JSContext * cx,GLenum target,GLenum pname,JS::MutableHandle<JS::Value> retval) const2270 void ClientWebGLContext::GetBufferParameter(
2271     JSContext* cx, GLenum target, GLenum pname,
2272     JS::MutableHandle<JS::Value> retval) const {
2273   retval.set(JS::NullValue());
2274   if (IsContextLost()) return;
2275 
2276   const auto maybe = [&]() {
2277     const auto& inProcess = mNotLost->inProcess;
2278     if (inProcess) {
2279       return inProcess->GetBufferParameter(target, pname);
2280     }
2281     const auto& child = mNotLost->outOfProcess;
2282     child->FlushPendingCmds();
2283     Maybe<double> ret;
2284     if (!child->SendGetBufferParameter(target, pname, &ret)) {
2285       ret.reset();
2286     }
2287     return ret;
2288   }();
2289   if (maybe) {
2290     retval.set(JS::NumberValue(*maybe));
2291   }
2292 }
2293 
IsFramebufferTarget(const bool isWebgl2,const GLenum target)2294 bool IsFramebufferTarget(const bool isWebgl2, const GLenum target) {
2295   switch (target) {
2296     case LOCAL_GL_FRAMEBUFFER:
2297       return true;
2298 
2299     case LOCAL_GL_DRAW_FRAMEBUFFER:
2300     case LOCAL_GL_READ_FRAMEBUFFER:
2301       return isWebgl2;
2302 
2303     default:
2304       return false;
2305   }
2306 }
2307 
GetFramebufferAttachmentParameter(JSContext * const cx,const GLenum target,const GLenum attachment,const GLenum pname,JS::MutableHandle<JS::Value> retval,ErrorResult & rv) const2308 void ClientWebGLContext::GetFramebufferAttachmentParameter(
2309     JSContext* const cx, const GLenum target, const GLenum attachment,
2310     const GLenum pname, JS::MutableHandle<JS::Value> retval,
2311     ErrorResult& rv) const {
2312   retval.set(JS::NullValue());
2313   const FuncScope funcScope(*this, "getFramebufferAttachmentParameter");
2314   if (IsContextLost()) return;
2315 
2316   const auto& state = State();
2317 
2318   if (!IsFramebufferTarget(mIsWebGL2, target)) {
2319     EnqueueError_ArgEnum("target", target);
2320     return;
2321   }
2322   auto fb = state.mBoundDrawFb;
2323   if (target == LOCAL_GL_READ_FRAMEBUFFER) {
2324     fb = state.mBoundReadFb;
2325   }
2326 
2327   const auto fnGet = [&](const GLenum pname) {
2328     const auto fbId = fb ? fb->mId : 0;
2329 
2330     const auto& inProcess = mNotLost->inProcess;
2331     if (inProcess) {
2332       return inProcess->GetFramebufferAttachmentParameter(fbId, attachment,
2333                                                           pname);
2334     }
2335     const auto& child = mNotLost->outOfProcess;
2336     child->FlushPendingCmds();
2337     Maybe<double> ret;
2338     if (!child->SendGetFramebufferAttachmentParameter(fbId, attachment, pname,
2339                                                       &ret)) {
2340       ret.reset();
2341     }
2342     return ret;
2343   };
2344 
2345   if (fb) {
2346     if (fb->mOpaque) {
2347       EnqueueError(LOCAL_GL_INVALID_OPERATION,
2348                    "An opaque framebuffer's attachments cannot be inspected or "
2349                    "changed.");
2350       return;
2351     }
2352     auto attachmentSlotEnum = attachment;
2353     if (mIsWebGL2 && attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
2354       // In webgl2, DEPTH_STENCIL is valid iff the DEPTH and STENCIL images
2355       // match, so check if the server errors.
2356       const auto maybe = fnGet(LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE);
2357       if (!maybe) return;
2358       attachmentSlotEnum = LOCAL_GL_DEPTH_ATTACHMENT;
2359     }
2360 
2361     const auto maybeSlot = fb->GetAttachment(attachmentSlotEnum);
2362     if (!maybeSlot) {
2363       EnqueueError_ArgEnum("attachment", attachment);
2364       return;
2365     }
2366     const auto& attached = *maybeSlot;
2367 
2368     // -
2369 
2370     if (pname == LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME) {
2371       if (attached.rb) {
2372         (void)ToJSValueOrNull(cx, attached.rb, retval);
2373       } else {
2374         if (!mIsWebGL2 && !attached.tex) {
2375           EnqueueError_ArgEnum("pname", pname);
2376           return;
2377         }
2378         (void)ToJSValueOrNull(cx, attached.tex, retval);
2379       }
2380       return;
2381     }
2382   }
2383 
2384   const auto maybe = fnGet(pname);
2385   if (maybe) {
2386     retval.set(JS::NumberValue(*maybe));
2387   }
2388 }
2389 
GetRenderbufferParameter(JSContext * cx,GLenum target,GLenum pname,JS::MutableHandle<JS::Value> retval) const2390 void ClientWebGLContext::GetRenderbufferParameter(
2391     JSContext* cx, GLenum target, GLenum pname,
2392     JS::MutableHandle<JS::Value> retval) const {
2393   retval.set(JS::NullValue());
2394   const FuncScope funcScope(*this, "getRenderbufferParameter");
2395   if (IsContextLost()) return;
2396 
2397   if (target != LOCAL_GL_RENDERBUFFER) {
2398     EnqueueError_ArgEnum("target", target);
2399     return;
2400   }
2401 
2402   const auto& state = State();
2403   const auto& rb = state.mBoundRb;
2404   const auto rbId = rb ? rb->mId : 0;
2405   const auto maybe = [&]() {
2406     const auto& inProcess = mNotLost->inProcess;
2407     if (inProcess) {
2408       return inProcess->GetRenderbufferParameter(rbId, pname);
2409     }
2410     const auto& child = mNotLost->outOfProcess;
2411     child->FlushPendingCmds();
2412     Maybe<double> ret;
2413     if (!child->SendGetRenderbufferParameter(rbId, pname, &ret)) {
2414       ret.reset();
2415     }
2416     return ret;
2417   }();
2418   if (maybe) {
2419     retval.set(JS::NumberValue(*maybe));
2420   }
2421 }
2422 
GetIndexedParameter(JSContext * cx,GLenum target,GLuint index,JS::MutableHandle<JS::Value> retval,ErrorResult & rv) const2423 void ClientWebGLContext::GetIndexedParameter(
2424     JSContext* cx, GLenum target, GLuint index,
2425     JS::MutableHandle<JS::Value> retval, ErrorResult& rv) const {
2426   retval.set(JS::NullValue());
2427   const FuncScope funcScope(*this, "getIndexedParameter");
2428   if (IsContextLost()) return;
2429   auto keepalive = mNotLost;
2430 
2431   const auto& state = State();
2432 
2433   switch (target) {
2434     case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_BINDING: {
2435       const auto& list = state.mBoundTfo->mAttribBuffers;
2436       if (index >= list.size()) {
2437         EnqueueError(LOCAL_GL_INVALID_VALUE,
2438                      "`index` (%u) >= MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS",
2439                      index);
2440         return;
2441       }
2442       (void)ToJSValueOrNull(cx, list[index], retval);
2443       return;
2444     }
2445 
2446     case LOCAL_GL_UNIFORM_BUFFER_BINDING: {
2447       const auto& list = state.mBoundUbos;
2448       if (index >= list.size()) {
2449         EnqueueError(LOCAL_GL_INVALID_VALUE,
2450                      "`index` (%u) >= MAX_UNIFORM_BUFFER_BINDINGS", index);
2451         return;
2452       }
2453       (void)ToJSValueOrNull(cx, list[index], retval);
2454       return;
2455     }
2456   }
2457 
2458   const auto maybe = [&]() {
2459     const auto& inProcess = mNotLost->inProcess;
2460     if (inProcess) {
2461       return inProcess->GetIndexedParameter(target, index);
2462     }
2463     const auto& child = mNotLost->outOfProcess;
2464     child->FlushPendingCmds();
2465     Maybe<double> ret;
2466     if (!child->SendGetIndexedParameter(target, index, &ret)) {
2467       ret.reset();
2468     }
2469     return ret;
2470   }();
2471   if (maybe) {
2472     switch (target) {
2473       case LOCAL_GL_COLOR_WRITEMASK: {
2474         const auto bs = std::bitset<4>(*maybe);
2475         const auto src = std::array<bool, 4>{bs[0], bs[1], bs[2], bs[3]};
2476         JS::Rooted<JS::Value> arr(cx);
2477         if (!dom::ToJSValue(cx, src.data(), src.size(), &arr)) {
2478           rv = NS_ERROR_OUT_OF_MEMORY;
2479         }
2480         retval.set(arr);
2481         return;
2482       }
2483 
2484       default:
2485         retval.set(JS::NumberValue(*maybe));
2486         return;
2487     }
2488   }
2489 }
2490 
GetUniform(JSContext * const cx,const WebGLProgramJS & prog,const WebGLUniformLocationJS & loc,JS::MutableHandle<JS::Value> retval)2491 void ClientWebGLContext::GetUniform(JSContext* const cx,
2492                                     const WebGLProgramJS& prog,
2493                                     const WebGLUniformLocationJS& loc,
2494                                     JS::MutableHandle<JS::Value> retval) {
2495   retval.set(JS::NullValue());
2496   const FuncScope funcScope(*this, "getUniform");
2497   if (IsContextLost()) return;
2498   if (!prog.ValidateUsable(*this, "prog")) return;
2499   if (!loc.ValidateUsable(*this, "loc")) return;
2500 
2501   const auto& activeLinkResult = GetActiveLinkResult();
2502   if (!activeLinkResult) {
2503     EnqueueError(LOCAL_GL_INVALID_OPERATION, "No active linked Program.");
2504     return;
2505   }
2506   const auto& reqLinkInfo = loc.mParent.lock();
2507   if (reqLinkInfo.get() != activeLinkResult) {
2508     EnqueueError(LOCAL_GL_INVALID_OPERATION,
2509                  "UniformLocation is not from the current active Program.");
2510     return;
2511   }
2512 
2513   const auto res = [&]() {
2514     const auto& inProcess = mNotLost->inProcess;
2515     if (inProcess) {
2516       return inProcess->GetUniform(prog.mId, loc.mLocation);
2517     }
2518     const auto& child = mNotLost->outOfProcess;
2519     child->FlushPendingCmds();
2520     webgl::GetUniformData ret;
2521     if (!child->SendGetUniform(prog.mId, loc.mLocation, &ret)) {
2522       ret = {};
2523     }
2524     return ret;
2525   }();
2526   if (!res.type) return;
2527 
2528   const auto elemCount = ElemTypeComponents(res.type);
2529   MOZ_ASSERT(elemCount);
2530 
2531   switch (res.type) {
2532     case LOCAL_GL_BOOL:
2533       retval.set(JS::BooleanValue(res.data[0]));
2534       return;
2535 
2536     case LOCAL_GL_FLOAT: {
2537       const auto ptr = reinterpret_cast<const float*>(res.data);
2538       MOZ_ALWAYS_TRUE(dom::ToJSValue(cx, *ptr, retval));
2539       return;
2540     }
2541     case LOCAL_GL_INT: {
2542       const auto ptr = reinterpret_cast<const int32_t*>(res.data);
2543       MOZ_ALWAYS_TRUE(dom::ToJSValue(cx, *ptr, retval));
2544       return;
2545     }
2546     case LOCAL_GL_UNSIGNED_INT:
2547     case LOCAL_GL_SAMPLER_2D:
2548     case LOCAL_GL_SAMPLER_3D:
2549     case LOCAL_GL_SAMPLER_CUBE:
2550     case LOCAL_GL_SAMPLER_2D_SHADOW:
2551     case LOCAL_GL_SAMPLER_2D_ARRAY:
2552     case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
2553     case LOCAL_GL_SAMPLER_CUBE_SHADOW:
2554     case LOCAL_GL_INT_SAMPLER_2D:
2555     case LOCAL_GL_INT_SAMPLER_3D:
2556     case LOCAL_GL_INT_SAMPLER_CUBE:
2557     case LOCAL_GL_INT_SAMPLER_2D_ARRAY:
2558     case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
2559     case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
2560     case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
2561     case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: {
2562       const auto ptr = reinterpret_cast<const uint32_t*>(res.data);
2563       MOZ_ALWAYS_TRUE(dom::ToJSValue(cx, *ptr, retval));
2564       return;
2565     }
2566 
2567       // -
2568 
2569     case LOCAL_GL_BOOL_VEC2:
2570     case LOCAL_GL_BOOL_VEC3:
2571     case LOCAL_GL_BOOL_VEC4: {
2572       const auto intArr = reinterpret_cast<const int32_t*>(res.data);
2573       bool boolArr[4] = {};
2574       for (const auto i : IntegerRange(elemCount)) {
2575         boolArr[i] = bool(intArr[i]);
2576       }
2577       MOZ_ALWAYS_TRUE(dom::ToJSValue(cx, boolArr, elemCount, retval));
2578       return;
2579     }
2580 
2581     case LOCAL_GL_FLOAT_VEC2:
2582     case LOCAL_GL_FLOAT_VEC3:
2583     case LOCAL_GL_FLOAT_VEC4:
2584     case LOCAL_GL_FLOAT_MAT2:
2585     case LOCAL_GL_FLOAT_MAT3:
2586     case LOCAL_GL_FLOAT_MAT4:
2587     case LOCAL_GL_FLOAT_MAT2x3:
2588     case LOCAL_GL_FLOAT_MAT2x4:
2589     case LOCAL_GL_FLOAT_MAT3x2:
2590     case LOCAL_GL_FLOAT_MAT3x4:
2591     case LOCAL_GL_FLOAT_MAT4x2:
2592     case LOCAL_GL_FLOAT_MAT4x3: {
2593       const auto ptr = reinterpret_cast<const float*>(res.data);
2594       JSObject* obj = dom::Float32Array::Create(cx, this, elemCount, ptr);
2595       MOZ_ASSERT(obj);
2596       retval.set(JS::ObjectOrNullValue(obj));
2597       return;
2598     }
2599 
2600     case LOCAL_GL_INT_VEC2:
2601     case LOCAL_GL_INT_VEC3:
2602     case LOCAL_GL_INT_VEC4: {
2603       const auto ptr = reinterpret_cast<const int32_t*>(res.data);
2604       JSObject* obj = dom::Int32Array::Create(cx, this, elemCount, ptr);
2605       MOZ_ASSERT(obj);
2606       retval.set(JS::ObjectOrNullValue(obj));
2607       return;
2608     }
2609 
2610     case LOCAL_GL_UNSIGNED_INT_VEC2:
2611     case LOCAL_GL_UNSIGNED_INT_VEC3:
2612     case LOCAL_GL_UNSIGNED_INT_VEC4: {
2613       const auto ptr = reinterpret_cast<const uint32_t*>(res.data);
2614       JSObject* obj = dom::Uint32Array::Create(cx, this, elemCount, ptr);
2615       MOZ_ASSERT(obj);
2616       retval.set(JS::ObjectOrNullValue(obj));
2617       return;
2618     }
2619 
2620     default:
2621       MOZ_CRASH("GFX: Invalid elemType.");
2622   }
2623 }
2624 
2625 already_AddRefed<WebGLShaderPrecisionFormatJS>
GetShaderPrecisionFormat(const GLenum shadertype,const GLenum precisiontype)2626 ClientWebGLContext::GetShaderPrecisionFormat(const GLenum shadertype,
2627                                              const GLenum precisiontype) {
2628   if (IsContextLost()) return nullptr;
2629   const auto info = [&]() {
2630     const auto& inProcess = mNotLost->inProcess;
2631     if (inProcess) {
2632       return inProcess->GetShaderPrecisionFormat(shadertype, precisiontype);
2633     }
2634     const auto& child = mNotLost->outOfProcess;
2635     child->FlushPendingCmds();
2636     Maybe<webgl::ShaderPrecisionFormat> ret;
2637     if (!child->SendGetShaderPrecisionFormat(shadertype, precisiontype, &ret)) {
2638       ret.reset();
2639     }
2640     return ret;
2641   }();
2642 
2643   if (!info) return nullptr;
2644   return AsAddRefed(new WebGLShaderPrecisionFormatJS(*info));
2645 }
2646 
BlendColor(GLclampf r,GLclampf g,GLclampf b,GLclampf a)2647 void ClientWebGLContext::BlendColor(GLclampf r, GLclampf g, GLclampf b,
2648                                     GLclampf a) {
2649   const FuncScope funcScope(*this, "blendColor");
2650   if (IsContextLost()) return;
2651   auto& state = State();
2652 
2653   auto& cache = state.mBlendColor;
2654   cache[0] = r;
2655   cache[1] = g;
2656   cache[2] = b;
2657   cache[3] = a;
2658 
2659   Run<RPROC(BlendColor)>(r, g, b, a);
2660 }
2661 
BlendEquationSeparateI(Maybe<GLuint> i,GLenum modeRGB,GLenum modeAlpha)2662 void ClientWebGLContext::BlendEquationSeparateI(Maybe<GLuint> i, GLenum modeRGB,
2663                                                 GLenum modeAlpha) {
2664   Run<RPROC(BlendEquationSeparate)>(i, modeRGB, modeAlpha);
2665 }
2666 
BlendFuncSeparateI(Maybe<GLuint> i,GLenum srcRGB,GLenum dstRGB,GLenum srcAlpha,GLenum dstAlpha)2667 void ClientWebGLContext::BlendFuncSeparateI(Maybe<GLuint> i, GLenum srcRGB,
2668                                             GLenum dstRGB, GLenum srcAlpha,
2669                                             GLenum dstAlpha) {
2670   Run<RPROC(BlendFuncSeparate)>(i, srcRGB, dstRGB, srcAlpha, dstAlpha);
2671 }
2672 
CheckFramebufferStatus(GLenum target)2673 GLenum ClientWebGLContext::CheckFramebufferStatus(GLenum target) {
2674   if (IsContextLost()) return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED;
2675 
2676   const auto& inProcess = mNotLost->inProcess;
2677   if (inProcess) {
2678     return inProcess->CheckFramebufferStatus(target);
2679   }
2680   const auto& child = mNotLost->outOfProcess;
2681   child->FlushPendingCmds();
2682   GLenum ret = 0;
2683   if (!child->SendCheckFramebufferStatus(target, &ret)) {
2684     ret = 0;
2685   }
2686   return ret;
2687 }
2688 
Clear(GLbitfield mask)2689 void ClientWebGLContext::Clear(GLbitfield mask) {
2690   Run<RPROC(Clear)>(mask);
2691 
2692   AfterDrawCall();
2693 }
2694 
2695 // -
2696 
ClearBufferTv(const GLenum buffer,const GLint drawBuffer,const webgl::AttribBaseType type,const Range<const uint8_t> & view,const GLuint srcElemOffset)2697 void ClientWebGLContext::ClearBufferTv(const GLenum buffer,
2698                                        const GLint drawBuffer,
2699                                        const webgl::AttribBaseType type,
2700                                        const Range<const uint8_t>& view,
2701                                        const GLuint srcElemOffset) {
2702   const FuncScope funcScope(*this, "clearBufferu?[fi]v");
2703   if (IsContextLost()) return;
2704 
2705   const auto byteOffset = CheckedInt<size_t>(srcElemOffset) * sizeof(float);
2706   if (!byteOffset.isValid() || byteOffset.value() > view.length()) {
2707     EnqueueError(LOCAL_GL_INVALID_VALUE, "`srcOffset` too large for `values`.");
2708     return;
2709   }
2710   webgl::TypedQuad data;
2711   data.type = type;
2712 
2713   auto dataSize = sizeof(data.data);
2714   switch (buffer) {
2715     case LOCAL_GL_COLOR:
2716       break;
2717 
2718     case LOCAL_GL_DEPTH:
2719       dataSize = sizeof(float);
2720       break;
2721 
2722     case LOCAL_GL_STENCIL:
2723       dataSize = sizeof(int32_t);
2724       break;
2725 
2726     default:
2727       EnqueueError_ArgEnum("buffer", buffer);
2728       return;
2729   }
2730 
2731   const auto requiredBytes = byteOffset + dataSize;
2732   if (!requiredBytes.isValid() || requiredBytes.value() > view.length()) {
2733     EnqueueError(LOCAL_GL_INVALID_VALUE, "`values` too small.");
2734     return;
2735   }
2736 
2737   memcpy(data.data, view.begin().get() + byteOffset.value(), dataSize);
2738   Run<RPROC(ClearBufferTv)>(buffer, drawBuffer, data);
2739 
2740   AfterDrawCall();
2741 }
2742 
ClearBufferfi(GLenum buffer,GLint drawBuffer,GLfloat depth,GLint stencil)2743 void ClientWebGLContext::ClearBufferfi(GLenum buffer, GLint drawBuffer,
2744                                        GLfloat depth, GLint stencil) {
2745   Run<RPROC(ClearBufferfi)>(buffer, drawBuffer, depth, stencil);
2746 
2747   AfterDrawCall();
2748 }
2749 
2750 // -
2751 
ClearColor(GLclampf r,GLclampf g,GLclampf b,GLclampf a)2752 void ClientWebGLContext::ClearColor(GLclampf r, GLclampf g, GLclampf b,
2753                                     GLclampf a) {
2754   const FuncScope funcScope(*this, "clearColor");
2755   if (IsContextLost()) return;
2756   auto& state = State();
2757 
2758   auto& cache = state.mClearColor;
2759   cache[0] = r;
2760   cache[1] = g;
2761   cache[2] = b;
2762   cache[3] = a;
2763 
2764   Run<RPROC(ClearColor)>(r, g, b, a);
2765 }
2766 
ClearDepth(GLclampf v)2767 void ClientWebGLContext::ClearDepth(GLclampf v) { Run<RPROC(ClearDepth)>(v); }
2768 
ClearStencil(GLint v)2769 void ClientWebGLContext::ClearStencil(GLint v) { Run<RPROC(ClearStencil)>(v); }
2770 
ColorMaskI(Maybe<GLuint> i,bool r,bool g,bool b,bool a) const2771 void ClientWebGLContext::ColorMaskI(Maybe<GLuint> i, bool r, bool g, bool b,
2772                                     bool a) const {
2773   const FuncScope funcScope(*this, "colorMask");
2774   if (IsContextLost()) return;
2775 
2776   const uint8_t mask =
2777       uint8_t(r << 0) | uint8_t(g << 1) | uint8_t(b << 2) | uint8_t(a << 3);
2778   Run<RPROC(ColorMask)>(i, mask);
2779 }
2780 
CullFace(GLenum face)2781 void ClientWebGLContext::CullFace(GLenum face) { Run<RPROC(CullFace)>(face); }
2782 
DepthFunc(GLenum func)2783 void ClientWebGLContext::DepthFunc(GLenum func) { Run<RPROC(DepthFunc)>(func); }
2784 
DepthMask(WebGLboolean b)2785 void ClientWebGLContext::DepthMask(WebGLboolean b) { Run<RPROC(DepthMask)>(b); }
2786 
DepthRange(GLclampf zNear,GLclampf zFar)2787 void ClientWebGLContext::DepthRange(GLclampf zNear, GLclampf zFar) {
2788   const FuncScope funcScope(*this, "depthRange");
2789   if (IsContextLost()) return;
2790   auto& state = State();
2791 
2792   state.mDepthRange = {zNear, zFar};
2793 
2794   Run<RPROC(DepthRange)>(zNear, zFar);
2795 }
2796 
Flush(const bool flushGl)2797 void ClientWebGLContext::Flush(const bool flushGl) {
2798   const FuncScope funcScope(*this, "flush");
2799   const auto notLost = mNotLost;
2800   if (IsContextLost()) return;
2801 
2802   if (flushGl) {
2803     Run<RPROC(Flush)>();
2804   }
2805 
2806   if (notLost->inProcess) return;
2807   const auto& child = mNotLost->outOfProcess;
2808   child->FlushPendingCmds();
2809 }
2810 
Finish()2811 void ClientWebGLContext::Finish() {
2812   if (IsContextLost()) return;
2813 
2814   const auto& inProcess = mNotLost->inProcess;
2815   if (inProcess) {
2816     inProcess->Finish();
2817     return;
2818   }
2819   const auto& child = mNotLost->outOfProcess;
2820   child->FlushPendingCmds();
2821   (void)child->SendFinish();
2822 }
2823 
FrontFace(GLenum mode)2824 void ClientWebGLContext::FrontFace(GLenum mode) { Run<RPROC(FrontFace)>(mode); }
2825 
GetError()2826 GLenum ClientWebGLContext::GetError() {
2827   const auto notLost = mNotLost;
2828   if (mNextError) {
2829     const auto ret = mNextError;
2830     mNextError = 0;
2831     return ret;
2832   }
2833   if (IsContextLost()) return 0;
2834 
2835   const auto& inProcess = notLost->inProcess;
2836   if (inProcess) {
2837     return inProcess->GetError();
2838   }
2839   const auto& child = notLost->outOfProcess;
2840   child->FlushPendingCmds();
2841   GLenum ret = 0;
2842   if (!child->SendGetError(&ret)) {
2843     ret = 0;
2844   }
2845   return ret;
2846 }
2847 
Hint(GLenum target,GLenum mode)2848 void ClientWebGLContext::Hint(GLenum target, GLenum mode) {
2849   Run<RPROC(Hint)>(target, mode);
2850 }
2851 
LineWidth(GLfloat width)2852 void ClientWebGLContext::LineWidth(GLfloat width) {
2853   Run<RPROC(LineWidth)>(width);
2854 }
2855 
2856 Maybe<webgl::ErrorInfo> SetPixelUnpack(
2857     const bool isWebgl2, webgl::PixelUnpackStateWebgl* const unpacking,
2858     const GLenum pname, const GLint param);
2859 
PixelStorei(const GLenum pname,const GLint iparam)2860 void ClientWebGLContext::PixelStorei(const GLenum pname, const GLint iparam) {
2861   const FuncScope funcScope(*this, "pixelStorei");
2862   if (IsContextLost()) return;
2863   if (!ValidateNonNegative("param", iparam)) return;
2864   const auto param = static_cast<uint32_t>(iparam);
2865 
2866   auto& state = State();
2867   auto& packState = state.mPixelPackState;
2868   switch (pname) {
2869     case LOCAL_GL_PACK_ALIGNMENT:
2870       switch (param) {
2871         case 1:
2872         case 2:
2873         case 4:
2874         case 8:
2875           break;
2876         default:
2877           EnqueueError(LOCAL_GL_INVALID_VALUE,
2878                        "PACK_ALIGNMENT must be one of [1,2,4,8], was %i.",
2879                        iparam);
2880           return;
2881       }
2882       packState.alignmentInTypeElems = param;
2883       return;
2884 
2885     case LOCAL_GL_PACK_ROW_LENGTH:
2886       if (!mIsWebGL2) break;
2887       packState.rowLength = param;
2888       return;
2889 
2890     case LOCAL_GL_PACK_SKIP_PIXELS:
2891       if (!mIsWebGL2) break;
2892       packState.skipPixels = param;
2893       return;
2894 
2895     case LOCAL_GL_PACK_SKIP_ROWS:
2896       if (!mIsWebGL2) break;
2897       packState.skipRows = param;
2898       return;
2899 
2900     case dom::MOZ_debug_Binding::UNPACK_REQUIRE_FASTPATH:
2901       if (!IsSupported(WebGLExtensionID::MOZ_debug)) {
2902         EnqueueError_ArgEnum("pname", pname);
2903         return;
2904       }
2905       break;
2906 
2907     default:
2908       break;
2909   }
2910 
2911   const auto err =
2912       SetPixelUnpack(mIsWebGL2, &state.mPixelUnpackState, pname, iparam);
2913   if (err) {
2914     EnqueueError(*err);
2915     return;
2916   }
2917 }
2918 
PolygonOffset(GLfloat factor,GLfloat units)2919 void ClientWebGLContext::PolygonOffset(GLfloat factor, GLfloat units) {
2920   Run<RPROC(PolygonOffset)>(factor, units);
2921 }
2922 
SampleCoverage(GLclampf value,WebGLboolean invert)2923 void ClientWebGLContext::SampleCoverage(GLclampf value, WebGLboolean invert) {
2924   Run<RPROC(SampleCoverage)>(value, invert);
2925 }
2926 
Scissor(GLint x,GLint y,GLsizei width,GLsizei height)2927 void ClientWebGLContext::Scissor(GLint x, GLint y, GLsizei width,
2928                                  GLsizei height) {
2929   const FuncScope funcScope(*this, "scissor");
2930   if (IsContextLost()) return;
2931   auto& state = State();
2932 
2933   if (!ValidateNonNegative("width", width) ||
2934       !ValidateNonNegative("height", height)) {
2935     return;
2936   }
2937 
2938   state.mScissor = {x, y, width, height};
2939 
2940   Run<RPROC(Scissor)>(x, y, width, height);
2941 }
2942 
StencilFuncSeparate(GLenum face,GLenum func,GLint ref,GLuint mask)2943 void ClientWebGLContext::StencilFuncSeparate(GLenum face, GLenum func,
2944                                              GLint ref, GLuint mask) {
2945   Run<RPROC(StencilFuncSeparate)>(face, func, ref, mask);
2946 }
2947 
StencilMaskSeparate(GLenum face,GLuint mask)2948 void ClientWebGLContext::StencilMaskSeparate(GLenum face, GLuint mask) {
2949   Run<RPROC(StencilMaskSeparate)>(face, mask);
2950 }
2951 
StencilOpSeparate(GLenum face,GLenum sfail,GLenum dpfail,GLenum dppass)2952 void ClientWebGLContext::StencilOpSeparate(GLenum face, GLenum sfail,
2953                                            GLenum dpfail, GLenum dppass) {
2954   Run<RPROC(StencilOpSeparate)>(face, sfail, dpfail, dppass);
2955 }
2956 
Viewport(GLint x,GLint y,GLsizei width,GLsizei height)2957 void ClientWebGLContext::Viewport(GLint x, GLint y, GLsizei width,
2958                                   GLsizei height) {
2959   const FuncScope funcScope(*this, "viewport");
2960   if (IsContextLost()) return;
2961   auto& state = State();
2962 
2963   if (!ValidateNonNegative("width", width) ||
2964       !ValidateNonNegative("height", height)) {
2965     return;
2966   }
2967 
2968   state.mViewport = {x, y, width, height};
2969 
2970   Run<RPROC(Viewport)>(x, y, width, height);
2971 }
2972 
2973 // ------------------------- Buffer Objects -------------------------
2974 
ValidateBindBuffer(const GLenum target,const webgl::BufferKind curKind)2975 Maybe<const webgl::ErrorInfo> ValidateBindBuffer(
2976     const GLenum target, const webgl::BufferKind curKind) {
2977   if (curKind == webgl::BufferKind::Undefined) return {};
2978 
2979   auto requiredKind = webgl::BufferKind::NonIndex;
2980   switch (target) {
2981     case LOCAL_GL_COPY_READ_BUFFER:
2982     case LOCAL_GL_COPY_WRITE_BUFFER:
2983       return {};  // Always ok
2984 
2985     case LOCAL_GL_ELEMENT_ARRAY_BUFFER:
2986       requiredKind = webgl::BufferKind::Index;
2987       break;
2988 
2989     default:
2990       break;
2991   }
2992 
2993   if (curKind != requiredKind) {
2994     const auto fnKindStr = [&](const webgl::BufferKind kind) {
2995       if (kind == webgl::BufferKind::Index) return "ELEMENT_ARRAY_BUFFER";
2996       return "non-ELEMENT_ARRAY_BUFFER";
2997     };
2998     const auto info = nsPrintfCString(
2999         "Buffer previously bound to %s cannot be now bound to %s.",
3000         fnKindStr(curKind), fnKindStr(requiredKind));
3001     return Some(
3002         webgl::ErrorInfo{LOCAL_GL_INVALID_OPERATION, info.BeginReading()});
3003   }
3004 
3005   return {};
3006 }
3007 
CheckBindBufferRange(const GLenum target,const GLuint index,const bool isBuffer,const uint64_t offset,const uint64_t size,const webgl::Limits & limits)3008 Maybe<webgl::ErrorInfo> CheckBindBufferRange(
3009     const GLenum target, const GLuint index, const bool isBuffer,
3010     const uint64_t offset, const uint64_t size, const webgl::Limits& limits) {
3011   const auto fnSome = [&](const GLenum type, const nsACString& info) {
3012     return Some(webgl::ErrorInfo{type, info.BeginReading()});
3013   };
3014 
3015   switch (target) {
3016     case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
3017       if (index >= webgl::kMaxTransformFeedbackSeparateAttribs) {
3018         const auto info = nsPrintfCString(
3019             "`index` (%u) must be less than "
3020             "MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS (%u).",
3021             index, webgl::kMaxTransformFeedbackSeparateAttribs);
3022         return fnSome(LOCAL_GL_INVALID_VALUE, info);
3023       }
3024 
3025       if (isBuffer) {
3026         if (offset % 4 != 0 || size % 4 != 0) {
3027           const auto info =
3028               nsPrintfCString("`offset` (%" PRIu64 ") and `size` (%" PRIu64
3029                               ") must both be aligned to 4 for"
3030                               " TRANSFORM_FEEDBACK_BUFFER.",
3031                               offset, size);
3032           return fnSome(LOCAL_GL_INVALID_VALUE, info);
3033         }
3034       }
3035       break;
3036 
3037     case LOCAL_GL_UNIFORM_BUFFER:
3038       if (index >= limits.maxUniformBufferBindings) {
3039         const auto info = nsPrintfCString(
3040             "`index` (%u) must be less than MAX_UNIFORM_BUFFER_BINDINGS (%u).",
3041             index, limits.maxUniformBufferBindings);
3042         return fnSome(LOCAL_GL_INVALID_VALUE, info);
3043       }
3044 
3045       if (isBuffer) {
3046         if (offset % limits.uniformBufferOffsetAlignment != 0) {
3047           const auto info =
3048               nsPrintfCString("`offset` (%" PRIu64
3049                               ") must be aligned to "
3050                               "UNIFORM_BUFFER_OFFSET_ALIGNMENT (%u).",
3051                               offset, limits.uniformBufferOffsetAlignment);
3052           return fnSome(LOCAL_GL_INVALID_VALUE, info);
3053         }
3054       }
3055       break;
3056 
3057     default: {
3058       const auto info =
3059           nsPrintfCString("Unrecognized `target`: 0x%04x", target);
3060       return fnSome(LOCAL_GL_INVALID_ENUM, info);
3061     }
3062   }
3063 
3064   return {};
3065 }
3066 
3067 // -
3068 
BindBuffer(const GLenum target,WebGLBufferJS * const buffer)3069 void ClientWebGLContext::BindBuffer(const GLenum target,
3070                                     WebGLBufferJS* const buffer) {
3071   const FuncScope funcScope(*this, "bindBuffer");
3072   if (IsContextLost()) return;
3073   if (buffer && !buffer->ValidateUsable(*this, "buffer")) return;
3074 
3075   // -
3076   // Check for INVALID_ENUM
3077 
3078   auto& state = State();
3079   auto* slot = &(state.mBoundVao->mIndexBuffer);
3080   if (target != LOCAL_GL_ELEMENT_ARRAY_BUFFER) {
3081     const auto itr = state.mBoundBufferByTarget.find(target);
3082     if (itr == state.mBoundBufferByTarget.end()) {
3083       EnqueueError_ArgEnum("target", target);
3084       return;
3085     }
3086     slot = &(itr->second);
3087   }
3088 
3089   // -
3090 
3091   auto kind = webgl::BufferKind::Undefined;
3092   if (buffer) {
3093     kind = buffer->mKind;
3094   }
3095   const auto err = ValidateBindBuffer(target, kind);
3096   if (err) {
3097     EnqueueError(err->type, "%s", err->info.c_str());
3098     return;
3099   }
3100 
3101   // -
3102   // Validation complete
3103 
3104   if (buffer && buffer->mKind == webgl::BufferKind::Undefined) {
3105     if (target == LOCAL_GL_ELEMENT_ARRAY_BUFFER) {
3106       buffer->mKind = webgl::BufferKind::Index;
3107     } else {
3108       buffer->mKind = webgl::BufferKind::NonIndex;
3109     }
3110   }
3111   *slot = buffer;
3112 
3113   // -
3114 
3115   Run<RPROC(BindBuffer)>(target, buffer ? buffer->mId : 0);
3116 }
3117 
3118 // -
3119 
BindBufferRangeImpl(const GLenum target,const GLuint index,WebGLBufferJS * const buffer,const uint64_t offset,const uint64_t size)3120 void ClientWebGLContext::BindBufferRangeImpl(const GLenum target,
3121                                              const GLuint index,
3122                                              WebGLBufferJS* const buffer,
3123                                              const uint64_t offset,
3124                                              const uint64_t size) {
3125   if (buffer && !buffer->ValidateUsable(*this, "buffer")) return;
3126   auto& state = State();
3127 
3128   // -
3129 
3130   const auto& limits = Limits();
3131   auto err =
3132       CheckBindBufferRange(target, index, bool(buffer), offset, size, limits);
3133   if (err) {
3134     EnqueueError(err->type, "%s", err->info.c_str());
3135     return;
3136   }
3137 
3138   // -
3139 
3140   auto kind = webgl::BufferKind::Undefined;
3141   if (buffer) {
3142     kind = buffer->mKind;
3143   }
3144   err = ValidateBindBuffer(target, kind);
3145   if (err) {
3146     EnqueueError(err->type, "%s", err->info.c_str());
3147     return;
3148   }
3149 
3150   if (target == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER) {
3151     if (state.mTfActiveAndNotPaused) {
3152       EnqueueError(LOCAL_GL_INVALID_OPERATION,
3153                    "Cannot change TRANSFORM_FEEDBACK_BUFFER while "
3154                    "TransformFeedback is active and not paused.");
3155       return;
3156     }
3157   }
3158 
3159   // -
3160   // Validation complete
3161 
3162   if (buffer && buffer->mKind == webgl::BufferKind::Undefined) {
3163     buffer->mKind = webgl::BufferKind::NonIndex;
3164   }
3165 
3166   // -
3167 
3168   switch (target) {
3169     case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
3170       state.mBoundTfo->mAttribBuffers[index] = buffer;
3171       break;
3172 
3173     case LOCAL_GL_UNIFORM_BUFFER:
3174       state.mBoundUbos[index] = buffer;
3175       break;
3176 
3177     default:
3178       MOZ_CRASH("Bad `target`");
3179   }
3180   state.mBoundBufferByTarget[target] = buffer;
3181 
3182   // -
3183 
3184   Run<RPROC(BindBufferRange)>(target, index, buffer ? buffer->mId : 0, offset,
3185                               size);
3186 }
3187 
GetBufferSubData(GLenum target,GLintptr srcByteOffset,const dom::ArrayBufferView & dstData,GLuint dstElemOffset,GLuint dstElemCountOverride)3188 void ClientWebGLContext::GetBufferSubData(GLenum target, GLintptr srcByteOffset,
3189                                           const dom::ArrayBufferView& dstData,
3190                                           GLuint dstElemOffset,
3191                                           GLuint dstElemCountOverride) {
3192   const FuncScope funcScope(*this, "getBufferSubData");
3193   if (IsContextLost()) return;
3194   const auto notLost =
3195       mNotLost;  // Hold a strong-ref to prevent LoseContext=>UAF.
3196   if (!ValidateNonNegative("srcByteOffset", srcByteOffset)) return;
3197 
3198   uint8_t* bytes;
3199   size_t byteLen;
3200   if (!ValidateArrayBufferView(dstData, dstElemOffset, dstElemCountOverride,
3201                                LOCAL_GL_INVALID_VALUE, &bytes, &byteLen)) {
3202     return;
3203   }
3204   const auto destView = Range<uint8_t>{bytes, byteLen};
3205 
3206   const auto& inProcessContext = notLost->inProcess;
3207   if (inProcessContext) {
3208     inProcessContext->GetBufferSubData(target, srcByteOffset, destView);
3209     return;
3210   }
3211 
3212   const auto& child = notLost->outOfProcess;
3213   child->FlushPendingCmds();
3214   mozilla::ipc::Shmem rawShmem;
3215   if (!child->SendGetBufferSubData(target, srcByteOffset, destView.length(),
3216                                    &rawShmem)) {
3217     return;
3218   }
3219   const webgl::RaiiShmem shmem{child, rawShmem};
3220   if (!shmem) {
3221     EnqueueError(LOCAL_GL_OUT_OF_MEMORY, "Failed to map in sub data buffer.");
3222     return;
3223   }
3224 
3225   const auto shmemView = shmem.ByteRange();
3226   MOZ_RELEASE_ASSERT(shmemView.length() == 1 + destView.length());
3227 
3228   const auto ok = bool(*(shmemView.begin().get()));
3229   const auto srcView =
3230       Range<const uint8_t>{shmemView.begin() + 1, shmemView.end()};
3231   if (ok) {
3232     Memcpy(destView.begin(), srcView.begin(), srcView.length());
3233   }
3234 }
3235 
3236 ////
3237 
BufferData(GLenum target,WebGLsizeiptr rawSize,GLenum usage)3238 void ClientWebGLContext::BufferData(GLenum target, WebGLsizeiptr rawSize,
3239                                     GLenum usage) {
3240   const FuncScope funcScope(*this, "bufferData");
3241   if (!ValidateNonNegative("size", rawSize)) return;
3242 
3243   const auto size = MaybeAs<size_t>(rawSize);
3244   if (!size) {
3245     EnqueueError(LOCAL_GL_OUT_OF_MEMORY, "`size` too large for platform.");
3246     return;
3247   }
3248 
3249   const auto data = RawBuffer<>{*size};
3250   Run<RPROC(BufferData)>(target, data, usage);
3251 }
3252 
BufferData(GLenum target,const dom::Nullable<dom::ArrayBuffer> & maybeSrc,GLenum usage)3253 void ClientWebGLContext::BufferData(
3254     GLenum target, const dom::Nullable<dom::ArrayBuffer>& maybeSrc,
3255     GLenum usage) {
3256   const FuncScope funcScope(*this, "bufferData");
3257   if (!ValidateNonNull("src", maybeSrc)) return;
3258   const auto& src = maybeSrc.Value();
3259 
3260   src.ComputeState();
3261   const auto range = Range<const uint8_t>{src.Data(), src.Length()};
3262   Run<RPROC(BufferData)>(target, RawBuffer<>(range), usage);
3263 }
3264 
BufferData(GLenum target,const dom::ArrayBufferView & src,GLenum usage,GLuint srcElemOffset,GLuint srcElemCountOverride)3265 void ClientWebGLContext::BufferData(GLenum target,
3266                                     const dom::ArrayBufferView& src,
3267                                     GLenum usage, GLuint srcElemOffset,
3268                                     GLuint srcElemCountOverride) {
3269   const FuncScope funcScope(*this, "bufferData");
3270   uint8_t* bytes;
3271   size_t byteLen;
3272   if (!ValidateArrayBufferView(src, srcElemOffset, srcElemCountOverride,
3273                                LOCAL_GL_INVALID_VALUE, &bytes, &byteLen)) {
3274     return;
3275   }
3276   const auto range = Range<const uint8_t>{bytes, byteLen};
3277   Run<RPROC(BufferData)>(target, RawBuffer<>(range), usage);
3278 }
3279 
RawBufferData(GLenum target,const Range<const uint8_t> & srcData,GLenum usage)3280 void ClientWebGLContext::RawBufferData(GLenum target,
3281                                        const Range<const uint8_t>& srcData,
3282                                        GLenum usage) {
3283   const FuncScope funcScope(*this, "bufferData");
3284 
3285   Run<RPROC(BufferData)>(target, RawBuffer<>(srcData), usage);
3286 }
3287 
3288 ////
3289 
BufferSubData(GLenum target,WebGLsizeiptr dstByteOffset,const dom::ArrayBuffer & src)3290 void ClientWebGLContext::BufferSubData(GLenum target,
3291                                        WebGLsizeiptr dstByteOffset,
3292                                        const dom::ArrayBuffer& src) {
3293   const FuncScope funcScope(*this, "bufferSubData");
3294   src.ComputeState();
3295   const auto range = Range<const uint8_t>{src.Data(), src.Length()};
3296   Run<RPROC(BufferSubData)>(target, dstByteOffset, RawBuffer<>(range));
3297 }
3298 
BufferSubData(GLenum target,WebGLsizeiptr dstByteOffset,const dom::ArrayBufferView & src,GLuint srcElemOffset,GLuint srcElemCountOverride)3299 void ClientWebGLContext::BufferSubData(GLenum target,
3300                                        WebGLsizeiptr dstByteOffset,
3301                                        const dom::ArrayBufferView& src,
3302                                        GLuint srcElemOffset,
3303                                        GLuint srcElemCountOverride) {
3304   const FuncScope funcScope(*this, "bufferSubData");
3305   uint8_t* bytes;
3306   size_t byteLen;
3307   if (!ValidateArrayBufferView(src, srcElemOffset, srcElemCountOverride,
3308                                LOCAL_GL_INVALID_VALUE, &bytes, &byteLen)) {
3309     return;
3310   }
3311   const auto range = Range<const uint8_t>{bytes, byteLen};
3312   Run<RPROC(BufferSubData)>(target, dstByteOffset, RawBuffer<>(range));
3313 }
3314 
CopyBufferSubData(GLenum readTarget,GLenum writeTarget,GLintptr readOffset,GLintptr writeOffset,GLsizeiptr size)3315 void ClientWebGLContext::CopyBufferSubData(GLenum readTarget,
3316                                            GLenum writeTarget,
3317                                            GLintptr readOffset,
3318                                            GLintptr writeOffset,
3319                                            GLsizeiptr size) {
3320   const FuncScope funcScope(*this, "copyBufferSubData");
3321   if (!ValidateNonNegative("readOffset", readOffset) ||
3322       !ValidateNonNegative("writeOffset", writeOffset) ||
3323       !ValidateNonNegative("size", size)) {
3324     return;
3325   }
3326   Run<RPROC(CopyBufferSubData)>(
3327       readTarget, writeTarget, static_cast<uint64_t>(readOffset),
3328       static_cast<uint64_t>(writeOffset), static_cast<uint64_t>(size));
3329 }
3330 
3331 // -------------------------- Framebuffer Objects --------------------------
3332 
BindFramebuffer(const GLenum target,WebGLFramebufferJS * const fb)3333 void ClientWebGLContext::BindFramebuffer(const GLenum target,
3334                                          WebGLFramebufferJS* const fb) {
3335   const FuncScope funcScope(*this, "bindFramebuffer");
3336   if (IsContextLost()) return;
3337   if (fb && !fb->ValidateUsable(*this, "fb")) return;
3338 
3339   if (!IsFramebufferTarget(mIsWebGL2, target)) {
3340     EnqueueError_ArgEnum("target", target);
3341     return;
3342   }
3343 
3344   // -
3345 
3346   auto& state = State();
3347 
3348   switch (target) {
3349     case LOCAL_GL_FRAMEBUFFER:
3350       state.mBoundDrawFb = fb;
3351       state.mBoundReadFb = fb;
3352       break;
3353 
3354     case LOCAL_GL_DRAW_FRAMEBUFFER:
3355       state.mBoundDrawFb = fb;
3356       break;
3357     case LOCAL_GL_READ_FRAMEBUFFER:
3358       state.mBoundReadFb = fb;
3359       break;
3360 
3361     default:
3362       MOZ_CRASH();
3363   }
3364 
3365   // -
3366 
3367   if (fb) {
3368     fb->mHasBeenBound = true;
3369   }
3370 
3371   Run<RPROC(BindFramebuffer)>(target, fb ? fb->mId : 0);
3372 }
3373 
3374 // -
3375 
FramebufferTexture2D(GLenum target,GLenum attachSlot,GLenum bindImageTarget,WebGLTextureJS * const tex,GLint mipLevel) const3376 void ClientWebGLContext::FramebufferTexture2D(GLenum target, GLenum attachSlot,
3377                                               GLenum bindImageTarget,
3378                                               WebGLTextureJS* const tex,
3379                                               GLint mipLevel) const {
3380   const FuncScope funcScope(*this, "framebufferTexture2D");
3381   if (IsContextLost()) return;
3382 
3383   const auto bindTexTarget = ImageToTexTarget(bindImageTarget);
3384   uint32_t zLayer = 0;
3385   switch (bindTexTarget) {
3386     case LOCAL_GL_TEXTURE_2D:
3387       break;
3388     case LOCAL_GL_TEXTURE_CUBE_MAP:
3389       zLayer = bindImageTarget - LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X;
3390       break;
3391     default:
3392       EnqueueError_ArgEnum("imageTarget", bindImageTarget);
3393       return;
3394   }
3395 
3396   if (!mIsWebGL2 &&
3397       !IsExtensionEnabled(WebGLExtensionID::OES_fbo_render_mipmap)) {
3398     if (mipLevel != 0) {
3399       EnqueueError(LOCAL_GL_INVALID_VALUE,
3400                    "mipLevel != 0 requires OES_fbo_render_mipmap.");
3401       return;
3402     }
3403   }
3404 
3405   FramebufferAttach(target, attachSlot, bindImageTarget, nullptr, tex,
3406                     static_cast<uint32_t>(mipLevel), zLayer, 0);
3407 }
3408 
CheckFramebufferAttach(const GLenum bindImageTarget,const GLenum curTexTarget,const uint32_t mipLevel,const uint32_t zLayerBase,const uint32_t zLayerCount,const webgl::Limits & limits)3409 Maybe<webgl::ErrorInfo> CheckFramebufferAttach(const GLenum bindImageTarget,
3410                                                const GLenum curTexTarget,
3411                                                const uint32_t mipLevel,
3412                                                const uint32_t zLayerBase,
3413                                                const uint32_t zLayerCount,
3414                                                const webgl::Limits& limits) {
3415   if (!curTexTarget) {
3416     return Some(
3417         webgl::ErrorInfo{LOCAL_GL_INVALID_OPERATION,
3418                          "`tex` not yet bound. Call bindTexture first."});
3419   }
3420 
3421   auto texTarget = curTexTarget;
3422   if (bindImageTarget) {
3423     // FramebufferTexture2D
3424     const auto bindTexTarget = ImageToTexTarget(bindImageTarget);
3425     if (curTexTarget != bindTexTarget) {
3426       return Some(webgl::ErrorInfo{LOCAL_GL_INVALID_OPERATION,
3427                                    "`tex` cannot be rebound to a new target."});
3428     }
3429 
3430     switch (bindTexTarget) {
3431       case LOCAL_GL_TEXTURE_2D:
3432       case LOCAL_GL_TEXTURE_CUBE_MAP:
3433         break;
3434       default:
3435         return Some(webgl::ErrorInfo{LOCAL_GL_INVALID_ENUM,
3436                                      "`tex` must have been bound to target "
3437                                      "TEXTURE_2D or TEXTURE_CUBE_MAP."});
3438     }
3439     texTarget = bindTexTarget;
3440   } else {
3441     // FramebufferTextureLayer/Multiview
3442     switch (curTexTarget) {
3443       case LOCAL_GL_TEXTURE_2D_ARRAY:
3444       case LOCAL_GL_TEXTURE_3D:
3445         break;
3446       default:
3447         return Some(webgl::ErrorInfo{LOCAL_GL_INVALID_OPERATION,
3448                                      "`tex` must have been bound to target "
3449                                      "TEXTURE_2D_ARRAY or TEXTURE_3D."});
3450     }
3451   }
3452   MOZ_ASSERT(texTarget);
3453   uint32_t maxSize;
3454   uint32_t maxZ;
3455   switch (texTarget) {
3456     case LOCAL_GL_TEXTURE_2D:
3457       maxSize = limits.maxTex2dSize;
3458       maxZ = 1;
3459       break;
3460     case LOCAL_GL_TEXTURE_CUBE_MAP:
3461       maxSize = limits.maxTexCubeSize;
3462       maxZ = 6;
3463       break;
3464     case LOCAL_GL_TEXTURE_2D_ARRAY:
3465       maxSize = limits.maxTex2dSize;
3466       maxZ = limits.maxTexArrayLayers;
3467       break;
3468     case LOCAL_GL_TEXTURE_3D:
3469       maxSize = limits.maxTex3dSize;
3470       maxZ = limits.maxTex3dSize;
3471       break;
3472     default:
3473       MOZ_CRASH();
3474   }
3475   const auto maxMipLevel = FloorLog2(maxSize);
3476   if (mipLevel > maxMipLevel) {
3477     return Some(webgl::ErrorInfo{LOCAL_GL_INVALID_VALUE,
3478                                  "`mipLevel` too large for texture target."});
3479   }
3480   const auto requiredZLayers = CheckedInt<uint32_t>(zLayerBase) + zLayerCount;
3481   if (!requiredZLayers.isValid() || requiredZLayers.value() > maxZ) {
3482     return Some(webgl::ErrorInfo{LOCAL_GL_INVALID_VALUE,
3483                                  "`zLayer` too large for texture target."});
3484   }
3485 
3486   return {};
3487 }
3488 
FramebufferAttach(const GLenum target,const GLenum attachSlot,const GLenum bindImageTarget,WebGLRenderbufferJS * const rb,WebGLTextureJS * const tex,const uint32_t mipLevel,const uint32_t zLayerBase,const uint32_t numViewLayers) const3489 void ClientWebGLContext::FramebufferAttach(
3490     const GLenum target, const GLenum attachSlot, const GLenum bindImageTarget,
3491     WebGLRenderbufferJS* const rb, WebGLTextureJS* const tex,
3492     const uint32_t mipLevel, const uint32_t zLayerBase,
3493     const uint32_t numViewLayers) const {
3494   if (rb && !rb->ValidateUsable(*this, "rb")) return;
3495   if (tex && !tex->ValidateUsable(*this, "tex")) return;
3496   const auto& state = State();
3497   const auto& limits = Limits();
3498 
3499   if (!IsFramebufferTarget(mIsWebGL2, target)) {
3500     EnqueueError_ArgEnum("target", target);
3501     return;
3502   }
3503   auto fb = state.mBoundDrawFb;
3504   if (target == LOCAL_GL_READ_FRAMEBUFFER) {
3505     fb = state.mBoundReadFb;
3506   }
3507   if (!fb) {
3508     EnqueueError(LOCAL_GL_INVALID_OPERATION, "No framebuffer bound.");
3509     return;
3510   }
3511 
3512   if (fb->mOpaque) {
3513     EnqueueError(
3514         LOCAL_GL_INVALID_OPERATION,
3515         "An opaque framebuffer's attachments cannot be inspected or changed.");
3516     return;
3517   }
3518 
3519   // -
3520   // Multiview-specific validation skipped by Host.
3521 
3522   if (tex && numViewLayers) {
3523     if (tex->mTarget != LOCAL_GL_TEXTURE_2D_ARRAY) {
3524       EnqueueError(LOCAL_GL_INVALID_OPERATION,
3525                    "`tex` must have been bound to target TEXTURE_2D_ARRAY.");
3526       return;
3527     }
3528     if (numViewLayers > limits.maxMultiviewLayers) {
3529       EnqueueError(LOCAL_GL_INVALID_VALUE,
3530                    "`numViews` (%u) must be <= MAX_VIEWS (%u).", numViewLayers,
3531                    limits.maxMultiviewLayers);
3532       return;
3533     }
3534   }
3535 
3536   // -
3537 
3538   webgl::ObjectId id = 0;
3539   if (tex) {
3540     auto zLayerCount = numViewLayers;
3541     if (!zLayerCount) {
3542       zLayerCount = 1;
3543     }
3544     const auto err =
3545         CheckFramebufferAttach(bindImageTarget, tex->mTarget, mipLevel,
3546                                zLayerBase, zLayerCount, limits);
3547     if (err) {
3548       EnqueueError(err->type, "%s", err->info.c_str());
3549       return;
3550     }
3551     id = tex->mId;
3552   } else if (rb) {
3553     if (!rb->mHasBeenBound) {
3554       EnqueueError(LOCAL_GL_INVALID_OPERATION,
3555                    "`rb` has not yet been bound with BindRenderbuffer.");
3556       return;
3557     }
3558     id = rb->mId;
3559   }
3560 
3561   // Ready!
3562   // But DEPTH_STENCIL in webgl2 is actually two slots!
3563 
3564   const auto fnAttachTo = [&](const GLenum actualAttachSlot) {
3565     const auto slot = fb->GetAttachment(actualAttachSlot);
3566     if (!slot) {
3567       EnqueueError_ArgEnum("attachment", actualAttachSlot);
3568       return;
3569     }
3570 
3571     slot->rb = rb;
3572     slot->tex = tex;
3573 
3574     Run<RPROC(FramebufferAttach)>(target, actualAttachSlot, bindImageTarget, id,
3575                                   mipLevel, zLayerBase, numViewLayers);
3576   };
3577 
3578   if (mIsWebGL2 && attachSlot == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
3579     fnAttachTo(LOCAL_GL_DEPTH_ATTACHMENT);
3580     fnAttachTo(LOCAL_GL_STENCIL_ATTACHMENT);
3581   } else {
3582     fnAttachTo(attachSlot);
3583   }
3584 
3585   if (bindImageTarget) {
3586     if (rb) {
3587       rb->mHasBeenBound = true;
3588     }
3589     if (tex) {
3590       tex->mTarget = ImageToTexTarget(bindImageTarget);
3591     }
3592   }
3593 }
3594 
3595 // -
3596 
BlitFramebuffer(GLint srcX0,GLint srcY0,GLint srcX1,GLint srcY1,GLint dstX0,GLint dstY0,GLint dstX1,GLint dstY1,GLbitfield mask,GLenum filter)3597 void ClientWebGLContext::BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1,
3598                                          GLint srcY1, GLint dstX0, GLint dstY0,
3599                                          GLint dstX1, GLint dstY1,
3600                                          GLbitfield mask, GLenum filter) {
3601   Run<RPROC(BlitFramebuffer)>(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1,
3602                               dstY1, mask, filter);
3603 
3604   AfterDrawCall();
3605 }
3606 
InvalidateFramebuffer(GLenum target,const dom::Sequence<GLenum> & attachments,ErrorResult & unused)3607 void ClientWebGLContext::InvalidateFramebuffer(
3608     GLenum target, const dom::Sequence<GLenum>& attachments,
3609     ErrorResult& unused) {
3610   const auto range = MakeRange(attachments);
3611   const auto& buffer = RawBufferView(range);
3612   Run<RPROC(InvalidateFramebuffer)>(target, buffer);
3613 
3614   // Never invalidate the backbuffer, so never needs AfterDrawCall.
3615 }
3616 
InvalidateSubFramebuffer(GLenum target,const dom::Sequence<GLenum> & attachments,GLint x,GLint y,GLsizei width,GLsizei height,ErrorResult & unused)3617 void ClientWebGLContext::InvalidateSubFramebuffer(
3618     GLenum target, const dom::Sequence<GLenum>& attachments, GLint x, GLint y,
3619     GLsizei width, GLsizei height, ErrorResult& unused) {
3620   const auto range = MakeRange(attachments);
3621   const auto& buffer = RawBufferView(range);
3622   Run<RPROC(InvalidateSubFramebuffer)>(target, buffer, x, y, width, height);
3623 
3624   // Never invalidate the backbuffer, so never needs AfterDrawCall.
3625 }
3626 
ReadBuffer(GLenum mode)3627 void ClientWebGLContext::ReadBuffer(GLenum mode) {
3628   Run<RPROC(ReadBuffer)>(mode);
3629 }
3630 
3631 // ----------------------- Renderbuffer objects -----------------------
3632 
BindRenderbuffer(const GLenum target,WebGLRenderbufferJS * const rb)3633 void ClientWebGLContext::BindRenderbuffer(const GLenum target,
3634                                           WebGLRenderbufferJS* const rb) {
3635   const FuncScope funcScope(*this, "bindRenderbuffer");
3636   if (IsContextLost()) return;
3637   if (rb && !rb->ValidateUsable(*this, "rb")) return;
3638   auto& state = State();
3639 
3640   if (target != LOCAL_GL_RENDERBUFFER) {
3641     EnqueueError_ArgEnum("target", target);
3642     return;
3643   }
3644 
3645   state.mBoundRb = rb;
3646   if (rb) {
3647     rb->mHasBeenBound = true;
3648   }
3649 }
3650 
RenderbufferStorageMultisample(GLenum target,GLsizei samples,GLenum internalFormat,GLsizei width,GLsizei height) const3651 void ClientWebGLContext::RenderbufferStorageMultisample(GLenum target,
3652                                                         GLsizei samples,
3653                                                         GLenum internalFormat,
3654                                                         GLsizei width,
3655                                                         GLsizei height) const {
3656   const FuncScope funcScope(*this, "renderbufferStorageMultisample");
3657   if (IsContextLost()) return;
3658 
3659   if (target != LOCAL_GL_RENDERBUFFER) {
3660     EnqueueError_ArgEnum("target", target);
3661     return;
3662   }
3663 
3664   const auto& state = State();
3665 
3666   const auto& rb = state.mBoundRb;
3667   if (!rb) {
3668     EnqueueError(LOCAL_GL_INVALID_OPERATION, "No renderbuffer bound");
3669     return;
3670   }
3671 
3672   if (!ValidateNonNegative("width", width) ||
3673       !ValidateNonNegative("height", height) ||
3674       !ValidateNonNegative("samples", samples)) {
3675     return;
3676   }
3677 
3678   if (internalFormat == LOCAL_GL_DEPTH_STENCIL && samples > 0) {
3679     // While our backend supports it trivially, the spec forbids it.
3680     EnqueueError(LOCAL_GL_INVALID_OPERATION,
3681                  "WebGL 1's DEPTH_STENCIL format may not be multisampled. Use "
3682                  "DEPTH24_STENCIL8 when `samples > 0`.");
3683     return;
3684   }
3685 
3686   Run<RPROC(RenderbufferStorageMultisample)>(
3687       rb->mId, static_cast<uint32_t>(samples), internalFormat,
3688       static_cast<uint32_t>(width), static_cast<uint32_t>(height));
3689 }
3690 
3691 // --------------------------- Texture objects ---------------------------
3692 
ActiveTexture(const GLenum texUnitEnum)3693 void ClientWebGLContext::ActiveTexture(const GLenum texUnitEnum) {
3694   const FuncScope funcScope(*this, "activeTexture");
3695   if (IsContextLost()) return;
3696 
3697   if (texUnitEnum < LOCAL_GL_TEXTURE0) {
3698     EnqueueError(LOCAL_GL_INVALID_VALUE,
3699                  "`texture` (0x%04x) must be >= TEXTURE0 (0x%04x).",
3700                  texUnitEnum, LOCAL_GL_TEXTURE0);
3701     return;
3702   }
3703 
3704   const auto texUnit = texUnitEnum - LOCAL_GL_TEXTURE0;
3705 
3706   auto& state = State();
3707   if (texUnit >= state.mTexUnits.size()) {
3708     EnqueueError(LOCAL_GL_INVALID_VALUE,
3709                  "TEXTURE%u must be < MAX_COMBINED_TEXTURE_IMAGE_UNITS (%zu).",
3710                  texUnit, state.mTexUnits.size());
3711     return;
3712   }
3713 
3714   //-
3715 
3716   state.mActiveTexUnit = texUnit;
3717   Run<RPROC(ActiveTexture)>(texUnit);
3718 }
3719 
IsTexTarget(const GLenum texTarget,const bool webgl2)3720 static bool IsTexTarget(const GLenum texTarget, const bool webgl2) {
3721   switch (texTarget) {
3722     case LOCAL_GL_TEXTURE_2D:
3723     case LOCAL_GL_TEXTURE_CUBE_MAP:
3724       return true;
3725 
3726     case LOCAL_GL_TEXTURE_2D_ARRAY:
3727     case LOCAL_GL_TEXTURE_3D:
3728       return webgl2;
3729 
3730     default:
3731       return false;
3732   }
3733 }
3734 
BindTexture(const GLenum texTarget,WebGLTextureJS * const tex)3735 void ClientWebGLContext::BindTexture(const GLenum texTarget,
3736                                      WebGLTextureJS* const tex) {
3737   const FuncScope funcScope(*this, "bindTexture");
3738   if (IsContextLost()) return;
3739   if (tex && !tex->ValidateUsable(*this, "tex")) return;
3740 
3741   if (!IsTexTarget(texTarget, mIsWebGL2)) {
3742     EnqueueError_ArgEnum("texTarget", texTarget);
3743     return;
3744   }
3745 
3746   if (tex && tex->mTarget) {
3747     if (texTarget != tex->mTarget) {
3748       EnqueueError(LOCAL_GL_INVALID_OPERATION,
3749                    "Texture previously bound to %s cannot be bound now to %s.",
3750                    EnumString(tex->mTarget).c_str(),
3751                    EnumString(texTarget).c_str());
3752       return;
3753     }
3754   }
3755 
3756   auto& state = State();
3757   auto& texUnit = state.mTexUnits[state.mActiveTexUnit];
3758   texUnit.texByTarget[texTarget] = tex;
3759   if (tex) {
3760     tex->mTarget = texTarget;
3761   }
3762 
3763   Run<RPROC(BindTexture)>(texTarget, tex ? tex->mId : 0);
3764 }
3765 
GenerateMipmap(GLenum texTarget) const3766 void ClientWebGLContext::GenerateMipmap(GLenum texTarget) const {
3767   Run<RPROC(GenerateMipmap)>(texTarget);
3768 }
3769 
GetTexParameter(JSContext * cx,GLenum texTarget,GLenum pname,JS::MutableHandle<JS::Value> retval) const3770 void ClientWebGLContext::GetTexParameter(
3771     JSContext* cx, GLenum texTarget, GLenum pname,
3772     JS::MutableHandle<JS::Value> retval) const {
3773   retval.set(JS::NullValue());
3774   const FuncScope funcScope(*this, "getTexParameter");
3775   if (IsContextLost()) return;
3776   auto& state = State();
3777 
3778   auto& texUnit = state.mTexUnits[state.mActiveTexUnit];
3779 
3780   const auto& tex = Find(texUnit.texByTarget, texTarget, nullptr);
3781   if (!tex) {
3782     if (!IsTexTarget(texTarget, mIsWebGL2)) {
3783       EnqueueError_ArgEnum("texTarget", texTarget);
3784     } else {
3785       EnqueueError(LOCAL_GL_INVALID_OPERATION, "No texture bound to %s[%u].",
3786                    EnumString(texTarget).c_str(), state.mActiveTexUnit);
3787     }
3788     return;
3789   }
3790 
3791   const auto maybe = [&]() {
3792     const auto& inProcess = mNotLost->inProcess;
3793     if (inProcess) {
3794       return inProcess->GetTexParameter(tex->mId, pname);
3795     }
3796     const auto& child = mNotLost->outOfProcess;
3797     child->FlushPendingCmds();
3798     Maybe<double> ret;
3799     if (!child->SendGetTexParameter(tex->mId, pname, &ret)) {
3800       ret.reset();
3801     }
3802     return ret;
3803   }();
3804 
3805   if (maybe) {
3806     switch (pname) {
3807       case LOCAL_GL_TEXTURE_IMMUTABLE_FORMAT:
3808         retval.set(JS::BooleanValue(*maybe));
3809         break;
3810 
3811       default:
3812         retval.set(JS::NumberValue(*maybe));
3813         break;
3814     }
3815   }
3816 }
3817 
TexParameterf(GLenum texTarget,GLenum pname,GLfloat param)3818 void ClientWebGLContext::TexParameterf(GLenum texTarget, GLenum pname,
3819                                        GLfloat param) {
3820   Run<RPROC(TexParameter_base)>(texTarget, pname, FloatOrInt(param));
3821 }
3822 
TexParameteri(GLenum texTarget,GLenum pname,GLint param)3823 void ClientWebGLContext::TexParameteri(GLenum texTarget, GLenum pname,
3824                                        GLint param) {
3825   Run<RPROC(TexParameter_base)>(texTarget, pname, FloatOrInt(param));
3826 }
3827 
3828 ////////////////////////////////////
3829 
JSTypeMatchUnpackTypeError(GLenum unpackType,js::Scalar::Type jsType)3830 static GLenum JSTypeMatchUnpackTypeError(GLenum unpackType,
3831                                          js::Scalar::Type jsType) {
3832   bool matches = false;
3833   switch (unpackType) {
3834     case LOCAL_GL_BYTE:
3835       matches = (jsType == js::Scalar::Type::Int8);
3836       break;
3837 
3838     case LOCAL_GL_UNSIGNED_BYTE:
3839       matches = (jsType == js::Scalar::Type::Uint8 ||
3840                  jsType == js::Scalar::Type::Uint8Clamped);
3841       break;
3842 
3843     case LOCAL_GL_SHORT:
3844       matches = (jsType == js::Scalar::Type::Int16);
3845       break;
3846 
3847     case LOCAL_GL_UNSIGNED_SHORT:
3848     case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
3849     case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
3850     case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
3851     case LOCAL_GL_HALF_FLOAT:
3852     case LOCAL_GL_HALF_FLOAT_OES:
3853       matches = (jsType == js::Scalar::Type::Uint16);
3854       break;
3855 
3856     case LOCAL_GL_INT:
3857       matches = (jsType == js::Scalar::Type::Int32);
3858       break;
3859 
3860     case LOCAL_GL_UNSIGNED_INT:
3861     case LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV:
3862     case LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV:
3863     case LOCAL_GL_UNSIGNED_INT_5_9_9_9_REV:
3864     case LOCAL_GL_UNSIGNED_INT_24_8:
3865       matches = (jsType == js::Scalar::Type::Uint32);
3866       break;
3867 
3868     case LOCAL_GL_FLOAT:
3869       matches = (jsType == js::Scalar::Type::Float32);
3870       break;
3871 
3872     case LOCAL_GL_FLOAT_32_UNSIGNED_INT_24_8_REV:
3873       matches = false;  // No valid jsType, but we allow uploads with null.
3874       break;
3875 
3876     default:
3877       return LOCAL_GL_INVALID_ENUM;
3878   }
3879   if (!matches) return LOCAL_GL_INVALID_OPERATION;
3880   return 0;
3881 }
3882 
3883 /////////////////////////////////////////////////
3884 
CastUvec2(const ivec2 & val)3885 static inline uvec2 CastUvec2(const ivec2& val) {
3886   return {static_cast<uint32_t>(val.x), static_cast<uint32_t>(val.y)};
3887 }
3888 
CastUvec3(const ivec3 & val)3889 static inline uvec3 CastUvec3(const ivec3& val) {
3890   return {static_cast<uint32_t>(val.x), static_cast<uint32_t>(val.y),
3891           static_cast<uint32_t>(val.z)};
3892 }
3893 
3894 template <typename T>
SubRange(const Range<T> & full,const size_t offset,const size_t length)3895 Range<T> SubRange(const Range<T>& full, const size_t offset,
3896                   const size_t length) {
3897   const auto newBegin = full.begin() + offset;
3898   return Range<T>{newBegin, newBegin + length};
3899 }
3900 
SizeOfViewElem(const dom::ArrayBufferView & view)3901 static inline size_t SizeOfViewElem(const dom::ArrayBufferView& view) {
3902   const auto& elemType = view.Type();
3903   if (elemType == js::Scalar::MaxTypedArrayViewType)  // DataViews.
3904     return 1;
3905 
3906   return js::Scalar::byteSize(elemType);
3907 }
3908 
GetRangeFromView(const dom::ArrayBufferView & view,GLuint elemOffset,GLuint elemCountOverride)3909 Maybe<Range<const uint8_t>> GetRangeFromView(const dom::ArrayBufferView& view,
3910                                              GLuint elemOffset,
3911                                              GLuint elemCountOverride) {
3912   const auto byteRange = MakeRangeAbv(view);  // In bytes.
3913   const auto bytesPerElem = SizeOfViewElem(view);
3914 
3915   auto elemCount = byteRange.length() / bytesPerElem;
3916   if (elemOffset > elemCount) return {};
3917   elemCount -= elemOffset;
3918 
3919   if (elemCountOverride) {
3920     if (elemCountOverride > elemCount) return {};
3921     elemCount = elemCountOverride;
3922   }
3923   const auto subrange =
3924       SubRange(byteRange, elemOffset * bytesPerElem, elemCount * bytesPerElem);
3925   return Some(subrange);
3926 }
3927 
3928 // -
3929 
IsTexTargetForDims(const GLenum texTarget,const bool webgl2,const uint8_t funcDims)3930 static bool IsTexTargetForDims(const GLenum texTarget, const bool webgl2,
3931                                const uint8_t funcDims) {
3932   if (!IsTexTarget(texTarget, webgl2)) return false;
3933   switch (texTarget) {
3934     case LOCAL_GL_TEXTURE_2D:
3935     case LOCAL_GL_TEXTURE_CUBE_MAP:
3936       return funcDims == 2;
3937 
3938     default:
3939       return funcDims == 3;
3940   }
3941 }
3942 
TexStorage(uint8_t funcDims,GLenum texTarget,GLsizei levels,GLenum internalFormat,const ivec3 & size) const3943 void ClientWebGLContext::TexStorage(uint8_t funcDims, GLenum texTarget,
3944                                     GLsizei levels, GLenum internalFormat,
3945                                     const ivec3& size) const {
3946   const FuncScope funcScope(*this, "texStorage[23]D");
3947   if (IsContextLost()) return;
3948   if (!IsTexTargetForDims(texTarget, mIsWebGL2, funcDims)) {
3949     EnqueueError_ArgEnum("texTarget", texTarget);
3950     return;
3951   }
3952   Run<RPROC(TexStorage)>(texTarget, static_cast<uint32_t>(levels),
3953                          internalFormat, CastUvec3(size));
3954 }
3955 
3956 namespace webgl {
3957 // TODO: Move these definitions into statics here.
3958 Maybe<webgl::TexUnpackBlobDesc> FromImageBitmap(
3959     GLenum target, Maybe<uvec3> size, const dom::ImageBitmap& imageBitmap,
3960     ErrorResult* const out_rv);
3961 
3962 webgl::TexUnpackBlobDesc FromImageData(GLenum target, Maybe<uvec3> size,
3963                                        const dom::ImageData& imageData,
3964                                        dom::Uint8ClampedArray* const scopedArr);
3965 
3966 Maybe<webgl::TexUnpackBlobDesc> FromOffscreenCanvas(
3967     const ClientWebGLContext&, GLenum target, Maybe<uvec3> size,
3968     const dom::OffscreenCanvas& src, ErrorResult* const out_error);
3969 
3970 Maybe<webgl::TexUnpackBlobDesc> FromDomElem(const ClientWebGLContext&,
3971                                             GLenum target, Maybe<uvec3> size,
3972                                             const dom::Element& src,
3973                                             ErrorResult* const out_error);
3974 }  // namespace webgl
3975 
3976 // -
3977 
Shrink(const webgl::PackingInfo & pi)3978 void webgl::TexUnpackBlobDesc::Shrink(const webgl::PackingInfo& pi) {
3979   if (cpuData) {
3980     if (!size.x || !size.y || !size.z) return;
3981 
3982     const auto unpackRes = ExplicitUnpacking(pi, {});
3983     if (!unpackRes.isOk()) {
3984       return;
3985     }
3986     const auto& unpack = unpackRes.inspect();
3987 
3988     const auto bytesUpperBound =
3989         CheckedInt<size_t>(unpack.metrics.bytesPerRowStride) *
3990         unpack.metrics.totalRows;
3991     if (bytesUpperBound.isValid()) {
3992       cpuData->Shrink(bytesUpperBound.value());
3993     }
3994   }
3995 }
3996 
3997 // -
3998 
TexImage(uint8_t funcDims,GLenum imageTarget,GLint level,GLenum respecFormat,const ivec3 & offset,const Maybe<ivec3> & isize,GLint border,const webgl::PackingInfo & pi,const TexImageSource & src) const3999 void ClientWebGLContext::TexImage(uint8_t funcDims, GLenum imageTarget,
4000                                   GLint level, GLenum respecFormat,
4001                                   const ivec3& offset,
4002                                   const Maybe<ivec3>& isize, GLint border,
4003                                   const webgl::PackingInfo& pi,
4004                                   const TexImageSource& src) const {
4005   const FuncScope funcScope(*this, "tex(Sub)Image[23]D");
4006   if (IsContextLost()) return;
4007   if (!IsTexTargetForDims(ImageToTexTarget(imageTarget), mIsWebGL2, funcDims)) {
4008     EnqueueError_ArgEnum("imageTarget", imageTarget);
4009     return;
4010   }
4011   if (border != 0) {
4012     EnqueueError(LOCAL_GL_INVALID_VALUE, "`border` must be 0.");
4013     return;
4014   }
4015 
4016   Maybe<uvec3> size;
4017   if (isize) {
4018     size = Some(CastUvec3(isize.value()));
4019   }
4020 
4021   // -
4022 
4023   // Demarcate the region within which GC is disallowed. Typed arrays can move
4024   // their data during a GC, so this will allow the rooting hazard analysis to
4025   // report if a GC is possible while any data pointers extracted from the
4026   // typed array are still live.
4027   dom::Uint8ClampedArray scopedArr;
4028   const auto reset = MakeScopeExit([&] {
4029     scopedArr.Reset();  // (For the hazard analysis) Done with the data.
4030   });
4031 
4032   // -
4033   bool isDataUpload = false;
4034   auto desc = [&]() -> Maybe<webgl::TexUnpackBlobDesc> {
4035     if (src.mPboOffset) {
4036       isDataUpload = true;
4037       const auto offset = static_cast<uint64_t>(*src.mPboOffset);
4038       return Some(webgl::TexUnpackBlobDesc{imageTarget,
4039                                            size.value(),
4040                                            gfxAlphaType::NonPremult,
4041                                            {},
4042                                            Some(offset)});
4043     }
4044 
4045     if (src.mView) {
4046       isDataUpload = true;
4047       const auto& view = *src.mView;
4048       const auto& jsType = view.Type();
4049       const auto err = JSTypeMatchUnpackTypeError(pi.type, jsType);
4050       switch (err) {
4051         case LOCAL_GL_INVALID_ENUM:
4052           EnqueueError_ArgEnum("unpackType", pi.type);
4053           return {};
4054         case LOCAL_GL_INVALID_OPERATION:
4055           EnqueueError(LOCAL_GL_INVALID_OPERATION,
4056                        "ArrayBufferView type %s not compatible with `type` %s.",
4057                        name(jsType), EnumString(pi.type).c_str());
4058           return {};
4059         default:
4060           break;
4061       }
4062 
4063       const auto range = GetRangeFromView(view, src.mViewElemOffset,
4064                                           src.mViewElemLengthOverride);
4065       if (!range) {
4066         EnqueueError(LOCAL_GL_INVALID_OPERATION, "`source` too small.");
4067         return {};
4068       }
4069       return Some(webgl::TexUnpackBlobDesc{imageTarget,
4070                                            size.value(),
4071                                            gfxAlphaType::NonPremult,
4072                                            Some(RawBuffer<>{*range}),
4073                                            {}});
4074     }
4075 
4076     if (src.mImageBitmap) {
4077       return webgl::FromImageBitmap(imageTarget, size, *(src.mImageBitmap),
4078                                     src.mOut_error);
4079     }
4080 
4081     if (src.mImageData) {
4082       return Some(webgl::FromImageData(imageTarget, size, *(src.mImageData),
4083                                        &scopedArr));
4084     }
4085 
4086     if (src.mOffscreenCanvas) {
4087       return webgl::FromOffscreenCanvas(
4088           *this, imageTarget, size, *(src.mOffscreenCanvas), src.mOut_error);
4089     }
4090 
4091     if (src.mDomElem) {
4092       return webgl::FromDomElem(*this, imageTarget, size, *(src.mDomElem),
4093                                 src.mOut_error);
4094     }
4095 
4096     return Some(webgl::TexUnpackBlobDesc{
4097         imageTarget, size.value(), gfxAlphaType::NonPremult, {}, {}});
4098   }();
4099   if (!desc) {
4100     return;
4101   }
4102 
4103   // -
4104 
4105   const auto& rawUnpacking = State().mPixelUnpackState;
4106   {
4107     auto defaultSubrectState = webgl::PixelPackingState{};
4108     defaultSubrectState.alignmentInTypeElems =
4109         rawUnpacking.alignmentInTypeElems;
4110     const bool isSubrect = (rawUnpacking != defaultSubrectState);
4111     if (isDataUpload && isSubrect) {
4112       if (rawUnpacking.flipY || rawUnpacking.premultiplyAlpha) {
4113         EnqueueError(LOCAL_GL_INVALID_OPERATION,
4114                      "Non-DOM-Element uploads with alpha-premult"
4115                      " or y-flip do not support subrect selection.");
4116         return;
4117       }
4118     }
4119   }
4120   desc->unpacking = rawUnpacking;
4121 
4122   if (desc->structuredSrcSize) {
4123     // WebGL 2 spec:
4124     //   ### 5.35 Pixel store parameters for uploads from TexImageSource
4125     //   UNPACK_ALIGNMENT and UNPACK_ROW_LENGTH are ignored.
4126     const auto& elemSize = *desc->structuredSrcSize;
4127     desc->unpacking.alignmentInTypeElems = 1;
4128     desc->unpacking.rowLength = elemSize.x;
4129   }
4130   if (!desc->unpacking.rowLength) {
4131     desc->unpacking.rowLength = desc->size.x;
4132   }
4133   if (!desc->unpacking.imageHeight) {
4134     desc->unpacking.imageHeight = desc->size.y;
4135   }
4136 
4137   // -
4138 
4139   mozilla::ipc::Shmem* pShmem = nullptr;
4140 
4141   if (desc->sd) {
4142     const auto& sd = *(desc->sd);
4143     const auto sdType = sd.type();
4144     const auto& contextInfo = mNotLost->info;
4145 
4146     const auto fallbackReason = [&]() -> Maybe<std::string> {
4147       auto fallbackReason = BlitPreventReason(level, offset, pi, *desc);
4148       if (fallbackReason) return fallbackReason;
4149 
4150       const bool canUploadViaSd = contextInfo.uploadableSdTypes[sdType];
4151       if (!canUploadViaSd) {
4152         const nsPrintfCString msg(
4153             "Fast uploads for resource type %i not implemented.", int(sdType));
4154         return Some(ToString(msg));
4155       }
4156 
4157       if (sdType == layers::SurfaceDescriptor::TSurfaceDescriptorBuffer) {
4158         const auto& sdb = sd.get_SurfaceDescriptorBuffer();
4159         const auto& data = sdb.data();
4160         if (data.type() == layers::MemoryOrShmem::TShmem) {
4161           pShmem = &data.get_Shmem();
4162         } else {
4163           return Some(
4164               std::string{"SurfaceDescriptorBuffer data is not Shmem."});
4165         }
4166       }
4167 
4168       if (StaticPrefs::webgl_disable_DOM_blit_uploads()) {
4169         return Some(std::string{"DOM blit uploads are disabled."});
4170       }
4171       return {};
4172     }();
4173 
4174     if (fallbackReason) {
4175       EnqueuePerfWarning("Missed GPU-copy fast-path: %s",
4176                          fallbackReason->c_str());
4177 
4178       const auto& image = desc->image;
4179       const RefPtr<gfx::SourceSurface> surf = image->GetAsSourceSurface();
4180       if (surf) {
4181         // WARNING: OSX can lose our MakeCurrent here.
4182         desc->dataSurf = surf->GetDataSurface();
4183       }
4184       if (!desc->dataSurf) {
4185         EnqueueError(LOCAL_GL_OUT_OF_MEMORY,
4186                      "Failed to retrieve source bytes for CPU upload.");
4187         return;
4188       }
4189       desc->sd = Nothing();
4190     }
4191   }
4192   desc->image = nullptr;
4193 
4194   desc->Shrink(pi);
4195 
4196   // -
4197 
4198   const bool doInlineUpload = !desc->sd;
4199   // Why always de-inline SDs here?
4200   // 1. This way we always send SDs down the same handling path, which
4201   // should keep things from breaking if things flip between paths because of
4202   // what we get handed by SurfaceFromElement etc.
4203   // 2. We don't actually always grab strong-refs to the resources in the SDs,
4204   // so we should try to use them sooner rather than later. Yes we should fix
4205   // this, but for now let's give the SDs the best chance of lucking out, eh?
4206   // :)
4207   // 3. It means we don't need to write QueueParamTraits<SurfaceDescriptor>.
4208   if (doInlineUpload) {
4209     // We definitely want e.g. TexImage(PBO) here.
4210     Run<RPROC(TexImage)>(static_cast<uint32_t>(level), respecFormat,
4211                          CastUvec3(offset), pi, std::move(*desc));
4212   } else {
4213     // We can't handle shmems like SurfaceDescriptorBuffer inline, so use ipdl.
4214     const auto& inProcess = mNotLost->inProcess;
4215     if (inProcess) {
4216       return inProcess->TexImage(static_cast<uint32_t>(level), respecFormat,
4217                                  CastUvec3(offset), pi, *desc);
4218     }
4219     const auto& child = mNotLost->outOfProcess;
4220     child->FlushPendingCmds();
4221 
4222     // The shmem we're handling was only shared from RDD to Content, and
4223     // immediately on Content receiving it, it was closed! RIP
4224     // Eventually we'll be able to make shmems that can traverse multiple
4225     // endpoints, but for now we need to make a new Content->WebGLParent shmem
4226     // and memcpy into it. We don't use `desc` elsewhere, so just replace the
4227     // Shmem buried within it with one that's valid for WebGLChild->Parent
4228     // transport.
4229     if (pShmem) {
4230       MOZ_ASSERT(desc->sd);
4231       const auto byteCount = pShmem->Size<uint8_t>();
4232       const auto* const src = pShmem->get<uint8_t>();
4233       const auto shmemType =
4234           mozilla::ipc::SharedMemory::SharedMemoryType::TYPE_BASIC;
4235       mozilla::ipc::Shmem shmemForResend;
4236       if (!child->AllocShmem(byteCount, shmemType, &shmemForResend)) {
4237         NS_WARNING("AllocShmem failed in TexImage");
4238         return;
4239       }
4240       auto* const dst = shmemForResend.get<uint8_t>();
4241       memcpy(dst, src, byteCount);
4242       *pShmem = shmemForResend;
4243     }
4244 
4245     (void)child->SendTexImage(static_cast<uint32_t>(level), respecFormat,
4246                               CastUvec3(offset), pi, std::move(*desc));
4247   }
4248 }
4249 
RawTexImage(uint32_t level,GLenum respecFormat,uvec3 offset,const webgl::PackingInfo & pi,const webgl::TexUnpackBlobDesc & desc) const4250 void ClientWebGLContext::RawTexImage(
4251     uint32_t level, GLenum respecFormat, uvec3 offset,
4252     const webgl::PackingInfo& pi, const webgl::TexUnpackBlobDesc& desc) const {
4253   const FuncScope funcScope(*this, "tex(Sub)Image[23]D");
4254   if (IsContextLost()) return;
4255   Run<RPROC(TexImage)>(level, respecFormat, offset, pi, desc);
4256 }
4257 
4258 // -
4259 
CompressedTexImage(bool sub,uint8_t funcDims,GLenum imageTarget,GLint level,GLenum format,const ivec3 & offset,const ivec3 & isize,GLint border,const TexImageSource & src,GLsizei pboImageSize) const4260 void ClientWebGLContext::CompressedTexImage(bool sub, uint8_t funcDims,
4261                                             GLenum imageTarget, GLint level,
4262                                             GLenum format, const ivec3& offset,
4263                                             const ivec3& isize, GLint border,
4264                                             const TexImageSource& src,
4265                                             GLsizei pboImageSize) const {
4266   const FuncScope funcScope(*this, "compressedTex(Sub)Image[23]D");
4267   if (IsContextLost()) return;
4268   if (!IsTexTargetForDims(ImageToTexTarget(imageTarget), mIsWebGL2, funcDims)) {
4269     EnqueueError_ArgEnum("imageTarget", imageTarget);
4270     return;
4271   }
4272   if (border != 0) {
4273     EnqueueError(LOCAL_GL_INVALID_VALUE, "`border` must be 0.");
4274     return;
4275   }
4276 
4277   RawBuffer<> range;
4278   Maybe<uint64_t> pboOffset;
4279   if (src.mView) {
4280     const auto maybe = GetRangeFromView(*src.mView, src.mViewElemOffset,
4281                                         src.mViewElemLengthOverride);
4282     if (!maybe) {
4283       EnqueueError(LOCAL_GL_INVALID_VALUE, "`source` too small.");
4284       return;
4285     }
4286     range = RawBuffer<>{*maybe};
4287   } else if (src.mPboOffset) {
4288     if (!ValidateNonNegative("offset", *src.mPboOffset)) return;
4289     pboOffset = Some(*src.mPboOffset);
4290   } else {
4291     MOZ_CRASH("impossible");
4292   }
4293 
4294   // We don't need to shrink `range` because valid calls require `range` to
4295   // match requirements exactly.
4296 
4297   Run<RPROC(CompressedTexImage)>(
4298       sub, imageTarget, static_cast<uint32_t>(level), format, CastUvec3(offset),
4299       CastUvec3(isize), range, static_cast<uint32_t>(pboImageSize), pboOffset);
4300 }
4301 
CopyTexImage(uint8_t funcDims,GLenum imageTarget,GLint level,GLenum respecFormat,const ivec3 & dstOffset,const ivec2 & srcOffset,const ivec2 & size,GLint border) const4302 void ClientWebGLContext::CopyTexImage(uint8_t funcDims, GLenum imageTarget,
4303                                       GLint level, GLenum respecFormat,
4304                                       const ivec3& dstOffset,
4305                                       const ivec2& srcOffset, const ivec2& size,
4306                                       GLint border) const {
4307   const FuncScope funcScope(*this, "copy(Sub)Image[23]D");
4308   if (IsContextLost()) return;
4309   if (!IsTexTargetForDims(ImageToTexTarget(imageTarget), mIsWebGL2, funcDims)) {
4310     EnqueueError_ArgEnum("imageTarget", imageTarget);
4311     return;
4312   }
4313   if (border != 0) {
4314     EnqueueError(LOCAL_GL_INVALID_VALUE, "`border` must be 0.");
4315     return;
4316   }
4317   Run<RPROC(CopyTexImage)>(imageTarget, static_cast<uint32_t>(level),
4318                            respecFormat, CastUvec3(dstOffset), srcOffset,
4319                            CastUvec2(size));
4320 }
4321 
4322 // ------------------- Programs and shaders --------------------------------
4323 
UseProgram(WebGLProgramJS * const prog)4324 void ClientWebGLContext::UseProgram(WebGLProgramJS* const prog) {
4325   const FuncScope funcScope(*this, "useProgram");
4326   if (IsContextLost()) return;
4327   if (prog && !prog->ValidateUsable(*this, "prog")) return;
4328 
4329   auto& state = State();
4330 
4331   if (state.mTfActiveAndNotPaused) {
4332     EnqueueError(LOCAL_GL_INVALID_OPERATION,
4333                  "Transform feedback is active and not paused.");
4334     return;
4335   }
4336 
4337   if (prog) {
4338     const auto& res = GetLinkResult(*prog);
4339     if (!res.success) {
4340       EnqueueError(LOCAL_GL_INVALID_OPERATION,
4341                    "Program must be linked successfully.");
4342       return;
4343     }
4344   }
4345 
4346   // -
4347 
4348   state.mCurrentProgram = prog;
4349   state.mProgramKeepAlive = prog ? prog->mKeepAliveWeak.lock() : nullptr;
4350   state.mActiveLinkResult = prog ? prog->mResult : nullptr;
4351 
4352   Run<RPROC(UseProgram)>(prog ? prog->mId : 0);
4353 }
4354 
ValidateProgram(WebGLProgramJS & prog) const4355 void ClientWebGLContext::ValidateProgram(WebGLProgramJS& prog) const {
4356   const FuncScope funcScope(*this, "validateProgram");
4357   if (IsContextLost()) return;
4358   if (!prog.ValidateUsable(*this, "prog")) return;
4359 
4360   prog.mLastValidate = [&]() {
4361     const auto& inProcess = mNotLost->inProcess;
4362     if (inProcess) {
4363       return inProcess->ValidateProgram(prog.mId);
4364     }
4365     const auto& child = mNotLost->outOfProcess;
4366     child->FlushPendingCmds();
4367     bool ret = {};
4368     if (!child->SendValidateProgram(prog.mId, &ret)) {
4369       ret = {};
4370     }
4371     return ret;
4372   }();
4373 }
4374 
4375 // ------------------------ Uniforms and attributes ------------------------
4376 
GetVertexAttribPriv(const GLuint index,const GLenum pname)4377 Maybe<double> ClientWebGLContext::GetVertexAttribPriv(const GLuint index,
4378                                                       const GLenum pname) {
4379   const auto& inProcess = mNotLost->inProcess;
4380   if (inProcess) {
4381     return inProcess->GetVertexAttrib(index, pname);
4382   }
4383   const auto& child = mNotLost->outOfProcess;
4384   child->FlushPendingCmds();
4385   Maybe<double> ret;
4386   if (!child->SendGetVertexAttrib(index, pname, &ret)) {
4387     ret.reset();
4388   }
4389   return ret;
4390 }
4391 
GetVertexAttrib(JSContext * cx,GLuint index,GLenum pname,JS::MutableHandle<JS::Value> retval,ErrorResult & rv)4392 void ClientWebGLContext::GetVertexAttrib(JSContext* cx, GLuint index,
4393                                          GLenum pname,
4394                                          JS::MutableHandle<JS::Value> retval,
4395                                          ErrorResult& rv) {
4396   retval.set(JS::NullValue());
4397   const FuncScope funcScope(*this, "getVertexAttrib");
4398   if (IsContextLost()) return;
4399   const auto& state = State();
4400 
4401   const auto& genericAttribs = state.mGenericVertexAttribs;
4402   if (index >= genericAttribs.size()) {
4403     EnqueueError(LOCAL_GL_INVALID_VALUE, "`index` (%u) >= MAX_VERTEX_ATTRIBS",
4404                  index);
4405     return;
4406   }
4407 
4408   switch (pname) {
4409     case LOCAL_GL_CURRENT_VERTEX_ATTRIB: {
4410       JS::RootedObject obj(cx);
4411 
4412       const auto& attrib = genericAttribs[index];
4413       switch (attrib.type) {
4414         case webgl::AttribBaseType::Float:
4415           obj = dom::Float32Array::Create(
4416               cx, this, 4, reinterpret_cast<const float*>(attrib.data));
4417           break;
4418         case webgl::AttribBaseType::Int:
4419           obj = dom::Int32Array::Create(
4420               cx, this, 4, reinterpret_cast<const int32_t*>(attrib.data));
4421           break;
4422         case webgl::AttribBaseType::Uint:
4423           obj = dom::Uint32Array::Create(
4424               cx, this, 4, reinterpret_cast<const uint32_t*>(attrib.data));
4425           break;
4426         case webgl::AttribBaseType::Boolean:
4427           MOZ_CRASH("impossible");
4428       }
4429 
4430       if (!obj) {
4431         rv.Throw(NS_ERROR_OUT_OF_MEMORY);
4432         return;
4433       }
4434       retval.set(JS::ObjectValue(*obj));
4435       return;
4436     }
4437 
4438     case LOCAL_GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING: {
4439       const auto& buffers = state.mBoundVao->mAttribBuffers;
4440       const auto& buffer = buffers[index];
4441       (void)ToJSValueOrNull(cx, buffer, retval);
4442       return;
4443     }
4444 
4445     case LOCAL_GL_VERTEX_ATTRIB_ARRAY_POINTER:
4446       // Disallowed from JS, but allowed in Host.
4447       EnqueueError_ArgEnum("pname", pname);
4448       return;
4449 
4450     default:
4451       break;
4452   }
4453 
4454   const auto maybe = GetVertexAttribPriv(index, pname);
4455   if (maybe) {
4456     switch (pname) {
4457       case LOCAL_GL_VERTEX_ATTRIB_ARRAY_ENABLED:
4458       case LOCAL_GL_VERTEX_ATTRIB_ARRAY_NORMALIZED:
4459       case LOCAL_GL_VERTEX_ATTRIB_ARRAY_INTEGER:
4460         retval.set(JS::BooleanValue(*maybe));
4461         break;
4462 
4463       default:
4464         retval.set(JS::NumberValue(*maybe));
4465         break;
4466     }
4467   }
4468 }
4469 
UniformData(const GLenum funcElemType,const WebGLUniformLocationJS * const loc,bool transpose,const Range<const uint8_t> & bytes,GLuint elemOffset,GLuint elemCountOverride) const4470 void ClientWebGLContext::UniformData(const GLenum funcElemType,
4471                                      const WebGLUniformLocationJS* const loc,
4472                                      bool transpose,
4473                                      const Range<const uint8_t>& bytes,
4474                                      GLuint elemOffset,
4475                                      GLuint elemCountOverride) const {
4476   const FuncScope funcScope(*this, "uniform setter");
4477   if (IsContextLost()) return;
4478 
4479   const auto& activeLinkResult = GetActiveLinkResult();
4480   if (!activeLinkResult) {
4481     EnqueueError(LOCAL_GL_INVALID_OPERATION, "No active linked Program.");
4482     return;
4483   }
4484 
4485   // -
4486 
4487   auto availCount = bytes.length() / sizeof(float);
4488   if (elemOffset > availCount) {
4489     EnqueueError(LOCAL_GL_INVALID_VALUE, "`elemOffset` too large for `data`.");
4490     return;
4491   }
4492   availCount -= elemOffset;
4493   if (elemCountOverride) {
4494     if (elemCountOverride > availCount) {
4495       EnqueueError(LOCAL_GL_INVALID_VALUE,
4496                    "`elemCountOverride` too large for `data`.");
4497       return;
4498     }
4499     availCount = elemCountOverride;
4500   }
4501 
4502   // -
4503 
4504   const auto channels = ElemTypeComponents(funcElemType);
4505   if (!availCount || availCount % channels != 0) {
4506     EnqueueError(LOCAL_GL_INVALID_VALUE,
4507                  "`values` length (%u) must be a positive "
4508                  "integer multiple of size of %s.",
4509                  availCount, EnumString(funcElemType).c_str());
4510     return;
4511   }
4512 
4513   // -
4514 
4515   uint32_t locId = -1;
4516   if (MOZ_LIKELY(loc)) {
4517     locId = loc->mLocation;
4518     if (!loc->ValidateUsable(*this, "location")) return;
4519 
4520     // -
4521 
4522     const auto& reqLinkInfo = loc->mParent.lock();
4523     if (reqLinkInfo.get() != activeLinkResult) {
4524       EnqueueError(LOCAL_GL_INVALID_OPERATION,
4525                    "UniformLocation is not from the current active Program.");
4526       return;
4527     }
4528 
4529     // -
4530 
4531     bool funcMatchesLocation = false;
4532     for (const auto allowed : loc->mValidUploadElemTypes) {
4533       funcMatchesLocation |= (funcElemType == allowed);
4534     }
4535     if (MOZ_UNLIKELY(!funcMatchesLocation)) {
4536       std::string validSetters;
4537       for (const auto allowed : loc->mValidUploadElemTypes) {
4538         validSetters += EnumString(allowed);
4539         validSetters += '/';
4540       }
4541       validSetters.pop_back();  // Cheekily discard the extra trailing '/'.
4542 
4543       EnqueueError(LOCAL_GL_INVALID_OPERATION,
4544                    "Uniform's `type` requires uniform setter of type %s.",
4545                    validSetters.c_str());
4546       return;
4547     }
4548   }
4549 
4550   // -
4551 
4552   const auto ptr = bytes.begin().get() + (elemOffset * sizeof(float));
4553   const auto range = Range<const uint8_t>{ptr, availCount * sizeof(float)};
4554   Run<RPROC(UniformData)>(locId, transpose, RawBuffer<>(range));
4555 }
4556 
4557 // -
4558 
BindVertexArray(WebGLVertexArrayJS * const vao)4559 void ClientWebGLContext::BindVertexArray(WebGLVertexArrayJS* const vao) {
4560   const FuncScope funcScope(*this, "bindVertexArray");
4561   if (IsContextLost()) return;
4562   if (vao && !vao->ValidateUsable(*this, "vao")) return;
4563   auto& state = State();
4564 
4565   if (vao) {
4566     vao->mHasBeenBound = true;
4567     state.mBoundVao = vao;
4568   } else {
4569     state.mBoundVao = state.mDefaultVao;
4570   }
4571 
4572   Run<RPROC(BindVertexArray)>(vao ? vao->mId : 0);
4573 }
4574 
EnableVertexAttribArray(GLuint index)4575 void ClientWebGLContext::EnableVertexAttribArray(GLuint index) {
4576   Run<RPROC(EnableVertexAttribArray)>(index);
4577 }
4578 
DisableVertexAttribArray(GLuint index)4579 void ClientWebGLContext::DisableVertexAttribArray(GLuint index) {
4580   Run<RPROC(DisableVertexAttribArray)>(index);
4581 }
4582 
GetVertexAttribOffset(GLuint index,GLenum pname)4583 WebGLsizeiptr ClientWebGLContext::GetVertexAttribOffset(GLuint index,
4584                                                         GLenum pname) {
4585   const FuncScope funcScope(*this, "getVertexAttribOffset");
4586   if (IsContextLost()) return 0;
4587 
4588   if (pname != LOCAL_GL_VERTEX_ATTRIB_ARRAY_POINTER) {
4589     EnqueueError_ArgEnum("pname", pname);
4590     return 0;
4591   }
4592 
4593   const auto maybe = GetVertexAttribPriv(index, pname);
4594   if (!maybe) return 0;
4595   return *maybe;
4596 }
4597 
VertexAttrib4Tv(GLuint index,webgl::AttribBaseType t,const Range<const uint8_t> & src)4598 void ClientWebGLContext::VertexAttrib4Tv(GLuint index, webgl::AttribBaseType t,
4599                                          const Range<const uint8_t>& src) {
4600   const FuncScope funcScope(*this, "vertexAttrib[1234]u?[fi]{v}");
4601   if (IsContextLost()) return;
4602   auto& state = State();
4603 
4604   if (src.length() / sizeof(float) < 4) {
4605     EnqueueError(LOCAL_GL_INVALID_VALUE, "Array must have >=4 elements.");
4606     return;
4607   }
4608 
4609   auto& list = state.mGenericVertexAttribs;
4610   if (index >= list.size()) {
4611     EnqueueError(LOCAL_GL_INVALID_VALUE,
4612                  "`index` must be < MAX_VERTEX_ATTRIBS.");
4613     return;
4614   }
4615 
4616   auto& attrib = list[index];
4617   attrib.type = t;
4618   memcpy(attrib.data, src.begin().get(), sizeof(attrib.data));
4619 
4620   Run<RPROC(VertexAttrib4T)>(index, attrib);
4621 }
4622 
4623 // -
4624 
VertexAttribDivisor(GLuint index,GLuint divisor)4625 void ClientWebGLContext::VertexAttribDivisor(GLuint index, GLuint divisor) {
4626   Run<RPROC(VertexAttribDivisor)>(index, divisor);
4627 }
4628 
4629 // -
4630 
VertexAttribPointerImpl(bool isFuncInt,GLuint index,GLint rawChannels,GLenum type,bool normalized,GLsizei rawByteStrideOrZero,WebGLintptr rawByteOffset)4631 void ClientWebGLContext::VertexAttribPointerImpl(bool isFuncInt, GLuint index,
4632                                                  GLint rawChannels, GLenum type,
4633                                                  bool normalized,
4634                                                  GLsizei rawByteStrideOrZero,
4635                                                  WebGLintptr rawByteOffset) {
4636   const FuncScope funcScope(*this, "vertexAttribI?Pointer");
4637   if (IsContextLost()) return;
4638   auto& state = State();
4639 
4640   const auto channels = MaybeAs<uint8_t>(rawChannels);
4641   if (!channels) {
4642     EnqueueError(LOCAL_GL_INVALID_VALUE,
4643                  "Channel count `size` must be within [1,4].");
4644     return;
4645   }
4646 
4647   const auto byteStrideOrZero = MaybeAs<uint8_t>(rawByteStrideOrZero);
4648   if (!byteStrideOrZero) {
4649     EnqueueError(LOCAL_GL_INVALID_VALUE, "`stride` must be within [0,255].");
4650     return;
4651   }
4652 
4653   if (!ValidateNonNegative("byteOffset", rawByteOffset)) return;
4654   const auto byteOffset = static_cast<uint64_t>(rawByteOffset);
4655 
4656   // -
4657 
4658   const webgl::VertAttribPointerDesc desc{
4659       isFuncInt, *channels, normalized, *byteStrideOrZero, type, byteOffset};
4660 
4661   const auto res = CheckVertexAttribPointer(mIsWebGL2, desc);
4662   if (res.isErr()) {
4663     const auto& err = res.inspectErr();
4664     EnqueueError(err.type, "%s", err.info.c_str());
4665     return;
4666   }
4667 
4668   auto& list = state.mBoundVao->mAttribBuffers;
4669   if (index >= list.size()) {
4670     EnqueueError(LOCAL_GL_INVALID_VALUE,
4671                  "`index` (%u) must be < MAX_VERTEX_ATTRIBS.", index);
4672     return;
4673   }
4674 
4675   const auto buffer = state.mBoundBufferByTarget[LOCAL_GL_ARRAY_BUFFER];
4676   if (!buffer && byteOffset) {
4677     return EnqueueError(LOCAL_GL_INVALID_OPERATION,
4678                         "If ARRAY_BUFFER is null, byteOffset must be zero.");
4679   }
4680 
4681   Run<RPROC(VertexAttribPointer)>(index, desc);
4682 
4683   list[index] = buffer;
4684 }
4685 
4686 // -------------------------------- Drawing -------------------------------
4687 
DrawArraysInstanced(GLenum mode,GLint first,GLsizei count,GLsizei primcount,FuncScopeId)4688 void ClientWebGLContext::DrawArraysInstanced(GLenum mode, GLint first,
4689                                              GLsizei count, GLsizei primcount,
4690                                              FuncScopeId) {
4691   Run<RPROC(DrawArraysInstanced)>(mode, first, count, primcount);
4692   AfterDrawCall();
4693 }
4694 
DrawElementsInstanced(GLenum mode,GLsizei count,GLenum type,WebGLintptr offset,GLsizei primcount,FuncScopeId)4695 void ClientWebGLContext::DrawElementsInstanced(GLenum mode, GLsizei count,
4696                                                GLenum type, WebGLintptr offset,
4697                                                GLsizei primcount, FuncScopeId) {
4698   Run<RPROC(DrawElementsInstanced)>(mode, count, type, offset, primcount);
4699   AfterDrawCall();
4700 }
4701 
4702 // ------------------------------ Readback -------------------------------
ReadPixels(GLint x,GLint y,GLsizei width,GLsizei height,GLenum format,GLenum type,WebGLsizeiptr offset,dom::CallerType aCallerType,ErrorResult & out_error) const4703 void ClientWebGLContext::ReadPixels(GLint x, GLint y, GLsizei width,
4704                                     GLsizei height, GLenum format, GLenum type,
4705                                     WebGLsizeiptr offset,
4706                                     dom::CallerType aCallerType,
4707                                     ErrorResult& out_error) const {
4708   const FuncScope funcScope(*this, "readPixels");
4709   if (!ReadPixels_SharedPrecheck(aCallerType, out_error)) return;
4710   const auto& state = State();
4711   if (!ValidateNonNegative("width", width)) return;
4712   if (!ValidateNonNegative("height", height)) return;
4713   if (!ValidateNonNegative("offset", offset)) return;
4714 
4715   const auto desc = webgl::ReadPixelsDesc{{x, y},
4716                                           *uvec2::From(width, height),
4717                                           {format, type},
4718                                           state.mPixelPackState};
4719   Run<RPROC(ReadPixelsPbo)>(desc, static_cast<uint64_t>(offset));
4720 }
4721 
ReadPixels(GLint x,GLint y,GLsizei width,GLsizei height,GLenum format,GLenum type,const dom::ArrayBufferView & dstData,GLuint dstElemOffset,dom::CallerType aCallerType,ErrorResult & out_error) const4722 void ClientWebGLContext::ReadPixels(GLint x, GLint y, GLsizei width,
4723                                     GLsizei height, GLenum format, GLenum type,
4724                                     const dom::ArrayBufferView& dstData,
4725                                     GLuint dstElemOffset,
4726                                     dom::CallerType aCallerType,
4727                                     ErrorResult& out_error) const {
4728   const FuncScope funcScope(*this, "readPixels");
4729   if (!ReadPixels_SharedPrecheck(aCallerType, out_error)) return;
4730   const auto& state = State();
4731   if (!ValidateNonNegative("width", width)) return;
4732   if (!ValidateNonNegative("height", height)) return;
4733 
4734   ////
4735 
4736   js::Scalar::Type reqScalarType;
4737   if (!GetJSScalarFromGLType(type, &reqScalarType)) {
4738     nsCString name;
4739     WebGLContext::EnumName(type, &name);
4740     EnqueueError(LOCAL_GL_INVALID_ENUM, "type: invalid enum value %s",
4741                  name.BeginReading());
4742     return;
4743   }
4744 
4745   auto viewElemType = dstData.Type();
4746   if (viewElemType == js::Scalar::Uint8Clamped) {
4747     viewElemType = js::Scalar::Uint8;
4748   }
4749   if (viewElemType != reqScalarType) {
4750     EnqueueError(LOCAL_GL_INVALID_OPERATION,
4751                  "`pixels` type does not match `type`.");
4752     return;
4753   }
4754 
4755   uint8_t* bytes;
4756   size_t byteLen;
4757   if (!ValidateArrayBufferView(dstData, dstElemOffset, 0,
4758                                LOCAL_GL_INVALID_VALUE, &bytes, &byteLen)) {
4759     return;
4760   }
4761 
4762   const auto desc = webgl::ReadPixelsDesc{{x, y},
4763                                           *uvec2::From(width, height),
4764                                           {format, type},
4765                                           state.mPixelPackState};
4766   const auto range = Range<uint8_t>(bytes, byteLen);
4767   if (!DoReadPixels(desc, range)) {
4768     return;
4769   }
4770 }
4771 
DoReadPixels(const webgl::ReadPixelsDesc & desc,const Range<uint8_t> dest) const4772 bool ClientWebGLContext::DoReadPixels(const webgl::ReadPixelsDesc& desc,
4773                                       const Range<uint8_t> dest) const {
4774   const auto notLost =
4775       mNotLost;  // Hold a strong-ref to prevent LoseContext=>UAF.
4776   if (!notLost) return false;
4777   const auto& inProcess = notLost->inProcess;
4778   if (inProcess) {
4779     inProcess->ReadPixelsInto(desc, dest);
4780     return true;
4781   }
4782   const auto& child = notLost->outOfProcess;
4783   child->FlushPendingCmds();
4784   webgl::ReadPixelsResultIpc res = {};
4785   if (!child->SendReadPixels(desc, dest.length(), &res)) {
4786     res = {};
4787   }
4788   if (!res.byteStride) return false;
4789   const auto& byteStride = res.byteStride;
4790   const auto& subrect = res.subrect;
4791   const webgl::RaiiShmem shmem{child, res.shmem};
4792   if (!shmem) {
4793     EnqueueError(LOCAL_GL_OUT_OF_MEMORY, "Failed to map in back buffer.");
4794     return false;
4795   }
4796 
4797   const auto& shmemBytes = shmem.ByteRange();
4798 
4799   uint8_t bpp;
4800   if (!GetBytesPerPixel(desc.pi, &bpp)) {
4801     MOZ_ASSERT(false);
4802     return false;
4803   }
4804 
4805   const auto& packing = desc.packState;
4806   auto packRect = *uvec2::From(subrect.x, subrect.y);
4807   packRect.x += packing.skipPixels;
4808   packRect.y += packing.skipRows;
4809 
4810   const auto xByteSize = bpp * static_cast<uint32_t>(subrect.width);
4811   const ptrdiff_t byteOffset = packRect.y * byteStride + packRect.x * bpp;
4812 
4813   auto srcItr = shmemBytes.begin() + byteOffset;
4814   auto destItr = dest.begin() + byteOffset;
4815 
4816   for (const auto i : IntegerRange(subrect.height)) {
4817     if (i) {
4818       // Don't trigger an assert on the last loop by pushing a RangedPtr past
4819       // its bounds.
4820       srcItr += byteStride;
4821       destItr += byteStride;
4822       MOZ_RELEASE_ASSERT(srcItr + xByteSize <= shmemBytes.end());
4823       MOZ_RELEASE_ASSERT(destItr + xByteSize <= dest.end());
4824     }
4825     Memcpy(destItr, srcItr, xByteSize);
4826   }
4827 
4828   return true;
4829 }
4830 
ReadPixels_SharedPrecheck(dom::CallerType aCallerType,ErrorResult & out_error) const4831 bool ClientWebGLContext::ReadPixels_SharedPrecheck(
4832     dom::CallerType aCallerType, ErrorResult& out_error) const {
4833   if (IsContextLost()) return false;
4834 
4835   if (mCanvasElement && mCanvasElement->IsWriteOnly() &&
4836       aCallerType != dom::CallerType::System) {
4837     JsWarning("readPixels: Not allowed");
4838     out_error.Throw(NS_ERROR_DOM_SECURITY_ERR);
4839     return false;
4840   }
4841 
4842   return true;
4843 }
4844 
4845 // --------------------------------- GL Query ---------------------------------
4846 
QuerySlotTarget(const GLenum specificTarget)4847 static inline GLenum QuerySlotTarget(const GLenum specificTarget) {
4848   if (specificTarget == LOCAL_GL_ANY_SAMPLES_PASSED_CONSERVATIVE) {
4849     return LOCAL_GL_ANY_SAMPLES_PASSED;
4850   }
4851   return specificTarget;
4852 }
4853 
GetQuery(JSContext * cx,GLenum specificTarget,GLenum pname,JS::MutableHandle<JS::Value> retval) const4854 void ClientWebGLContext::GetQuery(JSContext* cx, GLenum specificTarget,
4855                                   GLenum pname,
4856                                   JS::MutableHandle<JS::Value> retval) const {
4857   retval.set(JS::NullValue());
4858   const FuncScope funcScope(*this, "getQuery");
4859   if (IsContextLost()) return;
4860   const auto& limits = Limits();
4861   auto& state = State();
4862 
4863   if (IsExtensionEnabled(WebGLExtensionID::EXT_disjoint_timer_query)) {
4864     if (pname == LOCAL_GL_QUERY_COUNTER_BITS) {
4865       switch (specificTarget) {
4866         case LOCAL_GL_TIME_ELAPSED_EXT:
4867           retval.set(JS::NumberValue(limits.queryCounterBitsTimeElapsed));
4868           return;
4869 
4870         case LOCAL_GL_TIMESTAMP_EXT:
4871           retval.set(JS::NumberValue(limits.queryCounterBitsTimestamp));
4872           return;
4873 
4874         default:
4875           EnqueueError_ArgEnum("target", specificTarget);
4876           return;
4877       }
4878     }
4879   }
4880 
4881   if (pname != LOCAL_GL_CURRENT_QUERY) {
4882     EnqueueError_ArgEnum("pname", pname);
4883     return;
4884   }
4885 
4886   const auto slotTarget = QuerySlotTarget(specificTarget);
4887   const auto& slot = MaybeFind(state.mCurrentQueryByTarget, slotTarget);
4888   if (!slot) {
4889     EnqueueError_ArgEnum("target", specificTarget);
4890     return;
4891   }
4892 
4893   auto query = *slot;
4894   if (query && query->mTarget != specificTarget) {
4895     query = nullptr;
4896   }
4897 
4898   (void)ToJSValueOrNull(cx, query, retval);
4899 }
4900 
GetQueryParameter(JSContext *,WebGLQueryJS & query,const GLenum pname,JS::MutableHandle<JS::Value> retval) const4901 void ClientWebGLContext::GetQueryParameter(
4902     JSContext*, WebGLQueryJS& query, const GLenum pname,
4903     JS::MutableHandle<JS::Value> retval) const {
4904   retval.set(JS::NullValue());
4905   const FuncScope funcScope(*this, "getQueryParameter");
4906   if (IsContextLost()) return;
4907   if (!query.ValidateUsable(*this, "query")) return;
4908 
4909   auto maybe = [&]() {
4910     const auto& inProcess = mNotLost->inProcess;
4911     if (inProcess) {
4912       return inProcess->GetQueryParameter(query.mId, pname);
4913     }
4914     const auto& child = mNotLost->outOfProcess;
4915     child->FlushPendingCmds();
4916     Maybe<double> ret;
4917     if (!child->SendGetQueryParameter(query.mId, pname, &ret)) {
4918       ret.reset();
4919     }
4920     return ret;
4921   }();
4922   if (!maybe) return;
4923 
4924   // We must usually wait for an event loop before the query can be available.
4925   const bool canBeAvailable =
4926       (query.mCanBeAvailable || StaticPrefs::webgl_allow_immediate_queries());
4927   if (!canBeAvailable) {
4928     if (pname != LOCAL_GL_QUERY_RESULT_AVAILABLE) {
4929       return;
4930     }
4931     maybe = Some(0.0);
4932   }
4933 
4934   switch (pname) {
4935     case LOCAL_GL_QUERY_RESULT_AVAILABLE:
4936       retval.set(JS::BooleanValue(*maybe));
4937       break;
4938 
4939     default:
4940       retval.set(JS::NumberValue(*maybe));
4941       break;
4942   }
4943 }
4944 
BeginQuery(const GLenum specificTarget,WebGLQueryJS & query)4945 void ClientWebGLContext::BeginQuery(const GLenum specificTarget,
4946                                     WebGLQueryJS& query) {
4947   const FuncScope funcScope(*this, "beginQuery");
4948   if (IsContextLost()) return;
4949   if (!query.ValidateUsable(*this, "query")) return;
4950   auto& state = State();
4951 
4952   const auto slotTarget = QuerySlotTarget(specificTarget);
4953   const auto& slot = MaybeFind(state.mCurrentQueryByTarget, slotTarget);
4954   if (!slot) {
4955     EnqueueError_ArgEnum("target", specificTarget);
4956     return;
4957   }
4958 
4959   if (*slot) {
4960     auto enumStr = EnumString(slotTarget);
4961     if (slotTarget == LOCAL_GL_ANY_SAMPLES_PASSED) {
4962       enumStr += "/ANY_SAMPLES_PASSED_CONSERVATIVE";
4963     }
4964     EnqueueError(LOCAL_GL_INVALID_OPERATION,
4965                  "A Query is already active for %s.", enumStr.c_str());
4966     return;
4967   }
4968 
4969   if (query.mTarget && query.mTarget != specificTarget) {
4970     EnqueueError(LOCAL_GL_INVALID_OPERATION,
4971                  "`query` cannot be changed to a different target.");
4972     return;
4973   }
4974 
4975   *slot = &query;
4976   query.mTarget = specificTarget;
4977 
4978   Run<RPROC(BeginQuery)>(specificTarget, query.mId);
4979 }
4980 
EndQuery(const GLenum specificTarget)4981 void ClientWebGLContext::EndQuery(const GLenum specificTarget) {
4982   const FuncScope funcScope(*this, "endQuery");
4983   if (IsContextLost()) return;
4984   auto& state = State();
4985 
4986   const auto slotTarget = QuerySlotTarget(specificTarget);
4987   const auto& maybeSlot = MaybeFind(state.mCurrentQueryByTarget, slotTarget);
4988   if (!maybeSlot) {
4989     EnqueueError_ArgEnum("target", specificTarget);
4990     return;
4991   }
4992   auto& slot = *maybeSlot;
4993   if (!slot || slot->mTarget != specificTarget) {
4994     EnqueueError(LOCAL_GL_INVALID_OPERATION, "No Query is active for %s.",
4995                  EnumString(specificTarget).c_str());
4996     return;
4997   }
4998   const auto query = slot;
4999   slot = nullptr;
5000 
5001   Run<RPROC(EndQuery)>(specificTarget);
5002 
5003   auto& availRunnable = EnsureAvailabilityRunnable();
5004   availRunnable.mQueries.push_back(query.get());
5005   query->mCanBeAvailable = false;
5006 }
5007 
QueryCounter(WebGLQueryJS & query,const GLenum target) const5008 void ClientWebGLContext::QueryCounter(WebGLQueryJS& query,
5009                                       const GLenum target) const {
5010   const FuncScope funcScope(*this, "queryCounter");
5011   if (IsContextLost()) return;
5012   if (!query.ValidateUsable(*this, "query")) return;
5013 
5014   if (target != LOCAL_GL_TIMESTAMP) {
5015     EnqueueError(LOCAL_GL_INVALID_ENUM, "`target` must be TIMESTAMP.");
5016     return;
5017   }
5018 
5019   if (query.mTarget && query.mTarget != target) {
5020     EnqueueError(LOCAL_GL_INVALID_OPERATION,
5021                  "`query` cannot be changed to a different target.");
5022     return;
5023   }
5024   query.mTarget = target;
5025 
5026   Run<RPROC(QueryCounter)>(query.mId);
5027 
5028   auto& availRunnable = EnsureAvailabilityRunnable();
5029   availRunnable.mQueries.push_back(&query);
5030   query.mCanBeAvailable = false;
5031 }
5032 
5033 // -------------------------------- Sampler -------------------------------
GetSamplerParameter(JSContext * cx,const WebGLSamplerJS & sampler,const GLenum pname,JS::MutableHandle<JS::Value> retval) const5034 void ClientWebGLContext::GetSamplerParameter(
5035     JSContext* cx, const WebGLSamplerJS& sampler, const GLenum pname,
5036     JS::MutableHandle<JS::Value> retval) const {
5037   retval.set(JS::NullValue());
5038   const FuncScope funcScope(*this, "getSamplerParameter");
5039   if (IsContextLost()) return;
5040   if (!sampler.ValidateUsable(*this, "sampler")) return;
5041 
5042   const auto maybe = [&]() {
5043     const auto& inProcess = mNotLost->inProcess;
5044     if (inProcess) {
5045       return inProcess->GetSamplerParameter(sampler.mId, pname);
5046     }
5047     const auto& child = mNotLost->outOfProcess;
5048     child->FlushPendingCmds();
5049     Maybe<double> ret;
5050     if (!child->SendGetSamplerParameter(sampler.mId, pname, &ret)) {
5051       ret.reset();
5052     }
5053     return ret;
5054   }();
5055   if (maybe) {
5056     retval.set(JS::NumberValue(*maybe));
5057   }
5058 }
5059 
BindSampler(const GLuint unit,WebGLSamplerJS * const sampler)5060 void ClientWebGLContext::BindSampler(const GLuint unit,
5061                                      WebGLSamplerJS* const sampler) {
5062   const FuncScope funcScope(*this, "bindSampler");
5063   if (IsContextLost()) return;
5064   if (sampler && !sampler->ValidateUsable(*this, "sampler")) return;
5065   auto& state = State();
5066 
5067   auto& texUnits = state.mTexUnits;
5068   if (unit >= texUnits.size()) {
5069     EnqueueError(LOCAL_GL_INVALID_VALUE, "`unit` (%u) larger than %zu.", unit,
5070                  texUnits.size());
5071     return;
5072   }
5073 
5074   // -
5075 
5076   texUnits[unit].sampler = sampler;
5077 
5078   Run<RPROC(BindSampler)>(unit, sampler ? sampler->mId : 0);
5079 }
5080 
SamplerParameteri(WebGLSamplerJS & sampler,const GLenum pname,const GLint param) const5081 void ClientWebGLContext::SamplerParameteri(WebGLSamplerJS& sampler,
5082                                            const GLenum pname,
5083                                            const GLint param) const {
5084   const FuncScope funcScope(*this, "samplerParameteri");
5085   if (IsContextLost()) return;
5086   if (!sampler.ValidateUsable(*this, "sampler")) return;
5087 
5088   Run<RPROC(SamplerParameteri)>(sampler.mId, pname, param);
5089 }
5090 
SamplerParameterf(WebGLSamplerJS & sampler,const GLenum pname,const GLfloat param) const5091 void ClientWebGLContext::SamplerParameterf(WebGLSamplerJS& sampler,
5092                                            const GLenum pname,
5093                                            const GLfloat param) const {
5094   const FuncScope funcScope(*this, "samplerParameterf");
5095   if (IsContextLost()) return;
5096   if (!sampler.ValidateUsable(*this, "sampler")) return;
5097 
5098   Run<RPROC(SamplerParameterf)>(sampler.mId, pname, param);
5099 }
5100 
5101 // ------------------------------- GL Sync ---------------------------------
5102 
GetSyncParameter(JSContext * const cx,WebGLSyncJS & sync,const GLenum pname,JS::MutableHandle<JS::Value> retval) const5103 void ClientWebGLContext::GetSyncParameter(
5104     JSContext* const cx, WebGLSyncJS& sync, const GLenum pname,
5105     JS::MutableHandle<JS::Value> retval) const {
5106   retval.set(JS::NullValue());
5107   const FuncScope funcScope(*this, "getSyncParameter");
5108   if (IsContextLost()) return;
5109   if (!sync.ValidateUsable(*this, "sync")) return;
5110 
5111   retval.set([&]() -> JS::Value {
5112     switch (pname) {
5113       case LOCAL_GL_OBJECT_TYPE:
5114         return JS::NumberValue(LOCAL_GL_SYNC_FENCE);
5115       case LOCAL_GL_SYNC_CONDITION:
5116         return JS::NumberValue(LOCAL_GL_SYNC_GPU_COMMANDS_COMPLETE);
5117       case LOCAL_GL_SYNC_FLAGS:
5118         return JS::NumberValue(0);
5119       case LOCAL_GL_SYNC_STATUS: {
5120         if (!sync.mSignaled) {
5121           const auto res = ClientWaitSync(sync, 0, 0);
5122           sync.mSignaled = (res == LOCAL_GL_ALREADY_SIGNALED ||
5123                             res == LOCAL_GL_CONDITION_SATISFIED);
5124         }
5125         return JS::NumberValue(sync.mSignaled ? LOCAL_GL_SIGNALED
5126                                               : LOCAL_GL_UNSIGNALED);
5127       }
5128       default:
5129         EnqueueError_ArgEnum("pname", pname);
5130         return JS::NullValue();
5131     }
5132   }());
5133 }
5134 
ClientWaitSync(WebGLSyncJS & sync,const GLbitfield flags,const GLuint64 timeout) const5135 GLenum ClientWebGLContext::ClientWaitSync(WebGLSyncJS& sync,
5136                                           const GLbitfield flags,
5137                                           const GLuint64 timeout) const {
5138   const FuncScope funcScope(*this, "clientWaitSync");
5139   if (IsContextLost()) return LOCAL_GL_WAIT_FAILED;
5140   if (!sync.ValidateUsable(*this, "sync")) return LOCAL_GL_WAIT_FAILED;
5141 
5142   if (flags != 0 && flags != LOCAL_GL_SYNC_FLUSH_COMMANDS_BIT) {
5143     EnqueueError(LOCAL_GL_INVALID_VALUE,
5144                  "`flags` must be SYNC_FLUSH_COMMANDS_BIT or 0.");
5145     return LOCAL_GL_WAIT_FAILED;
5146   }
5147 
5148   const auto ret = [&]() {
5149     const auto& inProcess = mNotLost->inProcess;
5150     if (inProcess) {
5151       return inProcess->ClientWaitSync(sync.mId, flags, timeout);
5152     }
5153     const auto& child = mNotLost->outOfProcess;
5154     child->FlushPendingCmds();
5155     GLenum ret = {};
5156     if (!child->SendClientWaitSync(sync.mId, flags, timeout, &ret)) {
5157       ret = {};
5158     }
5159     return ret;
5160   }();
5161 
5162   switch (ret) {
5163     case LOCAL_GL_CONDITION_SATISFIED:
5164     case LOCAL_GL_ALREADY_SIGNALED:
5165       sync.mSignaled = true;
5166       break;
5167   }
5168 
5169   // -
5170 
5171   const bool canBeAvailable =
5172       (sync.mCanBeAvailable || StaticPrefs::webgl_allow_immediate_queries());
5173   if (!canBeAvailable) {
5174     if (!sync.mHasWarnedNotAvailable) {
5175       EnqueueWarning(
5176           "ClientWaitSync must return TIMEOUT_EXPIRED until control has"
5177           " returned to the user agent's main loop. (only warns once)");
5178       sync.mHasWarnedNotAvailable = true;
5179     }
5180     return LOCAL_GL_TIMEOUT_EXPIRED;
5181   }
5182 
5183   return ret;
5184 }
5185 
WaitSync(const WebGLSyncJS & sync,const GLbitfield flags,const GLint64 timeout) const5186 void ClientWebGLContext::WaitSync(const WebGLSyncJS& sync,
5187                                   const GLbitfield flags,
5188                                   const GLint64 timeout) const {
5189   const FuncScope funcScope(*this, "waitSync");
5190   if (IsContextLost()) return;
5191   if (!sync.ValidateUsable(*this, "sync")) return;
5192 
5193   if (flags != 0) {
5194     EnqueueError(LOCAL_GL_INVALID_VALUE, "`flags` must be 0.");
5195     return;
5196   }
5197   if (timeout != -1) {
5198     EnqueueError(LOCAL_GL_INVALID_VALUE, "`timeout` must be TIMEOUT_IGNORED.");
5199     return;
5200   }
5201 
5202   JsWarning("waitSync is a no-op.");
5203 }
5204 
5205 // -------------------------- Transform Feedback ---------------------------
5206 
BindTransformFeedback(const GLenum target,WebGLTransformFeedbackJS * const tf)5207 void ClientWebGLContext::BindTransformFeedback(
5208     const GLenum target, WebGLTransformFeedbackJS* const tf) {
5209   const FuncScope funcScope(*this, "bindTransformFeedback");
5210   if (IsContextLost()) return;
5211   if (tf && !tf->ValidateUsable(*this, "tf")) return;
5212   auto& state = State();
5213 
5214   if (target != LOCAL_GL_TRANSFORM_FEEDBACK) {
5215     EnqueueError(LOCAL_GL_INVALID_ENUM, "`target` must be TRANSFORM_FEEDBACK.");
5216     return;
5217   }
5218   if (state.mTfActiveAndNotPaused) {
5219     EnqueueError(LOCAL_GL_INVALID_OPERATION,
5220                  "Current Transform Feedback object is active and not paused.");
5221     return;
5222   }
5223 
5224   if (tf) {
5225     tf->mHasBeenBound = true;
5226     state.mBoundTfo = tf;
5227   } else {
5228     state.mBoundTfo = state.mDefaultTfo;
5229   }
5230 
5231   Run<RPROC(BindTransformFeedback)>(tf ? tf->mId : 0);
5232 }
5233 
BeginTransformFeedback(const GLenum primMode)5234 void ClientWebGLContext::BeginTransformFeedback(const GLenum primMode) {
5235   const FuncScope funcScope(*this, "beginTransformFeedback");
5236   if (IsContextLost()) return;
5237   auto& state = State();
5238   auto& tfo = *(state.mBoundTfo);
5239 
5240   if (tfo.mActiveOrPaused) {
5241     EnqueueError(LOCAL_GL_INVALID_OPERATION,
5242                  "Transform Feedback is already active or paused.");
5243     return;
5244   }
5245   MOZ_ASSERT(!state.mTfActiveAndNotPaused);
5246 
5247   auto& prog = state.mCurrentProgram;
5248   if (!prog) {
5249     EnqueueError(LOCAL_GL_INVALID_OPERATION, "No program in use.");
5250     return;
5251   }
5252   const auto& linkResult = GetLinkResult(*prog);
5253   if (!linkResult.success) {
5254     EnqueueError(LOCAL_GL_INVALID_OPERATION,
5255                  "Program is not successfully linked.");
5256     return;
5257   }
5258 
5259   auto tfBufferCount = linkResult.active.activeTfVaryings.size();
5260   if (tfBufferCount &&
5261       linkResult.tfBufferMode == LOCAL_GL_INTERLEAVED_ATTRIBS) {
5262     tfBufferCount = 1;
5263   }
5264   if (!tfBufferCount) {
5265     EnqueueError(LOCAL_GL_INVALID_OPERATION,
5266                  "Program does not use Transform Feedback.");
5267     return;
5268   }
5269 
5270   const auto& buffers = tfo.mAttribBuffers;
5271   for (const auto i : IntegerRange(tfBufferCount)) {
5272     if (!buffers[i]) {
5273       EnqueueError(LOCAL_GL_INVALID_OPERATION,
5274                    "Transform Feedback buffer %u is null.", i);
5275       return;
5276     }
5277   }
5278 
5279   switch (primMode) {
5280     case LOCAL_GL_POINTS:
5281     case LOCAL_GL_LINES:
5282     case LOCAL_GL_TRIANGLES:
5283       break;
5284     default:
5285       EnqueueError(LOCAL_GL_INVALID_ENUM,
5286                    "`primitiveMode` must be POINTS, LINES< or TRIANGLES.");
5287       return;
5288   }
5289 
5290   // -
5291 
5292   tfo.mActiveOrPaused = true;
5293   tfo.mActiveProgram = prog;
5294   tfo.mActiveProgramKeepAlive = prog->mKeepAliveWeak.lock();
5295   prog->mActiveTfos.insert(&tfo);
5296   state.mTfActiveAndNotPaused = true;
5297 
5298   Run<RPROC(BeginTransformFeedback)>(primMode);
5299 }
5300 
EndTransformFeedback()5301 void ClientWebGLContext::EndTransformFeedback() {
5302   const FuncScope funcScope(*this, "endTransformFeedback");
5303   if (IsContextLost()) return;
5304   auto& state = State();
5305   auto& tfo = *(state.mBoundTfo);
5306 
5307   if (!tfo.mActiveOrPaused) {
5308     EnqueueError(LOCAL_GL_INVALID_OPERATION,
5309                  "Transform Feedback is not active or paused.");
5310     return;
5311   }
5312 
5313   tfo.mActiveOrPaused = false;
5314   tfo.mActiveProgram->mActiveTfos.erase(&tfo);
5315   tfo.mActiveProgram = nullptr;
5316   tfo.mActiveProgramKeepAlive = nullptr;
5317   state.mTfActiveAndNotPaused = false;
5318   Run<RPROC(EndTransformFeedback)>();
5319 }
5320 
PauseTransformFeedback()5321 void ClientWebGLContext::PauseTransformFeedback() {
5322   const FuncScope funcScope(*this, "pauseTransformFeedback");
5323   if (IsContextLost()) return;
5324   auto& state = State();
5325   auto& tfo = *(state.mBoundTfo);
5326 
5327   if (!tfo.mActiveOrPaused) {
5328     EnqueueError(LOCAL_GL_INVALID_OPERATION,
5329                  "Transform Feedback is not active.");
5330     return;
5331   }
5332   if (!state.mTfActiveAndNotPaused) {
5333     EnqueueError(LOCAL_GL_INVALID_OPERATION,
5334                  "Transform Feedback is already paused.");
5335     return;
5336   }
5337 
5338   state.mTfActiveAndNotPaused = false;
5339   Run<RPROC(PauseTransformFeedback)>();
5340 }
5341 
ResumeTransformFeedback()5342 void ClientWebGLContext::ResumeTransformFeedback() {
5343   const FuncScope funcScope(*this, "resumeTransformFeedback");
5344   if (IsContextLost()) return;
5345   auto& state = State();
5346   auto& tfo = *(state.mBoundTfo);
5347 
5348   if (!tfo.mActiveOrPaused) {
5349     EnqueueError(LOCAL_GL_INVALID_OPERATION,
5350                  "Transform Feedback is not active and paused.");
5351     return;
5352   }
5353   if (state.mTfActiveAndNotPaused) {
5354     EnqueueError(LOCAL_GL_INVALID_OPERATION,
5355                  "Transform Feedback is not paused.");
5356     return;
5357   }
5358   if (state.mCurrentProgram != tfo.mActiveProgram) {
5359     EnqueueError(
5360         LOCAL_GL_INVALID_OPERATION,
5361         "Cannot Resume Transform Feedback with a program link result different"
5362         " from when Begin was called.");
5363     return;
5364   }
5365 
5366   state.mTfActiveAndNotPaused = true;
5367   Run<RPROC(ResumeTransformFeedback)>();
5368 }
5369 
SetFramebufferIsInOpaqueRAF(WebGLFramebufferJS * fb,bool value)5370 void ClientWebGLContext::SetFramebufferIsInOpaqueRAF(WebGLFramebufferJS* fb,
5371                                                      bool value) {
5372   fb->mInOpaqueRAF = value;
5373   Run<RPROC(SetFramebufferIsInOpaqueRAF)>(fb->mId, value);
5374 }
5375 
5376 // ---------------------------- Misc Extensions ----------------------------
DrawBuffers(const dom::Sequence<GLenum> & buffers)5377 void ClientWebGLContext::DrawBuffers(const dom::Sequence<GLenum>& buffers) {
5378   const auto range = MakeRange(buffers);
5379   const auto vec = std::vector<GLenum>(range.begin().get(), range.end().get());
5380   Run<RPROC(DrawBuffers)>(vec);
5381 }
5382 
EnqueueErrorImpl(const GLenum error,const nsACString & text) const5383 void ClientWebGLContext::EnqueueErrorImpl(const GLenum error,
5384                                           const nsACString& text) const {
5385   if (!mNotLost) return;  // Ignored if context is lost.
5386   Run<RPROC(GenerateError)>(error, ToString(text));
5387 }
5388 
RequestExtension(const WebGLExtensionID ext) const5389 void ClientWebGLContext::RequestExtension(const WebGLExtensionID ext) const {
5390   Run<RPROC(RequestExtension)>(ext);
5391 }
5392 
5393 // -
5394 
IsExtensionForbiddenForCaller(const WebGLExtensionID ext,const dom::CallerType callerType)5395 static bool IsExtensionForbiddenForCaller(const WebGLExtensionID ext,
5396                                           const dom::CallerType callerType) {
5397   if (callerType == dom::CallerType::System) return false;
5398 
5399   if (StaticPrefs::webgl_enable_privileged_extensions()) return false;
5400 
5401   const bool resistFingerprinting =
5402       nsContentUtils::ShouldResistFingerprinting();
5403   switch (ext) {
5404     case WebGLExtensionID::MOZ_debug:
5405       return true;
5406 
5407     case WebGLExtensionID::WEBGL_debug_renderer_info:
5408       return resistFingerprinting ||
5409              !StaticPrefs::webgl_enable_debug_renderer_info();
5410 
5411     case WebGLExtensionID::WEBGL_debug_shaders:
5412       return resistFingerprinting;
5413 
5414     default:
5415       return false;
5416   }
5417 }
5418 
IsSupported(const WebGLExtensionID ext,const dom::CallerType callerType) const5419 bool ClientWebGLContext::IsSupported(const WebGLExtensionID ext,
5420                                      const dom::CallerType callerType) const {
5421   if (IsExtensionForbiddenForCaller(ext, callerType)) return false;
5422   const auto& limits = Limits();
5423   return limits.supportedExtensions[ext];
5424 }
5425 
GetSupportedExtensions(dom::Nullable<nsTArray<nsString>> & retval,const dom::CallerType callerType) const5426 void ClientWebGLContext::GetSupportedExtensions(
5427     dom::Nullable<nsTArray<nsString>>& retval,
5428     const dom::CallerType callerType) const {
5429   retval.SetNull();
5430   if (!mNotLost) return;
5431 
5432   auto& retarr = retval.SetValue();
5433   for (const auto i : MakeEnumeratedRange(WebGLExtensionID::Max)) {
5434     if (!IsSupported(i, callerType)) continue;
5435 
5436     const auto& extStr = GetExtensionName(i);
5437     retarr.AppendElement(NS_ConvertUTF8toUTF16(extStr));
5438   }
5439 }
5440 
5441 // -
5442 
GetSupportedProfilesASTC(dom::Nullable<nsTArray<nsString>> & retval) const5443 void ClientWebGLContext::GetSupportedProfilesASTC(
5444     dom::Nullable<nsTArray<nsString>>& retval) const {
5445   retval.SetNull();
5446   if (!mNotLost) return;
5447   const auto& limits = Limits();
5448 
5449   auto& retarr = retval.SetValue();
5450   retarr.AppendElement(u"ldr"_ns);
5451   if (limits.astcHdr) {
5452     retarr.AppendElement(u"hdr"_ns);
5453   }
5454 }
5455 
5456 // -
5457 
ShouldResistFingerprinting() const5458 bool ClientWebGLContext::ShouldResistFingerprinting() const {
5459   if (mCanvasElement) {
5460     // If we're constructed from a canvas element
5461     return nsContentUtils::ShouldResistFingerprinting(GetOwnerDoc());
5462   }
5463   if (mOffscreenCanvas) {
5464     // If we're constructed from an offscreen canvas
5465     return mOffscreenCanvas->ShouldResistFingerprinting();
5466   }
5467   // Last resort, just check the global preference
5468   return nsContentUtils::ShouldResistFingerprinting();
5469 }
5470 
GetPrincipalHashValue() const5471 uint32_t ClientWebGLContext::GetPrincipalHashValue() const {
5472   if (mCanvasElement) {
5473     return mCanvasElement->NodePrincipal()->GetHashValue();
5474   }
5475   if (mOffscreenCanvas) {
5476     nsIGlobalObject* global = mOffscreenCanvas->GetOwnerGlobal();
5477     if (global) {
5478       return global->GetPrincipalHashValue();
5479     }
5480   }
5481   return 0;
5482 }
5483 
5484 // ---------------------------
5485 
EnqueueError_ArgEnum(const char * const argName,const GLenum val) const5486 void ClientWebGLContext::EnqueueError_ArgEnum(const char* const argName,
5487                                               const GLenum val) const {
5488   EnqueueError(LOCAL_GL_INVALID_ENUM, "Bad `%s`: 0x%04x", argName, val);
5489 }
5490 
5491 // -
5492 // WebGLProgramJS
5493 
AttachShader(WebGLProgramJS & prog,WebGLShaderJS & shader) const5494 void ClientWebGLContext::AttachShader(WebGLProgramJS& prog,
5495                                       WebGLShaderJS& shader) const {
5496   const FuncScope funcScope(*this, "attachShader");
5497   if (IsContextLost()) return;
5498   if (!prog.ValidateUsable(*this, "program")) return;
5499   if (!shader.ValidateUsable(*this, "shader")) return;
5500 
5501   auto& slot = *MaybeFind(prog.mNextLink_Shaders, shader.mType);
5502   if (slot.shader) {
5503     if (&shader == slot.shader) {
5504       EnqueueError(LOCAL_GL_INVALID_OPERATION, "`shader` is already attached.");
5505     } else {
5506       EnqueueError(LOCAL_GL_INVALID_OPERATION,
5507                    "Only one of each type of"
5508                    " shader may be attached to a program.");
5509     }
5510     return;
5511   }
5512   slot = {&shader, shader.mKeepAliveWeak.lock()};
5513 
5514   Run<RPROC(AttachShader)>(prog.mId, shader.mId);
5515 }
5516 
BindAttribLocation(WebGLProgramJS & prog,const GLuint location,const nsAString & name) const5517 void ClientWebGLContext::BindAttribLocation(WebGLProgramJS& prog,
5518                                             const GLuint location,
5519                                             const nsAString& name) const {
5520   const FuncScope funcScope(*this, "detachShader");
5521   if (IsContextLost()) return;
5522   if (!prog.ValidateUsable(*this, "program")) return;
5523 
5524   const auto& nameU8 = ToString(NS_ConvertUTF16toUTF8(name));
5525   Run<RPROC(BindAttribLocation)>(prog.mId, location, nameU8);
5526 }
5527 
DetachShader(WebGLProgramJS & prog,const WebGLShaderJS & shader) const5528 void ClientWebGLContext::DetachShader(WebGLProgramJS& prog,
5529                                       const WebGLShaderJS& shader) const {
5530   const FuncScope funcScope(*this, "detachShader");
5531   if (IsContextLost()) return;
5532   if (!prog.ValidateUsable(*this, "program")) return;
5533   if (!shader.ValidateUsable(*this, "shader")) return;
5534 
5535   auto& slot = *MaybeFind(prog.mNextLink_Shaders, shader.mType);
5536 
5537   if (slot.shader != &shader) {
5538     EnqueueError(LOCAL_GL_INVALID_OPERATION, "`shader` is not attached.");
5539     return;
5540   }
5541   slot = {};
5542 
5543   Run<RPROC(DetachShader)>(prog.mId, shader.mId);
5544 }
5545 
GetAttachedShaders(const WebGLProgramJS & prog,dom::Nullable<nsTArray<RefPtr<WebGLShaderJS>>> & retval) const5546 void ClientWebGLContext::GetAttachedShaders(
5547     const WebGLProgramJS& prog,
5548     dom::Nullable<nsTArray<RefPtr<WebGLShaderJS>>>& retval) const {
5549   const FuncScope funcScope(*this, "getAttachedShaders");
5550   if (IsContextLost()) return;
5551   if (!prog.ValidateUsable(*this, "program")) return;
5552 
5553   auto& arr = retval.SetValue();
5554   for (const auto& pair : prog.mNextLink_Shaders) {
5555     const auto& attachment = pair.second;
5556     if (!attachment.shader) continue;
5557     arr.AppendElement(attachment.shader);
5558   }
5559 }
5560 
LinkProgram(WebGLProgramJS & prog) const5561 void ClientWebGLContext::LinkProgram(WebGLProgramJS& prog) const {
5562   const FuncScope funcScope(*this, "linkProgram");
5563   if (IsContextLost()) return;
5564   if (!prog.ValidateUsable(*this, "program")) return;
5565 
5566   if (!prog.mActiveTfos.empty()) {
5567     EnqueueError(LOCAL_GL_INVALID_OPERATION,
5568                  "Program still in use by active or paused"
5569                  " Transform Feedback objects.");
5570     return;
5571   }
5572 
5573   prog.mResult = std::make_shared<webgl::LinkResult>();
5574   prog.mUniformLocByName = Nothing();
5575   prog.mUniformBlockBindings = {};
5576   Run<RPROC(LinkProgram)>(prog.mId);
5577 }
5578 
TransformFeedbackVaryings(WebGLProgramJS & prog,const dom::Sequence<nsString> & varyings,const GLenum bufferMode) const5579 void ClientWebGLContext::TransformFeedbackVaryings(
5580     WebGLProgramJS& prog, const dom::Sequence<nsString>& varyings,
5581     const GLenum bufferMode) const {
5582   const FuncScope funcScope(*this, "transformFeedbackVaryings");
5583   if (IsContextLost()) return;
5584   if (!prog.ValidateUsable(*this, "program")) return;
5585 
5586   std::vector<std::string> varyingsU8;
5587   varyingsU8.reserve(varyings.Length());
5588   for (const auto& cur : varyings) {
5589     const auto curU8 = ToString(NS_ConvertUTF16toUTF8(cur));
5590     varyingsU8.push_back(curU8);
5591   }
5592 
5593   Run<RPROC(TransformFeedbackVaryings)>(prog.mId, varyingsU8, bufferMode);
5594 }
5595 
UniformBlockBinding(WebGLProgramJS & prog,const GLuint blockIndex,const GLuint blockBinding) const5596 void ClientWebGLContext::UniformBlockBinding(WebGLProgramJS& prog,
5597                                              const GLuint blockIndex,
5598                                              const GLuint blockBinding) const {
5599   const FuncScope funcScope(*this, "uniformBlockBinding");
5600   if (IsContextLost()) return;
5601   if (!prog.ValidateUsable(*this, "program")) return;
5602   const auto& state = State();
5603 
5604   (void)GetLinkResult(prog);
5605   auto& list = prog.mUniformBlockBindings;
5606   if (blockIndex >= list.size()) {
5607     EnqueueError(
5608         LOCAL_GL_INVALID_VALUE,
5609         "`blockIndex` (%u) must be less than ACTIVE_UNIFORM_BLOCKS (%zu).",
5610         blockIndex, list.size());
5611     return;
5612   }
5613   if (blockBinding >= state.mBoundUbos.size()) {
5614     EnqueueError(LOCAL_GL_INVALID_VALUE,
5615                  "`blockBinding` (%u) must be less than "
5616                  "MAX_UNIFORM_BUFFER_BINDINGS (%zu).",
5617                  blockBinding, state.mBoundUbos.size());
5618     return;
5619   }
5620 
5621   list[blockIndex] = blockBinding;
5622   Run<RPROC(UniformBlockBinding)>(prog.mId, blockIndex, blockBinding);
5623 }
5624 
5625 // WebGLProgramJS link result reflection
5626 
GetActiveAttrib(const WebGLProgramJS & prog,const GLuint index)5627 already_AddRefed<WebGLActiveInfoJS> ClientWebGLContext::GetActiveAttrib(
5628     const WebGLProgramJS& prog, const GLuint index) {
5629   const FuncScope funcScope(*this, "getActiveAttrib");
5630   if (IsContextLost()) return nullptr;
5631   if (!prog.ValidateUsable(*this, "program")) return nullptr;
5632 
5633   const auto& res = GetLinkResult(prog);
5634   const auto& list = res.active.activeAttribs;
5635   if (index >= list.size()) {
5636     EnqueueError(LOCAL_GL_INVALID_VALUE, "`index` too large.");
5637     return nullptr;
5638   }
5639 
5640   const auto& info = list[index];
5641   return AsAddRefed(new WebGLActiveInfoJS(info));
5642 }
5643 
GetActiveUniform(const WebGLProgramJS & prog,const GLuint index)5644 already_AddRefed<WebGLActiveInfoJS> ClientWebGLContext::GetActiveUniform(
5645     const WebGLProgramJS& prog, const GLuint index) {
5646   const FuncScope funcScope(*this, "getActiveUniform");
5647   if (IsContextLost()) return nullptr;
5648   if (!prog.ValidateUsable(*this, "program")) return nullptr;
5649 
5650   const auto& res = GetLinkResult(prog);
5651   const auto& list = res.active.activeUniforms;
5652   if (index >= list.size()) {
5653     EnqueueError(LOCAL_GL_INVALID_VALUE, "`index` too large.");
5654     return nullptr;
5655   }
5656 
5657   const auto& info = list[index];
5658   return AsAddRefed(new WebGLActiveInfoJS(info));
5659 }
5660 
GetActiveUniformBlockName(const WebGLProgramJS & prog,const GLuint index,nsAString & retval) const5661 void ClientWebGLContext::GetActiveUniformBlockName(const WebGLProgramJS& prog,
5662                                                    const GLuint index,
5663                                                    nsAString& retval) const {
5664   retval.SetIsVoid(true);
5665   const FuncScope funcScope(*this, "getActiveUniformBlockName");
5666   if (IsContextLost()) return;
5667   if (!prog.ValidateUsable(*this, "program")) return;
5668 
5669   const auto& res = GetLinkResult(prog);
5670   if (!res.success) {
5671     EnqueueError(LOCAL_GL_INVALID_OPERATION, "Program has not been linked.");
5672     return;
5673   }
5674 
5675   const auto& list = res.active.activeUniformBlocks;
5676   if (index >= list.size()) {
5677     EnqueueError(LOCAL_GL_INVALID_VALUE, "`index` too large.");
5678     return;
5679   }
5680 
5681   const auto& block = list[index];
5682   CopyUTF8toUTF16(block.name, retval);
5683 }
5684 
GetActiveUniformBlockParameter(JSContext * const cx,const WebGLProgramJS & prog,const GLuint index,const GLenum pname,JS::MutableHandle<JS::Value> retval,ErrorResult & rv)5685 void ClientWebGLContext::GetActiveUniformBlockParameter(
5686     JSContext* const cx, const WebGLProgramJS& prog, const GLuint index,
5687     const GLenum pname, JS::MutableHandle<JS::Value> retval, ErrorResult& rv) {
5688   retval.set(JS::NullValue());
5689   const FuncScope funcScope(*this, "getActiveUniformBlockParameter");
5690   if (IsContextLost()) return;
5691   if (!prog.ValidateUsable(*this, "program")) return;
5692 
5693   const auto& res = GetLinkResult(prog);
5694   const auto& list = res.active.activeUniformBlocks;
5695   if (index >= list.size()) {
5696     EnqueueError(LOCAL_GL_INVALID_VALUE, "`index` too large.");
5697     return;
5698   }
5699   const auto& block = list[index];
5700 
5701   retval.set([&]() -> JS::Value {
5702     switch (pname) {
5703       case LOCAL_GL_UNIFORM_BLOCK_BINDING:
5704         return JS::NumberValue(prog.mUniformBlockBindings[index]);
5705 
5706       case LOCAL_GL_UNIFORM_BLOCK_DATA_SIZE:
5707         return JS::NumberValue(block.dataSize);
5708 
5709       case LOCAL_GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS:
5710         return JS::NumberValue(block.activeUniformIndices.size());
5711 
5712       case LOCAL_GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES: {
5713         const auto& indices = block.activeUniformIndices;
5714         JS::RootedObject obj(cx, dom::Uint32Array::Create(
5715                                      cx, this, indices.size(), indices.data()));
5716         if (!obj) {
5717           rv = NS_ERROR_OUT_OF_MEMORY;
5718         }
5719         return JS::ObjectOrNullValue(obj);
5720       }
5721 
5722       case LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER:
5723         return JS::BooleanValue(block.referencedByVertexShader);
5724 
5725       case LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER:
5726         return JS::BooleanValue(block.referencedByFragmentShader);
5727 
5728       default:
5729         EnqueueError_ArgEnum("pname", pname);
5730         return JS::NullValue();
5731     }
5732   }());
5733 }
5734 
GetActiveUniforms(JSContext * const cx,const WebGLProgramJS & prog,const dom::Sequence<GLuint> & uniformIndices,const GLenum pname,JS::MutableHandle<JS::Value> retval) const5735 void ClientWebGLContext::GetActiveUniforms(
5736     JSContext* const cx, const WebGLProgramJS& prog,
5737     const dom::Sequence<GLuint>& uniformIndices, const GLenum pname,
5738     JS::MutableHandle<JS::Value> retval) const {
5739   retval.set(JS::NullValue());
5740   const FuncScope funcScope(*this, "getActiveUniforms");
5741   if (IsContextLost()) return;
5742   if (!prog.ValidateUsable(*this, "program")) return;
5743 
5744   const auto& res = GetLinkResult(prog);
5745   const auto& list = res.active.activeUniforms;
5746 
5747   const auto count = uniformIndices.Length();
5748   JS::Rooted<JSObject*> array(cx, JS::NewArrayObject(cx, count));
5749   if (!array) return;  // Just bail.
5750 
5751   for (const auto i : IntegerRange(count)) {
5752     const auto index = uniformIndices[i];
5753     if (index >= list.size()) {
5754       EnqueueError(LOCAL_GL_INVALID_VALUE,
5755                    "`uniformIndices[%u]`: `%u` too large.", i, index);
5756       return;
5757     }
5758     const auto& uniform = list[index];
5759 
5760     JS::RootedValue value(cx);
5761     switch (pname) {
5762       case LOCAL_GL_UNIFORM_TYPE:
5763         value = JS::NumberValue(uniform.elemType);
5764         break;
5765 
5766       case LOCAL_GL_UNIFORM_SIZE:
5767         value = JS::NumberValue(uniform.elemCount);
5768         break;
5769 
5770       case LOCAL_GL_UNIFORM_BLOCK_INDEX:
5771         value = JS::NumberValue(uniform.block_index);
5772         break;
5773 
5774       case LOCAL_GL_UNIFORM_OFFSET:
5775         value = JS::NumberValue(uniform.block_offset);
5776         break;
5777 
5778       case LOCAL_GL_UNIFORM_ARRAY_STRIDE:
5779         value = JS::NumberValue(uniform.block_arrayStride);
5780         break;
5781 
5782       case LOCAL_GL_UNIFORM_MATRIX_STRIDE:
5783         value = JS::NumberValue(uniform.block_matrixStride);
5784         break;
5785 
5786       case LOCAL_GL_UNIFORM_IS_ROW_MAJOR:
5787         value = JS::BooleanValue(uniform.block_isRowMajor);
5788         break;
5789 
5790       default:
5791         EnqueueError_ArgEnum("pname", pname);
5792         return;
5793     }
5794     if (!JS_DefineElement(cx, array, i, value, JSPROP_ENUMERATE)) return;
5795   }
5796 
5797   retval.setObject(*array);
5798 }
5799 
5800 already_AddRefed<WebGLActiveInfoJS>
GetTransformFeedbackVarying(const WebGLProgramJS & prog,const GLuint index)5801 ClientWebGLContext::GetTransformFeedbackVarying(const WebGLProgramJS& prog,
5802                                                 const GLuint index) {
5803   const FuncScope funcScope(*this, "getTransformFeedbackVarying");
5804   if (IsContextLost()) return nullptr;
5805   if (!prog.ValidateUsable(*this, "program")) return nullptr;
5806 
5807   const auto& res = GetLinkResult(prog);
5808   const auto& list = res.active.activeTfVaryings;
5809   if (index >= list.size()) {
5810     EnqueueError(LOCAL_GL_INVALID_VALUE, "`index` too large.");
5811     return nullptr;
5812   }
5813 
5814   const auto& info = list[index];
5815   return AsAddRefed(new WebGLActiveInfoJS(info));
5816 }
5817 
GetAttribLocation(const WebGLProgramJS & prog,const nsAString & name) const5818 GLint ClientWebGLContext::GetAttribLocation(const WebGLProgramJS& prog,
5819                                             const nsAString& name) const {
5820   const FuncScope funcScope(*this, "getAttribLocation");
5821   if (IsContextLost()) return -1;
5822   if (!prog.ValidateUsable(*this, "program")) return -1;
5823 
5824   const auto nameU8 = ToString(NS_ConvertUTF16toUTF8(name));
5825   const auto& res = GetLinkResult(prog);
5826   for (const auto& cur : res.active.activeAttribs) {
5827     if (cur.name == nameU8) return cur.location;
5828   }
5829 
5830   const auto err = CheckGLSLVariableName(mIsWebGL2, nameU8);
5831   if (err) {
5832     EnqueueError(err->type, "%s", err->info.c_str());
5833   }
5834   return -1;
5835 }
5836 
GetFragDataLocation(const WebGLProgramJS & prog,const nsAString & name) const5837 GLint ClientWebGLContext::GetFragDataLocation(const WebGLProgramJS& prog,
5838                                               const nsAString& name) const {
5839   const FuncScope funcScope(*this, "getFragDataLocation");
5840   if (IsContextLost()) return -1;
5841   if (!prog.ValidateUsable(*this, "program")) return -1;
5842 
5843   const auto nameU8 = ToString(NS_ConvertUTF16toUTF8(name));
5844 
5845   const auto err = CheckGLSLVariableName(mIsWebGL2, nameU8);
5846   if (err) {
5847     EnqueueError(*err);
5848     return -1;
5849   }
5850 
5851   return [&]() {
5852     const auto& inProcess = mNotLost->inProcess;
5853     if (inProcess) {
5854       return inProcess->GetFragDataLocation(prog.mId, nameU8);
5855     }
5856     const auto& child = mNotLost->outOfProcess;
5857     child->FlushPendingCmds();
5858     GLint ret = {};
5859     if (!child->SendGetFragDataLocation(prog.mId, nameU8, &ret)) {
5860       ret = {};
5861     }
5862     return ret;
5863   }();
5864 }
5865 
GetUniformBlockIndex(const WebGLProgramJS & prog,const nsAString & blockName) const5866 GLuint ClientWebGLContext::GetUniformBlockIndex(
5867     const WebGLProgramJS& prog, const nsAString& blockName) const {
5868   const FuncScope funcScope(*this, "getUniformBlockIndex");
5869   if (IsContextLost()) return LOCAL_GL_INVALID_INDEX;
5870   if (!prog.ValidateUsable(*this, "program")) return LOCAL_GL_INVALID_INDEX;
5871 
5872   const auto nameU8 = ToString(NS_ConvertUTF16toUTF8(blockName));
5873 
5874   const auto& res = GetLinkResult(prog);
5875   const auto& list = res.active.activeUniformBlocks;
5876   for (const auto i : IntegerRange(list.size())) {
5877     const auto& cur = list[i];
5878     if (cur.name == nameU8) {
5879       return i;
5880     }
5881   }
5882   return LOCAL_GL_INVALID_INDEX;
5883 }
5884 
GetUniformIndices(const WebGLProgramJS & prog,const dom::Sequence<nsString> & uniformNames,dom::Nullable<nsTArray<GLuint>> & retval) const5885 void ClientWebGLContext::GetUniformIndices(
5886     const WebGLProgramJS& prog, const dom::Sequence<nsString>& uniformNames,
5887     dom::Nullable<nsTArray<GLuint>>& retval) const {
5888   const FuncScope funcScope(*this, "getUniformIndices");
5889   if (IsContextLost()) return;
5890   if (!prog.ValidateUsable(*this, "program")) return;
5891 
5892   const auto& res = GetLinkResult(prog);
5893   auto ret = nsTArray<GLuint>(uniformNames.Length());
5894 
5895   std::unordered_map<std::string, size_t> retIdByName;
5896   retIdByName.reserve(ret.Length());
5897 
5898   for (const auto i : IntegerRange(uniformNames.Length())) {
5899     const auto& name = uniformNames[i];
5900     auto nameU8 = ToString(NS_ConvertUTF16toUTF8(name));
5901     retIdByName.insert({std::move(nameU8), i});
5902     ret.AppendElement(LOCAL_GL_INVALID_INDEX);
5903   }
5904 
5905   GLuint i = 0;
5906   for (const auto& cur : res.active.activeUniforms) {
5907     const auto maybeRetId = MaybeFind(retIdByName, cur.name);
5908     if (maybeRetId) {
5909       ret[*maybeRetId] = i;
5910     }
5911     i += 1;
5912   }
5913 
5914   retval.SetValue(std::move(ret));
5915 }
5916 
GetUniformLocation(const WebGLProgramJS & prog,const nsAString & name) const5917 already_AddRefed<WebGLUniformLocationJS> ClientWebGLContext::GetUniformLocation(
5918     const WebGLProgramJS& prog, const nsAString& name) const {
5919   const FuncScope funcScope(*this, "getUniformLocation");
5920   if (IsContextLost()) return nullptr;
5921   if (!prog.ValidateUsable(*this, "program")) return nullptr;
5922 
5923   const auto& res = GetLinkResult(prog);
5924 
5925   if (!prog.mUniformLocByName) {
5926     // Cache a map from name->location.
5927     // Since the only way to set uniforms requires calling GetUniformLocation,
5928     // we expect apps to query most active uniforms once for each scalar or
5929     // array. NB: Uniform array setters do have overflow semantics, even though
5930     // uniform locations aren't guaranteed contiguous, but GetUniformLocation
5931     // must still be called once per array.
5932     prog.mUniformLocByName.emplace();
5933 
5934     for (const auto& activeUniform : res.active.activeUniforms) {
5935       if (activeUniform.block_index != -1) continue;
5936 
5937       auto locName = activeUniform.name;
5938       const auto indexed = webgl::ParseIndexed(locName);
5939       if (indexed) {
5940         locName = indexed->name;
5941       }
5942 
5943       const auto err = CheckGLSLVariableName(mIsWebGL2, locName);
5944       if (err) continue;
5945 
5946       const auto baseLength = locName.size();
5947       for (const auto& pair : activeUniform.locByIndex) {
5948         if (indexed) {
5949           locName.erase(baseLength);  // Erase previous "[N]".
5950           locName += '[';
5951           locName += std::to_string(pair.first);
5952           locName += ']';
5953         }
5954         const auto locInfo =
5955             WebGLProgramJS::UniformLocInfo{pair.second, activeUniform.elemType};
5956         prog.mUniformLocByName->insert({locName, locInfo});
5957       }
5958     }
5959   }
5960   const auto& locByName = *(prog.mUniformLocByName);
5961 
5962   const auto nameU8 = ToString(NS_ConvertUTF16toUTF8(name));
5963   auto loc = MaybeFind(locByName, nameU8);
5964   if (!loc) {
5965     loc = MaybeFind(locByName, nameU8 + "[0]");
5966   }
5967   if (!loc) {
5968     const auto err = CheckGLSLVariableName(mIsWebGL2, nameU8);
5969     if (err) {
5970       EnqueueError(err->type, "%s", err->info.c_str());
5971     }
5972     return nullptr;
5973   }
5974 
5975   return AsAddRefed(new WebGLUniformLocationJS(*this, prog.mResult,
5976                                                loc->location, loc->elemType));
5977 }
5978 
ValidUploadElemTypes(const GLenum elemType)5979 std::array<uint16_t, 3> ValidUploadElemTypes(const GLenum elemType) {
5980   std::vector<GLenum> ret;
5981   switch (elemType) {
5982     case LOCAL_GL_BOOL:
5983       ret = {LOCAL_GL_FLOAT, LOCAL_GL_INT, LOCAL_GL_UNSIGNED_INT};
5984       break;
5985     case LOCAL_GL_BOOL_VEC2:
5986       ret = {LOCAL_GL_FLOAT_VEC2, LOCAL_GL_INT_VEC2,
5987              LOCAL_GL_UNSIGNED_INT_VEC2};
5988       break;
5989     case LOCAL_GL_BOOL_VEC3:
5990       ret = {LOCAL_GL_FLOAT_VEC3, LOCAL_GL_INT_VEC3,
5991              LOCAL_GL_UNSIGNED_INT_VEC3};
5992       break;
5993     case LOCAL_GL_BOOL_VEC4:
5994       ret = {LOCAL_GL_FLOAT_VEC4, LOCAL_GL_INT_VEC4,
5995              LOCAL_GL_UNSIGNED_INT_VEC4};
5996       break;
5997 
5998     case LOCAL_GL_SAMPLER_2D:
5999     case LOCAL_GL_SAMPLER_3D:
6000     case LOCAL_GL_SAMPLER_CUBE:
6001     case LOCAL_GL_SAMPLER_2D_SHADOW:
6002     case LOCAL_GL_SAMPLER_2D_ARRAY:
6003     case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
6004     case LOCAL_GL_SAMPLER_CUBE_SHADOW:
6005     case LOCAL_GL_INT_SAMPLER_2D:
6006     case LOCAL_GL_INT_SAMPLER_3D:
6007     case LOCAL_GL_INT_SAMPLER_CUBE:
6008     case LOCAL_GL_INT_SAMPLER_2D_ARRAY:
6009     case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
6010     case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
6011     case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
6012     case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
6013       ret = {LOCAL_GL_INT};
6014       break;
6015 
6016     default:
6017       ret = {elemType};
6018       break;
6019   }
6020 
6021   std::array<uint16_t, 3> arr = {};
6022   MOZ_ASSERT(arr[2] == 0);
6023   for (const auto i : IntegerRange(ret.size())) {
6024     arr[i] = AssertedCast<uint16_t>(ret[i]);
6025   }
6026   return arr;
6027 }
6028 
GetProgramInfoLog(const WebGLProgramJS & prog,nsAString & retval) const6029 void ClientWebGLContext::GetProgramInfoLog(const WebGLProgramJS& prog,
6030                                            nsAString& retval) const {
6031   retval.SetIsVoid(true);
6032   const FuncScope funcScope(*this, "getProgramInfoLog");
6033   if (IsContextLost()) return;
6034   if (!prog.ValidateUsable(*this, "program")) return;
6035 
6036   const auto& res = GetLinkResult(prog);
6037   CopyUTF8toUTF16(res.log, retval);
6038 }
6039 
GetProgramParameter(JSContext * const js,const WebGLProgramJS & prog,const GLenum pname,JS::MutableHandle<JS::Value> retval) const6040 void ClientWebGLContext::GetProgramParameter(
6041     JSContext* const js, const WebGLProgramJS& prog, const GLenum pname,
6042     JS::MutableHandle<JS::Value> retval) const {
6043   retval.set(JS::NullValue());
6044   const FuncScope funcScope(*this, "getProgramParameter");
6045   if (IsContextLost()) return;
6046   if (!prog.ValidateUsable(*this, "program")) return;
6047 
6048   retval.set([&]() -> JS::Value {
6049     switch (pname) {
6050       case LOCAL_GL_DELETE_STATUS:
6051         // "Is flagged for deletion?"
6052         return JS::BooleanValue(!prog.mKeepAlive);
6053       case LOCAL_GL_VALIDATE_STATUS:
6054         return JS::BooleanValue(prog.mLastValidate);
6055       case LOCAL_GL_ATTACHED_SHADERS: {
6056         size_t shaders = 0;
6057         for (const auto& pair : prog.mNextLink_Shaders) {
6058           const auto& slot = pair.second;
6059           if (slot.shader) {
6060             shaders += 1;
6061           }
6062         }
6063         return JS::NumberValue(shaders);
6064       }
6065       default:
6066         break;
6067     }
6068 
6069     const auto& res = GetLinkResult(prog);
6070 
6071     switch (pname) {
6072       case LOCAL_GL_LINK_STATUS:
6073         return JS::BooleanValue(res.success);
6074 
6075       case LOCAL_GL_ACTIVE_ATTRIBUTES:
6076         return JS::NumberValue(res.active.activeAttribs.size());
6077 
6078       case LOCAL_GL_ACTIVE_UNIFORMS:
6079         return JS::NumberValue(res.active.activeUniforms.size());
6080 
6081       case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_MODE:
6082         if (!mIsWebGL2) break;
6083         return JS::NumberValue(res.tfBufferMode);
6084 
6085       case LOCAL_GL_TRANSFORM_FEEDBACK_VARYINGS:
6086         if (!mIsWebGL2) break;
6087         return JS::NumberValue(res.active.activeTfVaryings.size());
6088 
6089       case LOCAL_GL_ACTIVE_UNIFORM_BLOCKS:
6090         if (!mIsWebGL2) break;
6091         return JS::NumberValue(res.active.activeUniformBlocks.size());
6092 
6093       default:
6094         break;
6095     }
6096     EnqueueError_ArgEnum("pname", pname);
6097     return JS::NullValue();
6098   }());
6099 }
6100 
6101 // -
6102 // WebGLShaderJS
6103 
CompileShader(WebGLShaderJS & shader) const6104 void ClientWebGLContext::CompileShader(WebGLShaderJS& shader) const {
6105   const FuncScope funcScope(*this, "compileShader");
6106   if (IsContextLost()) return;
6107   if (!shader.ValidateUsable(*this, "shader")) return;
6108 
6109   shader.mResult = {};
6110   Run<RPROC(CompileShader)>(shader.mId);
6111 }
6112 
GetShaderInfoLog(const WebGLShaderJS & shader,nsAString & retval) const6113 void ClientWebGLContext::GetShaderInfoLog(const WebGLShaderJS& shader,
6114                                           nsAString& retval) const {
6115   retval.SetIsVoid(true);
6116   const FuncScope funcScope(*this, "getShaderInfoLog");
6117   if (IsContextLost()) return;
6118   if (!shader.ValidateUsable(*this, "shader")) return;
6119 
6120   const auto& result = GetCompileResult(shader);
6121   CopyUTF8toUTF16(result.log, retval);
6122 }
6123 
GetShaderParameter(JSContext * const cx,const WebGLShaderJS & shader,const GLenum pname,JS::MutableHandle<JS::Value> retval) const6124 void ClientWebGLContext::GetShaderParameter(
6125     JSContext* const cx, const WebGLShaderJS& shader, const GLenum pname,
6126     JS::MutableHandle<JS::Value> retval) const {
6127   retval.set(JS::NullValue());
6128   const FuncScope funcScope(*this, "getShaderParameter");
6129   if (IsContextLost()) return;
6130   if (!shader.ValidateUsable(*this, "shader")) return;
6131 
6132   retval.set([&]() -> JS::Value {
6133     switch (pname) {
6134       case LOCAL_GL_SHADER_TYPE:
6135         return JS::NumberValue(shader.mType);
6136 
6137       case LOCAL_GL_DELETE_STATUS:  // "Is flagged for deletion?"
6138         return JS::BooleanValue(!shader.mKeepAlive);
6139 
6140       case LOCAL_GL_COMPILE_STATUS: {
6141         const auto& result = GetCompileResult(shader);
6142         return JS::BooleanValue(result.success);
6143       }
6144 
6145       default:
6146         EnqueueError_ArgEnum("pname", pname);
6147         return JS::NullValue();
6148     }
6149   }());
6150 }
6151 
GetShaderSource(const WebGLShaderJS & shader,nsAString & retval) const6152 void ClientWebGLContext::GetShaderSource(const WebGLShaderJS& shader,
6153                                          nsAString& retval) const {
6154   retval.SetIsVoid(true);
6155   const FuncScope funcScope(*this, "getShaderSource");
6156   if (IsContextLost()) return;
6157   if (!shader.ValidateUsable(*this, "shader")) return;
6158 
6159   CopyUTF8toUTF16(shader.mSource, retval);
6160 }
6161 
GetTranslatedShaderSource(const WebGLShaderJS & shader,nsAString & retval) const6162 void ClientWebGLContext::GetTranslatedShaderSource(const WebGLShaderJS& shader,
6163                                                    nsAString& retval) const {
6164   retval.SetIsVoid(true);
6165   const FuncScope funcScope(*this, "getTranslatedShaderSource");
6166   if (IsContextLost()) return;
6167   if (!shader.ValidateUsable(*this, "shader")) return;
6168 
6169   const auto& result = GetCompileResult(shader);
6170   CopyUTF8toUTF16(result.translatedSource, retval);
6171 }
6172 
ShaderSource(WebGLShaderJS & shader,const nsAString & sourceU16) const6173 void ClientWebGLContext::ShaderSource(WebGLShaderJS& shader,
6174                                       const nsAString& sourceU16) const {
6175   const FuncScope funcScope(*this, "shaderSource");
6176   if (IsContextLost()) return;
6177   if (!shader.ValidateUsable(*this, "shader")) return;
6178 
6179   shader.mSource = ToString(NS_ConvertUTF16toUTF8(sourceU16));
6180   Run<RPROC(ShaderSource)>(shader.mId, shader.mSource);
6181 }
6182 
6183 // -
6184 
GetCompileResult(const WebGLShaderJS & shader) const6185 const webgl::CompileResult& ClientWebGLContext::GetCompileResult(
6186     const WebGLShaderJS& shader) const {
6187   if (shader.mResult.pending) {
6188     shader.mResult = [&]() {
6189       const auto& inProcess = mNotLost->inProcess;
6190       if (inProcess) {
6191         return inProcess->GetCompileResult(shader.mId);
6192       }
6193       const auto& child = mNotLost->outOfProcess;
6194       child->FlushPendingCmds();
6195       webgl::CompileResult ret = {};
6196       if (!child->SendGetCompileResult(shader.mId, &ret)) {
6197         ret = {};
6198       }
6199       return ret;
6200     }();
6201   }
6202   return shader.mResult;
6203 }
6204 
GetLinkResult(const WebGLProgramJS & prog) const6205 const webgl::LinkResult& ClientWebGLContext::GetLinkResult(
6206     const WebGLProgramJS& prog) const {
6207   if (prog.mResult->pending) {
6208     const auto notLost =
6209         mNotLost;  // Hold a strong-ref to prevent LoseContext=>UAF.
6210     if (!notLost) return *(prog.mResult);
6211 
6212     *(prog.mResult) = [&]() {
6213       const auto& inProcess = mNotLost->inProcess;
6214       if (inProcess) {
6215         return inProcess->GetLinkResult(prog.mId);
6216       }
6217       const auto& child = mNotLost->outOfProcess;
6218       child->FlushPendingCmds();
6219       webgl::LinkResult ret;
6220       if (!child->SendGetLinkResult(prog.mId, &ret)) {
6221         ret = {};
6222       }
6223       return ret;
6224     }();
6225 
6226     prog.mUniformBlockBindings.resize(
6227         prog.mResult->active.activeUniformBlocks.size());
6228 
6229     auto& state = State();
6230     if (state.mCurrentProgram == &prog && prog.mResult->success) {
6231       state.mActiveLinkResult = prog.mResult;
6232     }
6233   }
6234   return *(prog.mResult);
6235 }
6236 
6237 #undef RPROC
6238 
6239 // ---------------------------
6240 
ValidateArrayBufferView(const dom::ArrayBufferView & view,GLuint elemOffset,GLuint elemCountOverride,const GLenum errorEnum,uint8_t ** const out_bytes,size_t * const out_byteLen) const6241 bool ClientWebGLContext::ValidateArrayBufferView(
6242     const dom::ArrayBufferView& view, GLuint elemOffset,
6243     GLuint elemCountOverride, const GLenum errorEnum, uint8_t** const out_bytes,
6244     size_t* const out_byteLen) const {
6245   view.ComputeState();
6246   uint8_t* const bytes = view.Data();
6247   const size_t byteLen = view.Length();
6248 
6249   const auto& elemSize = SizeOfViewElem(view);
6250 
6251   size_t elemCount = byteLen / elemSize;
6252   if (elemOffset > elemCount) {
6253     EnqueueError(errorEnum, "Invalid offset into ArrayBufferView.");
6254     return false;
6255   }
6256   elemCount -= elemOffset;
6257 
6258   if (elemCountOverride) {
6259     if (elemCountOverride > elemCount) {
6260       EnqueueError(errorEnum, "Invalid sub-length for ArrayBufferView.");
6261       return false;
6262     }
6263     elemCount = elemCountOverride;
6264   }
6265 
6266   *out_bytes = bytes + (elemOffset * elemSize);
6267   *out_byteLen = elemCount * elemSize;
6268   return true;
6269 }
6270 
6271 // ---------------------------
6272 
ObjectJS(const ClientWebGLContext & webgl)6273 webgl::ObjectJS::ObjectJS(const ClientWebGLContext& webgl)
6274     : mGeneration(webgl.mNotLost), mId(webgl.mNotLost->state.NextId()) {}
6275 
6276 // -
6277 
WebGLFramebufferJS(const ClientWebGLContext & webgl,bool opaque)6278 WebGLFramebufferJS::WebGLFramebufferJS(const ClientWebGLContext& webgl,
6279                                        bool opaque)
6280     : webgl::ObjectJS(webgl), mOpaque(opaque) {
6281   (void)mAttachments[LOCAL_GL_DEPTH_ATTACHMENT];
6282   (void)mAttachments[LOCAL_GL_STENCIL_ATTACHMENT];
6283   if (!webgl.mIsWebGL2) {
6284     (void)mAttachments[LOCAL_GL_DEPTH_STENCIL_ATTACHMENT];
6285   }
6286 
6287   EnsureColorAttachments();
6288 }
6289 
EnsureColorAttachments()6290 void WebGLFramebufferJS::EnsureColorAttachments() {
6291   const auto& webgl = Context();
6292   const auto& limits = webgl->Limits();
6293   auto maxColorDrawBuffers = limits.maxColorDrawBuffers;
6294   if (!webgl->mIsWebGL2 &&
6295       !webgl->IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers)) {
6296     maxColorDrawBuffers = 1;
6297   }
6298   for (const auto i : IntegerRange(maxColorDrawBuffers)) {
6299     (void)mAttachments[LOCAL_GL_COLOR_ATTACHMENT0 + i];
6300   }
6301 }
6302 
WebGLProgramJS(const ClientWebGLContext & webgl)6303 WebGLProgramJS::WebGLProgramJS(const ClientWebGLContext& webgl)
6304     : webgl::ObjectJS(webgl),
6305       mKeepAlive(std::make_shared<webgl::ProgramKeepAlive>(*this)),
6306       mKeepAliveWeak(mKeepAlive) {
6307   (void)mNextLink_Shaders[LOCAL_GL_VERTEX_SHADER];
6308   (void)mNextLink_Shaders[LOCAL_GL_FRAGMENT_SHADER];
6309 
6310   mResult = std::make_shared<webgl::LinkResult>();
6311 }
6312 
WebGLShaderJS(const ClientWebGLContext & webgl,const GLenum type)6313 WebGLShaderJS::WebGLShaderJS(const ClientWebGLContext& webgl, const GLenum type)
6314     : webgl::ObjectJS(webgl),
6315       mType(type),
6316       mKeepAlive(std::make_shared<webgl::ShaderKeepAlive>(*this)),
6317       mKeepAliveWeak(mKeepAlive) {}
6318 
WebGLTransformFeedbackJS(const ClientWebGLContext & webgl)6319 WebGLTransformFeedbackJS::WebGLTransformFeedbackJS(
6320     const ClientWebGLContext& webgl)
6321     : webgl::ObjectJS(webgl),
6322       mAttribBuffers(webgl::kMaxTransformFeedbackSeparateAttribs) {}
6323 
WebGLVertexArrayJS(const ClientWebGLContext & webgl)6324 WebGLVertexArrayJS::WebGLVertexArrayJS(const ClientWebGLContext& webgl)
6325     : webgl::ObjectJS(webgl), mAttribBuffers(webgl.Limits().maxVertexAttribs) {}
6326 
6327 // -
6328 
6329 #define _(WebGLType)                                                      \
6330   JSObject* WebGLType##JS::WrapObject(JSContext* const cx,                \
6331                                       JS::Handle<JSObject*> givenProto) { \
6332     return dom::WebGLType##_Binding::Wrap(cx, this, givenProto);          \
6333   }
6334 
6335 _(WebGLBuffer)
_(WebGLFramebuffer)6336 _(WebGLFramebuffer)
6337 _(WebGLProgram)
6338 _(WebGLQuery)
6339 _(WebGLRenderbuffer)
6340 _(WebGLSampler)
6341 _(WebGLShader)
6342 _(WebGLSync)
6343 _(WebGLTexture)
6344 _(WebGLTransformFeedback)
6345 _(WebGLUniformLocation)
6346 //_(WebGLVertexArray) // The webidl is `WebGLVertexArrayObject` :(
6347 
6348 #undef _
6349 
6350 JSObject* WebGLVertexArrayJS::WrapObject(JSContext* const cx,
6351                                          JS::Handle<JSObject*> givenProto) {
6352   return dom::WebGLVertexArrayObject_Binding::Wrap(cx, this, givenProto);
6353 }
6354 
WrapObject(JSContext * const cx,JS::Handle<JSObject * > givenProto,JS::MutableHandle<JSObject * > reflector)6355 bool WebGLActiveInfoJS::WrapObject(JSContext* const cx,
6356                                    JS::Handle<JSObject*> givenProto,
6357                                    JS::MutableHandle<JSObject*> reflector) {
6358   return dom::WebGLActiveInfo_Binding::Wrap(cx, this, givenProto, reflector);
6359 }
6360 
WrapObject(JSContext * const cx,JS::Handle<JSObject * > givenProto,JS::MutableHandle<JSObject * > reflector)6361 bool WebGLShaderPrecisionFormatJS::WrapObject(
6362     JSContext* const cx, JS::Handle<JSObject*> givenProto,
6363     JS::MutableHandle<JSObject*> reflector) {
6364   return dom::WebGLShaderPrecisionFormat_Binding::Wrap(cx, this, givenProto,
6365                                                        reflector);
6366 }
6367 
6368 // ---------------------
6369 
6370 // Todo: Move this to RefPtr.h.
6371 template <typename T>
ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback & callback,const RefPtr<T> & field,const char * name,uint32_t flags)6372 void ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& callback,
6373                                  const RefPtr<T>& field, const char* name,
6374                                  uint32_t flags) {
6375   ImplCycleCollectionTraverse(callback, const_cast<RefPtr<T>&>(field), name,
6376                               flags);
6377 }
6378 
6379 // -
6380 
6381 template <typename T>
ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback & callback,const std::vector<RefPtr<T>> & field,const char * name,uint32_t flags)6382 void ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& callback,
6383                                  const std::vector<RefPtr<T>>& field,
6384                                  const char* name, uint32_t flags) {
6385   for (const auto& cur : field) {
6386     ImplCycleCollectionTraverse(callback, cur, name, flags);
6387   }
6388 }
6389 
6390 template <typename T>
ImplCycleCollectionUnlink(std::vector<RefPtr<T>> & field)6391 void ImplCycleCollectionUnlink(std::vector<RefPtr<T>>& field) {
6392   field = {};
6393 }
6394 
6395 // -
6396 
6397 template <typename T, size_t N>
ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback & callback,const std::array<RefPtr<T>,N> & field,const char * name,uint32_t flags)6398 void ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& callback,
6399                                  const std::array<RefPtr<T>, N>& field,
6400                                  const char* name, uint32_t flags) {
6401   for (const auto& cur : field) {
6402     ImplCycleCollectionTraverse(callback, cur, name, flags);
6403   }
6404 }
6405 
6406 template <typename T, size_t N>
ImplCycleCollectionUnlink(std::array<RefPtr<T>,N> & field)6407 void ImplCycleCollectionUnlink(std::array<RefPtr<T>, N>& field) {
6408   field = {};
6409 }
6410 
6411 // -
6412 
6413 template <typename T>
ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback & callback,const std::unordered_map<GLenum,RefPtr<T>> & field,const char * name,uint32_t flags)6414 void ImplCycleCollectionTraverse(
6415     nsCycleCollectionTraversalCallback& callback,
6416     const std::unordered_map<GLenum, RefPtr<T>>& field, const char* name,
6417     uint32_t flags) {
6418   for (const auto& pair : field) {
6419     ImplCycleCollectionTraverse(callback, pair.second, name, flags);
6420   }
6421 }
6422 
6423 template <typename T>
ImplCycleCollectionUnlink(std::unordered_map<GLenum,RefPtr<T>> & field)6424 void ImplCycleCollectionUnlink(std::unordered_map<GLenum, RefPtr<T>>& field) {
6425   field = {};
6426 }
6427 
6428 // -
6429 
ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback & callback,const std::unordered_map<GLenum,WebGLFramebufferJS::Attachment> & field,const char * name,uint32_t flags)6430 void ImplCycleCollectionTraverse(
6431     nsCycleCollectionTraversalCallback& callback,
6432     const std::unordered_map<GLenum, WebGLFramebufferJS::Attachment>& field,
6433     const char* name, uint32_t flags) {
6434   for (const auto& pair : field) {
6435     const auto& attach = pair.second;
6436     ImplCycleCollectionTraverse(callback, attach.rb, name, flags);
6437     ImplCycleCollectionTraverse(callback, attach.tex, name, flags);
6438   }
6439 }
6440 
ImplCycleCollectionUnlink(std::unordered_map<GLenum,WebGLFramebufferJS::Attachment> & field)6441 void ImplCycleCollectionUnlink(
6442     std::unordered_map<GLenum, WebGLFramebufferJS::Attachment>& field) {
6443   field = {};
6444 }
6445 
6446 // -
6447 
ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback & callback,const std::unordered_map<GLenum,WebGLProgramJS::Attachment> & field,const char * name,uint32_t flags)6448 void ImplCycleCollectionTraverse(
6449     nsCycleCollectionTraversalCallback& callback,
6450     const std::unordered_map<GLenum, WebGLProgramJS::Attachment>& field,
6451     const char* name, uint32_t flags) {
6452   for (const auto& pair : field) {
6453     const auto& attach = pair.second;
6454     ImplCycleCollectionTraverse(callback, attach.shader, name, flags);
6455   }
6456 }
6457 
ImplCycleCollectionUnlink(std::unordered_map<GLenum,WebGLProgramJS::Attachment> & field)6458 void ImplCycleCollectionUnlink(
6459     std::unordered_map<GLenum, WebGLProgramJS::Attachment>& field) {
6460   field = {};
6461 }
6462 
6463 // -
6464 
ImplCycleCollectionUnlink(const RefPtr<ClientWebGLExtensionLoseContext> & field)6465 void ImplCycleCollectionUnlink(
6466     const RefPtr<ClientWebGLExtensionLoseContext>& field) {
6467   const_cast<RefPtr<ClientWebGLExtensionLoseContext>&>(field) = nullptr;
6468 }
ImplCycleCollectionUnlink(const RefPtr<WebGLProgramJS> & field)6469 void ImplCycleCollectionUnlink(const RefPtr<WebGLProgramJS>& field) {
6470   const_cast<RefPtr<WebGLProgramJS>&>(field) = nullptr;
6471 }
ImplCycleCollectionUnlink(const RefPtr<WebGLShaderJS> & field)6472 void ImplCycleCollectionUnlink(const RefPtr<WebGLShaderJS>& field) {
6473   const_cast<RefPtr<WebGLShaderJS>&>(field) = nullptr;
6474 }
6475 
6476 // ----------------------
6477 
ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback & callback,const std::shared_ptr<webgl::NotLostData> & field,const char * name,uint32_t flags)6478 void ImplCycleCollectionTraverse(
6479     nsCycleCollectionTraversalCallback& callback,
6480     const std::shared_ptr<webgl::NotLostData>& field, const char* name,
6481     uint32_t flags) {
6482   if (!field) return;
6483 
6484   ImplCycleCollectionTraverse(callback, field->extensions,
6485                               "NotLostData.extensions", flags);
6486 
6487   const auto& state = field->state;
6488 
6489   ImplCycleCollectionTraverse(callback, state.mDefaultTfo, "state.mDefaultTfo",
6490                               flags);
6491   ImplCycleCollectionTraverse(callback, state.mDefaultVao, "state.mDefaultVao",
6492                               flags);
6493 
6494   ImplCycleCollectionTraverse(callback, state.mCurrentProgram,
6495                               "state.mCurrentProgram", flags);
6496 
6497   ImplCycleCollectionTraverse(callback, state.mBoundBufferByTarget,
6498                               "state.mBoundBufferByTarget", flags);
6499   ImplCycleCollectionTraverse(callback, state.mBoundUbos, "state.mBoundUbos",
6500                               flags);
6501   ImplCycleCollectionTraverse(callback, state.mBoundDrawFb,
6502                               "state.mBoundDrawFb", flags);
6503   ImplCycleCollectionTraverse(callback, state.mBoundReadFb,
6504                               "state.mBoundReadFb", flags);
6505   ImplCycleCollectionTraverse(callback, state.mBoundRb, "state.mBoundRb",
6506                               flags);
6507   ImplCycleCollectionTraverse(callback, state.mBoundTfo, "state.mBoundTfo",
6508                               flags);
6509   ImplCycleCollectionTraverse(callback, state.mBoundVao, "state.mBoundVao",
6510                               flags);
6511   ImplCycleCollectionTraverse(callback, state.mCurrentQueryByTarget,
6512                               "state.state.mCurrentQueryByTarget", flags);
6513 
6514   for (const auto& texUnit : state.mTexUnits) {
6515     ImplCycleCollectionTraverse(callback, texUnit.sampler,
6516                                 "state.mTexUnits[].sampler", flags);
6517     ImplCycleCollectionTraverse(callback, texUnit.texByTarget,
6518                                 "state.mTexUnits[].texByTarget", flags);
6519   }
6520 }
6521 
ImplCycleCollectionUnlink(std::shared_ptr<webgl::NotLostData> & field)6522 void ImplCycleCollectionUnlink(std::shared_ptr<webgl::NotLostData>& field) {
6523   if (!field) return;
6524   const auto keepAlive = field;
6525   keepAlive->extensions = {};
6526   keepAlive->state = {};
6527   field = nullptr;
6528 }
6529 
6530 // -----------------------------------------------------
6531 
6532 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLBufferJS)
6533 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLFramebufferJS, mAttachments)
6534 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLProgramJS, mNextLink_Shaders)
6535 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLQueryJS)
6536 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLRenderbufferJS)
6537 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLSamplerJS)
6538 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLShaderJS)
6539 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLSyncJS)
6540 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLTextureJS)
6541 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLTransformFeedbackJS, mAttribBuffers,
6542                                       mActiveProgram)
6543 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLUniformLocationJS)
6544 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLVertexArrayJS, mIndexBuffer,
6545                                       mAttribBuffers)
6546 
6547 // -
6548 
6549 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ClientWebGLContext)
6550   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
6551   NS_INTERFACE_MAP_ENTRY(nsICanvasRenderingContextInternal)
6552   NS_INTERFACE_MAP_ENTRY(nsISupports)
6553 NS_INTERFACE_MAP_END
6554 
6555 NS_IMPL_CYCLE_COLLECTING_ADDREF(ClientWebGLContext)
6556 NS_IMPL_CYCLE_COLLECTING_RELEASE(ClientWebGLContext)
6557 
6558 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WEAK_PTR(
6559     ClientWebGLContext, mExtLoseContext, mNotLost,
6560     // Don't forget nsICanvasRenderingContextInternal:
6561     mCanvasElement, mOffscreenCanvas)
6562 
6563 // -----------------------------
6564 
6565 #define _(X)                                                 \
6566   NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGL##X##JS, AddRef) \
6567   NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGL##X##JS, Release)
6568 
6569 _(Buffer)
6570 _(Framebuffer)
6571 _(Program)
6572 _(Query)
6573 _(Renderbuffer)
6574 _(Sampler)
6575 _(Shader)
6576 _(Sync)
6577 _(Texture)
6578 _(TransformFeedback)
6579 _(UniformLocation)
6580 _(VertexArray)
6581 
6582 #undef _
6583 
6584 }  // namespace mozilla
6585