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