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, ¬Lost.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, ¬Lost.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