tor-browser

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

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:
Mgfx/tests/mochitest/mochitest.toml | 3+++
Agfx/tests/mochitest/test_offscreen_missing_fonts.html | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mgfx/thebes/gfxTextRun.cpp | 39++++++++++++++++++++++++++++++++-------
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))); + } } }