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