tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ClientWebGLContext.cpp (230137B)


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