commit 3dcf89739daf0e26532d06c3c8a9be37c22f7cfa
parent 8af71fddd509ec43dfa57e2841c06b483388a4a0
Author: Jonathan Kew <jkew@mozilla.com>
Date: Fri, 5 Dec 2025 14:04:13 +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, 85 insertions(+), 7 deletions(-)
diff --git a/gfx/tests/crashtests/1871755-1.html b/gfx/tests/crashtests/1871755-1.html
@@ -0,0 +1,52 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<head>
+<title>Missing-font notifications from OffscreenCanvas2d</title>
+</head>
+<body>
+</body>
+<script>
+// 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) => {
+ console.log(msg.data);
+ if (msg.data == "Done") {
+ document.documentElement.classList.remove("reftest-wait")
+ }
+};
+
+let offscreen = new OffscreenCanvas(100, 100);
+worker.postMessage({ canvas: offscreen }, [offscreen]);
+</script>
+</html>
diff --git a/gfx/tests/crashtests/crashtests.list b/gfx/tests/crashtests/crashtests.list
@@ -225,6 +225,7 @@ load 1808830.html
load 1825450.html
load 1683679.html
load 1843622.html
+skip-if(Android&&debug) pref(gfx.missing_fonts.notify,true) load 1871755-1.html
load 1898569.html
load 1938548.html
load 1983053.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)));
+ }
}
}