ClientWebGLContext.cpp 196 KB
Newer Older
1
2
3
4
5
6
7
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "ClientWebGLContext.h"

8
#include "ClientWebGLExtensions.h"
9
#include "Layers.h"
10
#include "gfxCrashReporterUtils.h"
11
#include "HostWebGLContext.h"
12
#include "js/ScalarType.h"  // js::Scalar::Type
13
#include "mozilla/dom/Document.h"
14
15
16
17
#include "mozilla/dom/ToJSValue.h"
#include "mozilla/dom/WebGLContextEvent.h"
#include "mozilla/dom/WorkerCommon.h"
#include "mozilla/EnumeratedRange.h"
18
#include "mozilla/gfx/gfxVars.h"
19
20
21
22
23
24
#include "mozilla/ipc/Shmem.h"
#include "mozilla/layers/CompositorBridgeChild.h"
#include "mozilla/layers/ImageBridgeChild.h"
#include "mozilla/layers/LayerTransactionChild.h"
#include "mozilla/layers/OOPCanvasRenderer.h"
#include "mozilla/layers/TextureClientSharedSurface.h"
25
26
#include "mozilla/layers/WebRenderUserData.h"
#include "mozilla/layers/WebRenderCanvasRenderer.h"
27
#include "mozilla/Preferences.h"
28
#include "mozilla/ScopeExit.h"
29
#include "mozilla/StaticPrefs_webgl.h"
30
#include "nsContentUtils.h"
31
#include "nsDisplayList.h"
32
33
34
#include "TexUnpackBlob.h"
#include "WebGLMethodDispatcher.h"
#include "WebGLChild.h"
35
#include "WebGLValidateStrings.h"
36
37
38

namespace mozilla {

39
40
webgl::NotLostData::NotLostData(ClientWebGLContext& _context)
    : context(_context) {}
41
42
43
44

webgl::NotLostData::~NotLostData() {
  if (outOfProcess) {
    const auto& pwebgl = outOfProcess->mWebGLChild;
45
    Unused << dom::WebGLChild::Send__delete__(pwebgl.get());
46
47
  }
}
48
49

// -
50

51
52
53
54
55
56
57
58
59
60
61
bool webgl::ObjectJS::ValidateForContext(
    const ClientWebGLContext& targetContext, const char* const argName) const {
  if (!IsForContext(targetContext)) {
    targetContext.EnqueueError(
        LOCAL_GL_INVALID_OPERATION,
        "`%s` is from a different (or lost) WebGL context.", argName);
    return false;
  }
  return true;
}

62
63
void webgl::ObjectJS::WarnInvalidUse(const ClientWebGLContext& targetContext,
                                     const char* const argName) const {
64
65
66
67
  if (!ValidateForContext(targetContext, argName)) return;

  const auto errEnum = ErrorOnDeleted();
  targetContext.EnqueueError(errEnum, "Object `%s` is already deleted.",
68
69
                             argName);
}
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114

static bool GetJSScalarFromGLType(GLenum type,
                                  js::Scalar::Type* const out_scalarType) {
  switch (type) {
    case LOCAL_GL_BYTE:
      *out_scalarType = js::Scalar::Int8;
      return true;

    case LOCAL_GL_UNSIGNED_BYTE:
      *out_scalarType = js::Scalar::Uint8;
      return true;

    case LOCAL_GL_SHORT:
      *out_scalarType = js::Scalar::Int16;
      return true;

    case LOCAL_GL_HALF_FLOAT:
    case LOCAL_GL_HALF_FLOAT_OES:
    case LOCAL_GL_UNSIGNED_SHORT:
    case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
    case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
    case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
      *out_scalarType = js::Scalar::Uint16;
      return true;

    case LOCAL_GL_UNSIGNED_INT:
    case LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV:
    case LOCAL_GL_UNSIGNED_INT_5_9_9_9_REV:
    case LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV:
    case LOCAL_GL_UNSIGNED_INT_24_8:
      *out_scalarType = js::Scalar::Uint32;
      return true;
    case LOCAL_GL_INT:
      *out_scalarType = js::Scalar::Int32;
      return true;

    case LOCAL_GL_FLOAT:
      *out_scalarType = js::Scalar::Float32;
      return true;

    default:
      return false;
  }
}

115
116
117
ClientWebGLContext::ClientWebGLContext(const bool webgl2)
    : mIsWebGL2(webgl2),
      mExtLoseContext(new ClientWebGLExtensionLoseContext(*this)) {}
118

119
ClientWebGLContext::~ClientWebGLContext() { RemovePostRefreshObserver(); }
120

121
void ClientWebGLContext::JsWarning(const std::string& utf8) const {
122
123
124
125
126
127
128
  if (!mCanvasElement) {
    return;
  }
  dom::AutoJSAPI api;
  if (!api.Init(mCanvasElement->OwnerDoc()->GetScopeObject())) {
    return;
  }
129
130
131
132
133
134
135
  const auto& cx = api.cx();
  JS::WarnUTF8(cx, "%s", utf8.c_str());
}

void AutoJsWarning(const std::string& utf8) {
  const AutoJSContext cx;
  JS::WarnUTF8(cx, "%s", utf8.c_str());
136
137
}

138
139
140
// ---------

bool ClientWebGLContext::DispatchEvent(const nsAString& eventName) const {
141
142
  const auto kCanBubble = CanBubble::eYes;
  const auto kIsCancelable = Cancelable::eYes;
143
  bool useDefaultHandler = true;
144
145
146
147

  if (mCanvasElement) {
    nsContentUtils::DispatchTrustedEvent(
        mCanvasElement->OwnerDoc(), static_cast<nsIContent*>(mCanvasElement),
148
        eventName, kCanBubble, kIsCancelable, &useDefaultHandler);
149
  } else if (mOffscreenCanvas) {
150
    // OffscreenCanvas case
151
152
    RefPtr<dom::Event> event =
        new dom::Event(mOffscreenCanvas, nullptr, nullptr);
153
    event->InitEvent(eventName, kCanBubble, kIsCancelable);
154
155
    event->SetTrusted(true);
    useDefaultHandler = mOffscreenCanvas->DispatchEvent(
156
        *event, dom::CallerType::System, IgnoreErrors());
157
  }
158
159
160
161
162
  return useDefaultHandler;
}

// -

163
void ClientWebGLContext::EmulateLoseContext() const {
164
165
166
167
168
169
170
171
172
173
174
  const FuncScope funcScope(*this, "loseContext");
  if (mLossStatus != webgl::LossStatus::Ready) {
    JsWarning("loseContext: Already lost.");
    if (!mNextError) {
      mNextError = LOCAL_GL_INVALID_OPERATION;
    }
    return;
  }
  OnContextLoss(webgl::ContextLossReason::Manual);
}

175
176
void ClientWebGLContext::OnContextLoss(
    const webgl::ContextLossReason reason) const {
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
  MOZ_ASSERT(NS_IsMainThread());
  JsWarning("WebGL context was lost.");

  if (mNotLost) {
    for (const auto& ext : mNotLost->extensions) {
      if (!ext) continue;
      ext->mContext = nullptr;  // Detach.
    }
    mNotLost = {};  // Lost now!
    mNextError = LOCAL_GL_CONTEXT_LOST_WEBGL;
  }

  switch (reason) {
    case webgl::ContextLossReason::Guilty:
      mLossStatus = webgl::LossStatus::LostForever;
      break;
193

194
195
196
197
198
199
200
    case webgl::ContextLossReason::None:
      mLossStatus = webgl::LossStatus::Lost;
      break;

    case webgl::ContextLossReason::Manual:
      mLossStatus = webgl::LossStatus::LostManually;
      break;
201
  }
202

203
  const auto weak = WeakPtr<const ClientWebGLContext>(this);
204
  const auto fnRun = [weak]() {
205
    const auto strong = RefPtr<const ClientWebGLContext>(weak);
206
207
208
209
210
211
    if (!strong) return;
    strong->Event_webglcontextlost();
  };
  already_AddRefed<mozilla::Runnable> runnable =
      NS_NewRunnableFunction("enqueue Event_webglcontextlost", fnRun);
  NS_DispatchToCurrentThread(std::move(runnable));
212
213
}

214
void ClientWebGLContext::Event_webglcontextlost() const {
215
  WEBGL_BRIDGE_LOGD("[%p] Posting webglcontextlost event", this);
216
  const bool useDefaultHandler = DispatchEvent(u"webglcontextlost"_ns);
217
218
219
220
221
222
223
224
225
226
  if (useDefaultHandler) {
    mLossStatus = webgl::LossStatus::LostForever;
  }

  if (mLossStatus == webgl::LossStatus::Lost) {
    RestoreContext(webgl::LossStatus::Lost);
  }
}

void ClientWebGLContext::RestoreContext(
227
    const webgl::LossStatus requiredStatus) const {
228
229
230
231
232
233
234
235
236
237
238
239
240
241
  if (requiredStatus != mLossStatus) {
    JsWarning(
        "restoreContext: Only valid iff context lost with loseContext().");
    if (!mNextError) {
      mNextError = LOCAL_GL_INVALID_OPERATION;
    }
    return;
  }
  MOZ_RELEASE_ASSERT(mLossStatus == webgl::LossStatus::Lost ||
                     mLossStatus == webgl::LossStatus::LostManually);

  if (mAwaitingRestore) return;
  mAwaitingRestore = true;

242
  const auto weak = WeakPtr<const ClientWebGLContext>(this);
243
  const auto fnRun = [weak]() {
244
    const auto strong = RefPtr<const ClientWebGLContext>(weak);
245
246
247
248
249
250
251
252
    if (!strong) return;
    strong->Event_webglcontextrestored();
  };
  already_AddRefed<mozilla::Runnable> runnable =
      NS_NewRunnableFunction("enqueue Event_webglcontextrestored", fnRun);
  NS_DispatchToCurrentThread(std::move(runnable));
}

253
void ClientWebGLContext::Event_webglcontextrestored() const {
254
255
256
257
  mAwaitingRestore = false;
  mLossStatus = webgl::LossStatus::Ready;
  mNextError = 0;

258
  const uvec2 requestSize = {mCanvasElement->Width(), mCanvasElement->Height()};
259
260
261
  const auto mutThis = const_cast<ClientWebGLContext*>(
      this);  // TODO: Make context loss non-mutable.
  if (!mutThis->CreateHostContext(requestSize)) {
262
263
    mLossStatus = webgl::LossStatus::LostForever;
    return;
264
  }
265
266

  WEBGL_BRIDGE_LOGD("[%p] Posting webglcontextrestored event", this);
267
  (void)DispatchEvent(u"webglcontextrestored"_ns);
268
269
}

270
271
272
273
274
275
276
277
// ---------

void ClientWebGLContext::ThrowEvent_WebGLContextCreationError(
    const std::string& text) const {
  nsCString msg;
  msg.AppendPrintf("Failed to create WebGL context: %s", text.c_str());
  JsWarning(msg.BeginReading());

278
  RefPtr<dom::EventTarget> target = mCanvasElement;
279
280
281
282
283
284
285
  if (!target && mOffscreenCanvas) {
    target = mOffscreenCanvas;
  } else if (!target) {
    return;
  }

  WEBGL_BRIDGE_LOGD("[%p] Posting webglcontextcreationerror event", this);
286
  const auto kEventName = u"webglcontextcreationerror"_ns;
287
288
289

  dom::WebGLContextEventInit eventInit;
  // eventInit.mCancelable = true; // The spec says this, but it's silly.
290
  eventInit.mStatusMessage = NS_ConvertASCIItoUTF16(text.c_str());
291

292
293
  const RefPtr<dom::WebGLContextEvent> event =
      dom::WebGLContextEvent::Constructor(target, kEventName, eventInit);
294
295
296
297
298
  event->SetTrusted(true);

  target->DispatchEvent(*event);
}

299
// -
300

301
302
303
304
305
306
307
308
309
310
311
// If we are running WebGL in this process then call the HostWebGLContext
// method directly.  Otherwise, dispatch over IPC.
template <typename MethodType, MethodType method, typename... Args>
void ClientWebGLContext::Run(Args&&... args) const {
  const auto notLost =
      mNotLost;  // Hold a strong-ref to prevent LoseContext=>UAF.
  if (IsContextLost()) return;

  const auto& inProcess = notLost->inProcess;
  if (inProcess) {
    return (inProcess.get()->*method)(std::forward<Args>(args)...);
312
  }
313

314
  const auto& child = notLost->outOfProcess->mWebGLChild;
315

316
  const auto id = IdByMethod<MethodType, method>();
317

318
319
320
321
322
323
324
325
326
  const auto size = webgl::SerializedSize(id, args...);
  const auto maybeDest = child->AllocPendingCmdBytes(size);
  if (!maybeDest) {
    JsWarning("Failed to allocate internal command buffer.");
    OnContextLoss(webgl::ContextLossReason::None);
    return;
  }
  const auto& destBytes = *maybeDest;
  webgl::Serialize(destBytes, id, args...);
327
328
}

329
330
331
332
333
334
335
336
337
// -------------------------------------------------------------------------
// Client-side helper methods.  Dispatch to a Host method.
// -------------------------------------------------------------------------

#define RPROC(_METHOD) \
  decltype(&HostWebGLContext::_METHOD), &HostWebGLContext::_METHOD

// ------------------------- Composition, etc -------------------------

338
339
340
void ClientWebGLContext::OnBeforePaintTransaction() {
  const RefPtr<layers::ImageBridgeChild> imageBridge =
      layers::ImageBridgeChild::GetSingleton();
341

342
  const auto texType = layers::TexTypeForWebgl(imageBridge);
343
  Present(nullptr, texType);
344
345
}

346
347
348
349
void ClientWebGLContext::EndComposition() {
  // Mark ourselves as no longer invalidated.
  MarkContextClean();
}
350

351
// -
352

353
354
355
356
357
358
359
void ClientWebGLContext::Present(WebGLFramebufferJS* const xrFb,
                                 const layers::TextureType type,
                                 const bool webvr) {
  if (!mIsCanvasDirty && !xrFb) return;
  if (!xrFb) {
    mIsCanvasDirty = false;
  }
360

361
  Run<RPROC(Present)>(xrFb ? xrFb->mId : 0, type, webvr);
362
}
363

364
Maybe<layers::SurfaceDescriptor> ClientWebGLContext::GetFrontBuffer(
365
    WebGLFramebufferJS* const fb, bool vr) {
366
367
368
369
370
371
372
373
374
375
376
377
378
  const auto notLost = mNotLost;
  if (IsContextLost()) return {};

  const auto& inProcess = mNotLost->inProcess;
  if (inProcess) {
    return inProcess->GetFrontBuffer(fb ? fb->mId : 0, vr);
  }

  const auto& child = mNotLost->outOfProcess->mWebGLChild;
  child->FlushPendingCmds();
  Maybe<layers::SurfaceDescriptor> ret;
  if (!child->SendGetFrontBuffer(fb ? fb->mId : 0, vr, &ret)) return {};
  return ret;
379
}
380

381
382
void ClientWebGLContext::ClearVRSwapChain() { Run<RPROC(ClearVRSwapChain)>(); }

383
384
// -

385
386
already_AddRefed<layers::Layer> ClientWebGLContext::GetCanvasLayer(
    nsDisplayListBuilder* builder, Layer* oldLayer, LayerManager* manager) {
387
  if (!mResetLayer && oldLayer) {
388
389
390
391
392
393
394
395
396
397
398
399
    RefPtr<layers::Layer> ret = oldLayer;
    return ret.forget();
  }

  WEBGL_BRIDGE_LOGI("[%p] Creating WebGL CanvasLayer/Renderer", this);

  RefPtr<CanvasLayer> canvasLayer = manager->CreateCanvasLayer();
  if (!canvasLayer) {
    NS_WARNING("CreateCanvasLayer returned null!");
    return nullptr;
  }

400
  const auto canvasRenderer = canvasLayer->CreateOrGetCanvasRenderer();
401
402
  if (!InitializeCanvasRenderer(builder, canvasRenderer)) return nullptr;

403
404
405
406
  uint32_t flags = 0;
  if (GetIsOpaque()) {
    flags |= Layer::CONTENT_OPAQUE;
  }
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
  canvasLayer->SetContentFlags(flags);

  mResetLayer = false;
  return canvasLayer.forget();
}

bool ClientWebGLContext::UpdateWebRenderCanvasData(
    nsDisplayListBuilder* aBuilder, WebRenderCanvasData* aCanvasData) {
  CanvasRenderer* renderer = aCanvasData->GetCanvasRenderer();

  if (!mResetLayer && renderer) {
    return true;
  }

  WEBGL_BRIDGE_LOGI("[%p] Creating WebGL WR CanvasLayer/Renderer", this);
  renderer = aCanvasData->CreateCanvasRenderer();
  if (!InitializeCanvasRenderer(aBuilder, renderer)) {
    // Clear CanvasRenderer of WebRenderCanvasData
    aCanvasData->ClearCanvasRenderer();
    return false;
  }

  MOZ_ASSERT(renderer);
  mResetLayer = false;
  return true;
}

bool ClientWebGLContext::InitializeCanvasRenderer(
    nsDisplayListBuilder* aBuilder, CanvasRenderer* aRenderer) {
436
  const FuncScope funcScope(*this, "<InitializeCanvasRenderer>");
437
438
  if (IsContextLost()) return false;

439
  layers::CanvasRendererData data;
440
441
  data.mContext = mSharedPtrPtr;
  data.mOriginPos = gl::OriginPos::BottomLeft;
442

443
444
445
446
447
  const auto& options = *mInitialOptions;
  const auto& size = DrawingBufferSize();
  data.mIsOpaque = !options.alpha;
  data.mIsAlphaPremult = !options.alpha || options.premultipliedAlpha;
  data.mSize = {size.x, size.y};
448

449
  if (aBuilder->IsPaintingToWindow() && mCanvasElement) {
450
    data.mDoPaintCallbacks = true;
451
452
  }

453
454
455
456
457
458
459
460
461
462
463
464
  aRenderer->Initialize(data);
  aRenderer->SetDirty();
  return true;
}

layers::LayersBackend ClientWebGLContext::GetCompositorBackendType() const {
  if (mCanvasElement) {
    return mCanvasElement->GetCompositorBackendType();
  } else if (mOffscreenCanvas) {
    return mOffscreenCanvas->GetCompositorBackendType();
  }

465
  return layers::LayersBackend::LAYERS_NONE;
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
}

mozilla::dom::Document* ClientWebGLContext::GetOwnerDoc() const {
  MOZ_ASSERT(mCanvasElement);
  if (!mCanvasElement) {
    return nullptr;
  }
  return mCanvasElement->OwnerDoc();
}

void ClientWebGLContext::Commit() {
  if (mOffscreenCanvas) {
    mOffscreenCanvas->CommitFrameToCompositor();
  }
}

void ClientWebGLContext::GetCanvas(
483
    dom::Nullable<dom::OwningHTMLCanvasElementOrOffscreenCanvas>& retval) {
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
  if (mCanvasElement) {
    MOZ_RELEASE_ASSERT(!mOffscreenCanvas, "GFX: Canvas is offscreen.");

    if (mCanvasElement->IsInNativeAnonymousSubtree()) {
      retval.SetNull();
    } else {
      retval.SetValue().SetAsHTMLCanvasElement() = mCanvasElement;
    }
  } else if (mOffscreenCanvas) {
    retval.SetValue().SetAsOffscreenCanvas() = mOffscreenCanvas;
  } else {
    retval.SetNull();
  }
}

void ClientWebGLContext::GetContextAttributes(
    dom::Nullable<dom::WebGLContextAttributes>& retval) {
  retval.SetNull();
502
  const FuncScope funcScope(*this, "getContextAttributes");
503
504
505
506
  if (IsContextLost()) return;

  dom::WebGLContextAttributes& result = retval.SetValue();

507
508
509
510
511
512
513
514
515
516
  const auto& options = mNotLost->info.options;

  result.mAlpha.Construct(options.alpha);
  result.mDepth = options.depth;
  result.mStencil = options.stencil;
  result.mAntialias.Construct(options.antialias);
  result.mPremultipliedAlpha = options.premultipliedAlpha;
  result.mPreserveDrawingBuffer = options.preserveDrawingBuffer;
  result.mFailIfMajorPerformanceCaveat = options.failIfMajorPerformanceCaveat;
  result.mPowerPreference = options.powerPreference;
517
518
}

519
520
// -----------------------

521
NS_IMETHODIMP
522
523
ClientWebGLContext::SetDimensions(const int32_t signedWidth,
                                  const int32_t signedHeight) {
524
  const FuncScope funcScope(*this, "<SetDimensions>");
525
526
527
  WEBGL_BRIDGE_LOGI("[%p] SetDimensions: (%d, %d)", this, signedWidth,
                    signedHeight);

528
529
  MOZ_ASSERT(mInitialOptions);

530
531
532
533
534
  if (mLossStatus != webgl::LossStatus::Ready) {
    // Attempted resize of a lost context.
    return NS_OK;
  }

535
536
537
538
539
540
541
  uvec2 size = {static_cast<uint32_t>(signedWidth),
                static_cast<uint32_t>(signedHeight)};
  if (!size.x) {
    size.x = 1;
  }
  if (!size.y) {
    size.y = 1;
542
  }
543
544
  const auto prevRequestedSize = mRequestedSize;
  mRequestedSize = size;
545

546
547
  mResetLayer = true;  // Always treat this as resize.

548
  if (mNotLost) {
549
    auto& state = State();
550

551
552
553
554
555
556
557
    auto curSize = prevRequestedSize;
    if (state.mDrawingBufferSize) {
      curSize = *state.mDrawingBufferSize;
    }
    if (size == curSize) return NS_OK;  // MUST skip no-op resize

    state.mDrawingBufferSize = Nothing();
558
559
    Run<RPROC(Resize)>(size);

560
561
562
563
564
565
566
    MarkCanvasDirty();
    return NS_OK;
  }

  // -
  // Context (re-)creation

567
  if (!CreateHostContext(size)) {
568
569
570
571
572
    return NS_ERROR_FAILURE;
  }
  return NS_OK;
}

573
static bool IsWebglOutOfProcessEnabled() {
574
575
  bool useOop = StaticPrefs::webgl_out_of_process();

576
  if (!gfx::gfxVars::AllowWebglOop()) {
577
578
579
580
581
582
    useOop = false;
  }
  if (StaticPrefs::webgl_out_of_process_force()) {
    useOop = true;
  }
  return useOop;
583
584
}

585
586
587
588
589
static inline bool StartsWith(const std::string& haystack,
                              const std::string& needle) {
  return haystack.find(needle) == 0;
}

590
bool ClientWebGLContext::CreateHostContext(const uvec2& requestedSize) {
591
592
  const auto pNotLost = std::make_shared<webgl::NotLostData>(*this);
  auto& notLost = *pNotLost;
593
594
595
596
597
598

  auto res = [&]() -> Result<Ok, std::string> {
    auto options = *mInitialOptions;
    if (StaticPrefs::webgl_disable_fail_if_major_performance_caveat()) {
      options.failIfMajorPerformanceCaveat = false;
    }
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613

    if (options.failIfMajorPerformanceCaveat) {
      const auto backend = GetCompositorBackendType();
      bool isCompositorSlow = false;
      isCompositorSlow |= (backend == layers::LayersBackend::LAYERS_BASIC);
      isCompositorSlow |= (backend == layers::LayersBackend::LAYERS_WR &&
                           gfx::gfxVars::UseSoftwareWebRender());

      if (isCompositorSlow) {
        return Err(
            "failIfMajorPerformanceCaveat: Compositor is not"
            " hardware-accelerated.");
      }
    }

614
    const bool resistFingerprinting = ShouldResistFingerprinting();
615
616
617

    const auto& principal = GetCanvas()->NodePrincipal();
    const auto principalKey = principal->GetHashValue();
618
    const auto initDesc = webgl::InitContextDesc{
619
        mIsWebGL2, resistFingerprinting, requestedSize, options, principalKey};
620
621
622

    // -

623
    auto useOop = IsWebglOutOfProcessEnabled();
624
625
626
627
628
    if (XRE_IsParentProcess()) {
      useOop = false;
    }

    if (!useOop) {
629
630
631
632
633
634
635
636
637
638
      auto ownerData = HostWebGLContext::OwnerData{
          Some(this),
      };
      notLost.inProcess = HostWebGLContext::Create(std::move(ownerData),
                                                   initDesc, &notLost.info);
      return Ok();
    }

    // -

639
640
    ScopedGfxFeatureReporter reporter("IpcWebGL");

641
    webgl::RemotingData outOfProcess;
642

643
    auto* const cbc = layers::CompositorBridgeChild::Get();
644
645
646
647
648
    MOZ_ASSERT(cbc);
    if (!cbc) {
      return Err("!CompositorBridgeChild::Get()");
    }

649
    outOfProcess.mWebGLChild = new dom::WebGLChild(*this);
650
651
652
    outOfProcess.mWebGLChild = static_cast<dom::WebGLChild*>(
        cbc->SendPWebGLConstructor(outOfProcess.mWebGLChild));
    if (!outOfProcess.mWebGLChild) {
653
      return Err("SendPWebGLConstructor failed");
654
655
    }

656
657
    UniquePtr<HostWebGLCommandSinkP> sinkP;
    UniquePtr<HostWebGLCommandSinkI> sinkI;
658
659
660
661
662
663
664
665
666
    if (StaticPrefs::webgl_oop_via_pcq()) {
      using mozilla::webgl::ProducerConsumerQueue;
      static constexpr size_t CommandQueueSize = 256 * 1024;  // 256K
      static constexpr size_t ResponseQueueSize = 8 * 1024;   // 8K
      auto command = ProducerConsumerQueue::Create(cbc, CommandQueueSize);
      auto response = ProducerConsumerQueue::Create(cbc, ResponseQueueSize);
      if (!command || !response) {
        return Err("Failed to create command/response PCQ");
      }
667

668
669
670
671
672
673
674
675
676
677
678
679
680
681
      outOfProcess.mCommandSourcePcq = MakeUnique<ClientWebGLCommandSourceP>(
          command->TakeProducer(), response->TakeConsumer());
      sinkP = MakeUnique<HostWebGLCommandSinkP>(command->TakeConsumer(),
                                                response->TakeProducer());

    } else {
      using mozilla::IpdlWebGLCommandQueue;
      using mozilla::IpdlWebGLResponseQueue;
      auto command =
          IpdlWebGLCommandQueue::Create(outOfProcess.mWebGLChild.get());
      auto response =
          IpdlWebGLResponseQueue::Create(outOfProcess.mWebGLChild.get());
      if (!command || !response) {
        return Err("Failed to create command/response IpdlQueue");
682
683
      }

684
685
686
687
      outOfProcess.mCommandSourceIpdl = MakeUnique<ClientWebGLCommandSourceI>(
          command->TakeProducer(), response->TakeConsumer());
      sinkI = MakeUnique<HostWebGLCommandSinkI>(command->TakeConsumer(),
                                                response->TakeProducer());
688
689
690
691
    }

    if (!outOfProcess.mWebGLChild->SendInitialize(
            initDesc, std::move(sinkP), std::move(sinkI), &notLost.info)) {
692
693
694
      return Err("WebGL actor Initialize failed");
    }

695
    notLost.outOfProcess = Some(std::move(outOfProcess));
696
    reporter.SetSuccessful();
697
698
699
    return Ok();
  }();
  if (!res.isOk()) {
700
701
702
703
704
705
706
    auto str = res.unwrapErr();
    if (StartsWith(str, "failIfMajorPerformanceCaveat")) {
      str +=
          " (about:config override available:"
          " webgl.disable-fail-if-major-performance-caveat)";
    }
    notLost.info.error = str;
707
  }
708
  if (!notLost.info.error.empty()) {
709
710
711
    ThrowEvent_WebGLContextCreationError(notLost.info.error);
    return false;
  }
712
  mNotLost = pNotLost;
713
  MarkCanvasDirty();
714
715
716

  // Init state
  const auto& limits = Limits();
717
  auto& state = State();
718
719
  state.mDefaultTfo = new WebGLTransformFeedbackJS(*this);
  state.mDefaultVao = new WebGLVertexArrayJS(*this);
720

721
722
  state.mBoundTfo = state.mDefaultTfo;
  state.mBoundVao = state.mDefaultVao;
723

724
  (void)state.mBoundBufferByTarget[LOCAL_GL_ARRAY_BUFFER];
725

726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
  state.mTexUnits.resize(limits.maxTexUnits);
  state.mBoundUbos.resize(limits.maxUniformBufferBindings);

  {
    webgl::TypedQuad initVal;
    const float fData[4] = {0, 0, 0, 1};
    memcpy(initVal.data, fData, sizeof(initVal.data));
    state.mGenericVertexAttribs.resize(limits.maxVertexAttribs, initVal);
  }

  const auto& size = DrawingBufferSize();
  state.mViewport = {0, 0, static_cast<int32_t>(size.x),
                     static_cast<int32_t>(size.y)};
  state.mScissor = state.mViewport;

  if (mIsWebGL2) {
    // Insert keys to enable slots:
    (void)state.mBoundBufferByTarget[LOCAL_GL_COPY_READ_BUFFER];
    (void)state.mBoundBufferByTarget[LOCAL_GL_COPY_WRITE_BUFFER];
    (void)state.mBoundBufferByTarget[LOCAL_GL_PIXEL_PACK_BUFFER];
    (void)state.mBoundBufferByTarget[LOCAL_GL_PIXEL_UNPACK_BUFFER];
    (void)state.mBoundBufferByTarget[LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER];
    (void)state.mBoundBufferByTarget[LOCAL_GL_UNIFORM_BUFFER];

    (void)state.mCurrentQueryByTarget[LOCAL_GL_ANY_SAMPLES_PASSED];
751
752
    //(void)state.mCurrentQueryByTarget[LOCAL_GL_ANY_SAMPLES_PASSED_CONSERVATIVE];
    //// Same slot as ANY_SAMPLES_PASSED.
753
754
    (void)state
        .mCurrentQueryByTarget[LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN];
755
756
  }

757
  return true;
758
759
}

760
761
// -------

762
763
uvec2 ClientWebGLContext::DrawingBufferSize() {
  if (IsContextLost()) return {};
764
765
  const auto notLost =
      mNotLost;  // Hold a strong-ref to prevent LoseContext=>UAF.
766
767
  auto& state = State();
  auto& size = state.mDrawingBufferSize;
768

769
  if (!size) {
770
771
772
773
774
775
776
777
778
779
    const auto& inProcess = mNotLost->inProcess;
    if (inProcess) {
      size = Some(inProcess->DrawingBufferSize());
    } else {
      const auto& child = mNotLost->outOfProcess->mWebGLChild;
      child->FlushPendingCmds();
      uvec2 actual = {};
      if (!child->SendDrawingBufferSize(&actual)) return {};
      size = Some(actual);
    }
780
781
  }

782
  return *size;
783
784
785
}

void ClientWebGLContext::OnMemoryPressure() {
786
787
788
789
790
791
792
793
  if (IsContextLost()) return;

  const auto& inProcess = mNotLost->inProcess;
  if (inProcess) {
    return inProcess->OnMemoryPressure();
  }
  const auto& child = mNotLost->outOfProcess->mWebGLChild;
  (void)child->SendOnMemoryPressure();
794
795
796
797
798
799
}

NS_IMETHODIMP
ClientWebGLContext::SetContextOptions(JSContext* cx,
                                      JS::Handle<JS::Value> options,
                                      ErrorResult& aRvForDictionaryInit) {
800
  if (mInitialOptions && options.isNullOrUndefined()) return NS_OK;
801

802
  dom::WebGLContextAttributes attributes;
803
804
805
806
807
808
809
810
811
812
813
814
815
  if (!attributes.Init(cx, options)) {
    aRvForDictionaryInit.Throw(NS_ERROR_UNEXPECTED);
    return NS_ERROR_UNEXPECTED;
  }

  WebGLContextOptions newOpts;

  newOpts.stencil = attributes.mStencil;
  newOpts.depth = attributes.mDepth;
  newOpts.premultipliedAlpha = attributes.mPremultipliedAlpha;
  newOpts.preserveDrawingBuffer = attributes.mPreserveDrawingBuffer;
  newOpts.failIfMajorPerformanceCaveat =
      attributes.mFailIfMajorPerformanceCaveat;
816
  newOpts.xrCompatible = attributes.mXrCompatible;
817
818
  newOpts.powerPreference = attributes.mPowerPreference;
  newOpts.enableDebugRendererInfo =
819
      StaticPrefs::webgl_enable_debug_renderer_info();
820
821
822
823
824
825
826
827
828
829
830
831
832
  MOZ_ASSERT(mCanvasElement || mOffscreenCanvas);
  newOpts.shouldResistFingerprinting =
      mCanvasElement ?
                     // If we're constructed from a canvas element
          nsContentUtils::ShouldResistFingerprinting(GetOwnerDoc())
                     :
                     // If we're constructed from an offscreen canvas
          nsContentUtils::ShouldResistFingerprinting(
              mOffscreenCanvas->GetOwnerGlobal()->PrincipalOrNull());

  if (attributes.mAlpha.WasPassed()) {
    newOpts.alpha = attributes.mAlpha.Value();
  }
833
834
835
  if (attributes.mAntialias.WasPassed()) {
    newOpts.antialias = attributes.mAntialias.Value();
  }
836
837

  // Don't do antialiasing if we've disabled MSAA.
838
  if (!StaticPrefs::webgl_msaa_samples()) {
839
840
841
    newOpts.antialias = false;
  }

842
843
844
  if (mInitialOptions && *mInitialOptions != newOpts) {
    // Err if the options asked for aren't the same as what they were
    // originally.
845
846
847
    return NS_ERROR_FAILURE;
  }

848
849
  mXRCompatible = attributes.mXrCompatible;

850
  mInitialOptions.emplace(newOpts);
851
852
853
854
855
  return NS_OK;
}

void ClientWebGLContext::DidRefresh() { Run<RPROC(DidRefresh)>(); }

856
already_AddRefed<gfx::SourceSurface> ClientWebGLContext::GetSurfaceSnapshot(
857
858
859
860
861
862
    gfxAlphaType* const out_alphaType) {
  const FuncScope funcScope(*this, "<GetSurfaceSnapshot>");
  if (IsContextLost()) return nullptr;
  const auto notLost =
      mNotLost;  // Hold a strong-ref to prevent LoseContext=>UAF.

863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
  auto ret = BackBufferSnapshot();
  if (!ret) return nullptr;

  // -

  const auto& options = mNotLost->info.options;

  auto srcAlphaType = gfxAlphaType::Opaque;
  if (options.alpha) {
    if (options.premultipliedAlpha) {
      srcAlphaType = gfxAlphaType::Premult;
    } else {
      srcAlphaType = gfxAlphaType::NonPremult;
    }
  }

  if (out_alphaType) {
    *out_alphaType = srcAlphaType;
  } else {
    // Expects Opaque or Premult
    if (srcAlphaType == gfxAlphaType::NonPremult) {
      const auto nonPremultSurf = ret;
      const auto& size = nonPremultSurf->GetSize();
      const auto format = nonPremultSurf->GetFormat();
      ret = gfx::Factory::CreateDataSourceSurface(size, format, /*zero=*/false);
      gfxUtils::PremultiplyDataSurface(nonPremultSurf, ret);
    }
  }

  return ret.forget();
}

895
896
RefPtr<gfx::SourceSurface> ClientWebGLContext::GetFrontBufferSnapshot(
    const bool requireAlphaPremult) {
897
898
899
900
901
902
903
  const FuncScope funcScope(*this, "<GetSurfaceSnapshot>");
  if (IsContextLost()) return nullptr;
  const auto notLost =
      mNotLost;  // Hold a strong-ref to prevent LoseContext=>UAF.

  const auto& options = mNotLost->info.options;

904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
  const auto surfFormat = options.alpha ? gfx::SurfaceFormat::B8G8R8A8
                                        : gfx::SurfaceFormat::B8G8R8X8;

  const auto fnNewSurf = [&](const uvec2 size) {
    const auto stride = size.x * 4;
    return RefPtr<gfx::DataSourceSurface>(
        gfx::Factory::CreateDataSourceSurfaceWithStride({size.x, size.y},
                                                        surfFormat, stride,
                                                        /*zero=*/true));
  };

  auto snapshot = [&]() -> RefPtr<gfx::DataSourceSurface> {
    const auto& inProcess = mNotLost->inProcess;
    if (inProcess) {
      const auto surfSize = inProcess->GetFrontBufferSize();
      const auto stride = surfSize.x * 4;
      const auto byteSize = stride * surfSize.y;
      const auto surf = fnNewSurf(surfSize);
      if (!surf) return nullptr;
      {
        const gfx::DataSourceSurface::ScopedMap map(
            surf, gfx::DataSourceSurface::READ_WRITE);
        if (!map.IsMapped()) {
          MOZ_ASSERT(false);
          return nullptr;
        }
        MOZ_RELEASE_ASSERT(map.GetStride() == static_cast<int64_t>(stride));
        auto range = Range<uint8_t>{map.GetData(), byteSize};
        if (!inProcess->FrontBufferSnapshotInto(range)) return nullptr;
      }
      return surf;
    }
    const auto& child = mNotLost->outOfProcess->mWebGLChild;
    child->FlushPendingCmds();
    webgl::FrontBufferSnapshotIpc res;
    if (!child->SendGetFrontBufferSnapshot(&res)) {
      res = {};
    }
    const auto& surfSize = res.surfSize;
943
944
945
    const webgl::RaiiShmem shmem{child, res.shmem};
    const auto& shmemBytes = shmem.ByteRange();
    if (!surfSize.x) return nullptr;  // Zero means failure.
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965

    const auto stride = surfSize.x * 4;
    const auto byteSize = stride * surfSize.y;

    const auto surf = fnNewSurf(surfSize);
    if (!surf) return nullptr;

    {
      const gfx::DataSourceSurface::ScopedMap map(
          surf, gfx::DataSourceSurface::READ_WRITE);
      if (!map.IsMapped()) {
        MOZ_ASSERT(false);
        return nullptr;
      }
      MOZ_RELEASE_ASSERT(map.GetStride() == static_cast<int64_t>(stride));
      MOZ_RELEASE_ASSERT(shmemBytes.length() == byteSize);
      memcpy(map.GetData(), shmemBytes.begin().get(), byteSize);
    }
    return surf;
  }();
966
  if (!snapshot) return nullptr;
967

968
969
970
971
972
973
  if (requireAlphaPremult && options.alpha && !options.premultipliedAlpha) {
    const auto nonPremultSurf = snapshot;
    const auto& size = nonPremultSurf->GetSize();
    const auto format = nonPremultSurf->GetFormat();
    snapshot =
        gfx::Factory::CreateDataSourceSurface(size, format, /*zero=*/false);
974
975
976
    if (!snapshot) {
      gfxCriticalNote << "CreateDataSourceSurface failed for size " << size;
    }
977
    gfxUtils::PremultiplyDataSurface(nonPremultSurf, snapshot);
978
  }
979
980

  return snapshot;
981
982
983
984
985
986
987
}

RefPtr<gfx::DataSourceSurface> ClientWebGLContext::BackBufferSnapshot() {
  if (IsContextLost()) return nullptr;
  const auto notLost =
      mNotLost;  // Hold a strong-ref to prevent LoseContext=>UAF.

988
989
990
991
992
  const auto& options = mNotLost->info.options;
  const auto& state = State();

  const auto drawFbWas = state.mBoundDrawFb;
  const auto readFbWas = state.mBoundReadFb;
993
994
  const auto pboWas =
      Find(state.mBoundBufferByTarget, LOCAL_GL_PIXEL_PACK_BUFFER);
995
996
997
998
999
1000

  const auto size = DrawingBufferSize();

  // -

  BindFramebuffer(LOCAL_GL_FRAMEBUFFER, nullptr);