commit 1641cfad2817da46e141f6b38c42aebb30d681c7
parent 8c6bdf95da7975bbd7e69323cf8d1c172923fbb7
Author: Jonathan Kew <jkew@mozilla.com>
Date: Thu, 4 Dec 2025 16:54:35 +0000
Bug 1871755 - Make gfxMissingFontRecorder::Flush safe to call from worker threads. r=gfx-reviewers,lsalzman
The included testcase hits this crash reliably for me in local testing,
and passes once the patch is applied.
Differential Revision: https://phabricator.services.mozilla.com/D275077
Diffstat:
3 files changed, 91 insertions(+), 7 deletions(-)
diff --git a/gfx/tests/mochitest/mochitest.toml b/gfx/tests/mochitest/mochitest.toml
@@ -15,3 +15,6 @@ skip-if = [
"asan", # Race between pref service and gfx platform IPC causes frequent failures on debug/ASan
"debug", # Race between pref service and gfx platform IPC causes frequent failures on debug/ASan
]
+
+["test_offscreen_missing_fonts.html"]
+prefs=["gfx.missing_fonts.notify=true"]
diff --git a/gfx/tests/mochitest/test_offscreen_missing_fonts.html b/gfx/tests/mochitest/test_offscreen_missing_fonts.html
@@ -0,0 +1,56 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<title>Missing-font notifications from OffscreenCanvas2d</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+<script>
+SimpleTest.waitForExplicitFinish();
+
+// Workaround for bug 1962172: this will cause the CJK fallback order
+// to be loaded on the main thread, before the worker tries to use it.
+// (Can be removed when bug 1962172 is resolved.)
+let canvas = new OffscreenCanvas(100, 100);
+let ctx = canvas.getContext("2d");
+ctx.measureText("\u4567\u9876");
+
+// Try measuring a selection of characters from across the
+// Unicode space; almost certainly this will hit some codepoints
+// for which font support is lacking.
+const blob = new Blob([`
+ self.onmessage = (evt) => {
+ let ctx = evt.data.canvas.getContext("2d");
+ // Sampling planes 0 and 1 is enough; plane 2 just adds more CJK, not new scripts.
+ for (plane = 0; plane < 2; ++plane) {
+ let txt = "Plane " + plane + " text: ";
+ for (block = 0; block < 256; ++block) {
+ for (ch = 0; ch < 256; ch += 64) {
+ txt += String.fromCodePoint(plane * 0x10000 + block * 0x100 + ch);
+ }
+ }
+ ctx.measureText(txt);
+ postMessage(txt);
+ }
+ postMessage("Done");
+ }
+`], {
+ type: "application/typescript",
+});
+
+const url = URL.createObjectURL(blob);
+const worker = new Worker(url);
+
+worker.onmessage = (msg) => {
+ ok(true, msg.data);
+ if (msg.data == "Done") {
+ SimpleTest.finish();
+ }
+};
+
+let offscreen = new OffscreenCanvas(100, 100);
+worker.postMessage({ canvas: offscreen }, [offscreen]);
+</script>
+</body>
+</html>
diff --git a/gfx/thebes/gfxTextRun.cpp b/gfx/thebes/gfxTextRun.cpp
@@ -3985,15 +3985,33 @@ gfxFont::Metrics gfxFontGroup::GetMetricsForCSSUnits(
return metrics;
}
+class DeferredNotifyMissingFonts final : public nsIRunnable {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ explicit DeferredNotifyMissingFonts(nsString&& aScriptList)
+ : mScriptList(std::move(aScriptList)) {}
+
+ protected:
+ virtual ~DeferredNotifyMissingFonts() {}
+
+ NS_IMETHOD Run(void) override {
+ nsCOMPtr<nsIObserverService> service = GetObserverService();
+ service->NotifyObservers(nullptr, "font-needed", mScriptList.get());
+ return NS_OK;
+ }
+
+ nsString mScriptList;
+};
+
+NS_IMPL_ISUPPORTS(DeferredNotifyMissingFonts, nsIRunnable)
+
void gfxMissingFontRecorder::Flush() {
- static bool mNotifiedFontsInitialized = false;
static uint32_t mNotifiedFonts[gfxMissingFontRecorder::kNumScriptBitsWords];
- if (!mNotifiedFontsInitialized) {
- memset(&mNotifiedFonts, 0, sizeof(mNotifiedFonts));
- mNotifiedFontsInitialized = true;
- }
+ static StaticMutex sNotifiedFontsMutex;
nsAutoString fontNeeded;
+ sNotifiedFontsMutex.Lock();
for (uint32_t i = 0; i < kNumScriptBitsWords; ++i) {
mMissingFonts[i] &= ~mNotifiedFonts[i];
if (!mMissingFonts[i]) {
@@ -4018,8 +4036,15 @@ void gfxMissingFontRecorder::Flush() {
}
mMissingFonts[i] = 0;
}
+ sNotifiedFontsMutex.Unlock();
+
if (!fontNeeded.IsEmpty()) {
- nsCOMPtr<nsIObserverService> service = GetObserverService();
- service->NotifyObservers(nullptr, "font-needed", fontNeeded.get());
+ if (NS_IsMainThread()) {
+ nsCOMPtr<nsIObserverService> service = GetObserverService();
+ service->NotifyObservers(nullptr, "font-needed", fontNeeded.get());
+ } else {
+ NS_DispatchToMainThread(
+ new DeferredNotifyMissingFonts(std::move(fontNeeded)));
+ }
}
}