commit f26084f2dfc00c1e10377d4433cfea594f7ea8c2
parent 032930c7c7426846e41cd0488abd968be76279af
Author: Nathan Agrin <n8agrin@gmail.com>
Date: Tue, 23 Dec 2025 16:45:32 +0000
Bug 2004797 - Only call UpdateContext when creating a new context in OffscreenCanvas r=lsalzman
In Javascript, when getContext('2d') is called multiple times on an OffscreenCanvas instance transferred via
transferControlToOffscreen(), a new ImageContainer (in CPP code) was created each time. This caused the canvas to noticeably flicker.
This fix ensures only one ImageContainer instance is created per OffscreenCanvas, eliminating the flicker.
Differential Revision: https://phabricator.services.mozilla.com/D275549
Diffstat:
3 files changed, 49 insertions(+), 6 deletions(-)
diff --git a/dom/canvas/OffscreenCanvasDisplayHelper.cpp b/dom/canvas/OffscreenCanvasDisplayHelper.cpp
@@ -122,18 +122,20 @@ RefPtr<layers::ImageContainer> OffscreenCanvasDisplayHelper::GetImageContainer()
void OffscreenCanvasDisplayHelper::UpdateContext(
OffscreenCanvas* aOffscreenCanvas, RefPtr<ThreadSafeWorkerRef>&& aWorkerRef,
CanvasContextType aType, const Maybe<mozilla::ipc::ActorId>& aChildId) {
- RefPtr<layers::ImageContainer> imageContainer =
- MakeRefPtr<layers::ImageContainer>(
- layers::ImageUsageType::OffscreenCanvas,
- layers::ImageContainer::ASYNCHRONOUS);
-
MutexAutoLock lock(mMutex);
+ // Only create ImageContainer if we don't already have one (Bug 2004797).
+ // Recreating it would discard any existing frames, causing flicker.
+ if (!mImageContainer) {
+ mImageContainer = MakeRefPtr<layers::ImageContainer>(
+ layers::ImageUsageType::OffscreenCanvas,
+ layers::ImageContainer::ASYNCHRONOUS);
+ }
+
mOffscreenCanvas = aOffscreenCanvas;
mWorkerRef = std::move(aWorkerRef);
mType = aType;
mContextChildId = aChildId;
- mImageContainer = std::move(imageContainer);
if (aChildId) {
mContextManagerId = Some(gfx::CanvasManagerChild::Get()->Id());
diff --git a/dom/canvas/gtest/TestOffscreenCanvas.cpp b/dom/canvas/gtest/TestOffscreenCanvas.cpp
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 2; 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 "OffscreenCanvasDisplayHelper.h"
+#include "gtest/gtest.h"
+#include "mozilla/dom/CanvasRenderingContextHelper.h"
+#include "mozilla/dom/WorkerRef.h"
+
+namespace mozilla::dom {
+
+// Bug 2004797: Verify UpdateContext preserves ImageContainer.
+// Recreating ImageContainer on each call discards existing frames, causing
+// flicker when getContext() is called repeatedly.
+TEST(OffscreenCanvasDisplayHelper, UpdateContextPreservesImageContainer)
+{
+ RefPtr<OffscreenCanvasDisplayHelper> helper =
+ MakeRefPtr<OffscreenCanvasDisplayHelper>(nullptr, 100, 100);
+
+ // Initially no ImageContainer
+ EXPECT_EQ(helper->GetImageContainer(), nullptr);
+
+ // First UpdateContext creates an ImageContainer
+ helper->UpdateContext(nullptr, nullptr, CanvasContextType::Canvas2D,
+ Nothing());
+ RefPtr<layers::ImageContainer> container1 = helper->GetImageContainer();
+ EXPECT_NE(container1, nullptr);
+
+ // Second UpdateContext should preserve the same ImageContainer
+ helper->UpdateContext(nullptr, nullptr, CanvasContextType::Canvas2D,
+ Nothing());
+ RefPtr<layers::ImageContainer> container2 = helper->GetImageContainer();
+ EXPECT_EQ(container1.get(), container2.get());
+}
+
+} // namespace mozilla::dom
diff --git a/dom/canvas/gtest/moz.build b/dom/canvas/gtest/moz.build
@@ -6,10 +6,14 @@
LOCAL_INCLUDES += [
"/dom/canvas",
+ "/dom/workers",
]
UNIFIED_SOURCES += [
+ "TestOffscreenCanvas.cpp",
"TestSanitizeRenderer.cpp",
]
+include("/ipc/chromium/chromium-config.mozbuild")
+
FINAL_LIBRARY = "xul-gtest"