FontFace.load sometimes never resolves
Categories
(Core :: Layout: Text and Fonts, defect)
Tracking
()
People
(Reporter: rawing7, Assigned: emilio)
Details
Attachments
(3 files)
User Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:127.0) Gecko/20100101 Firefox/127.0
Steps to reproduce:
I'm writing a web app that dynamically loads fonts via the JS FontFace
API, and occasionally the promise fontFace.load()
remains in "pending" state indefinitely.
This is the relevant TS code:
export async function registerFont(
name: string,
urls: string[]
): Promise<void> {
const VARIATIONS = [
{ weight: 'normal', style: 'normal' },
{ weight: 'bold', style: 'normal' },
{ weight: 'normal', style: 'italic' },
{ weight: 'bold', style: 'italic' },
];
let promises = new Map<string, Promise<FontFace>>();
for (let [i, url] of urls.entries()) {
let fontFace = new FontFace(name, `url(${url})`, VARIATIONS[i]);
promises.set(url, fontFace.load());
}
for (let [url, promise] of promises.entries()) {
let fontFace: FontFace;
console.log(`Loading font file ${url}`);
try {
fontFace = await promise;
} catch (error) {
console.warn(`Failed to load font file ${url}: ${error}`);
continue;
}
document.fonts.add(fontFace);
}
}
Actual results:
It printed "Loading font file <url>" in the console, and then nothing else.
Extra info:
- Rapidly reloading the page makes the bug occur more often. I can consistently reproduce the bug in 5-10 attempts.
- I think that when the bug occurs, it's always the first font file. (Or maybe all 4 of them don't load, I'm not sure)
- I can see in the "Network" tab of the browser console that all 4 font files were loaded successfully. The file size also looks correct, so I don't think the downloaded file is corrupted or anything.
- This bug doesn't occur in Edge or Chrome.
Expected results:
FontFace.load()
should resolve, either successfully or with an error.
Comment 1•6 months ago
|
||
The Bugbug bot thinks this bug should belong to the 'Core::Layout: Text and Fonts' component, and is moving the bug to that component. Please correct in case you think the bot is wrong.
Comment 2•6 months ago
|
||
Thanks for filing the bug. Can you perhaps provide a small working example where we can try to reproduce the bug?
I'm not quite sure how to do that. I tried to write a small HTML file that loads the font front the local file system, but Firefox kept saying "An invalid or illegal string was specified". Do you have any suggestions? Would a python script that starts a fastapi server be ok?
Assignee | ||
Comment 4•6 months ago
|
||
Yeah, something that starts a local server is also acceptable, of course. Anything that can help us reproduce locally :)
Assignee | ||
Comment 5•6 months ago
|
||
I'd also be curious about whether this reproduces in nightly (I made some tweaks to that code recently).
Alright folks, I spent the last couple of days fiddling with this, but I just cannot come up with a small piece of code that reproduces this bug even somewhat reliably. On one hand, you really don't need much to make the bug occur:
import fastapi.responses, requests
font = requests.get('https://www.1001fonts.com/download/font/roboto.regular.ttf').content
app = fastapi.FastAPI()
@app.get('/font/{cache_killer}')
async def get_font(request: fastapi.Request, cache_killer: str):
return fastapi.responses.Response(content=font)
@app.get("/")
def get_root():
return fastapi.responses.HTMLResponse('''
<html>
<body>
<script>
let promise;
async function registerFont(){
let fontFace = new FontFace(
'Roboto',
`url(/font/${Math.random()})`,
);
promise = fontFace.load();
await promise;
setTimeout(
() => window.location.reload(),
Math.random() * 1500,
);
}
registerFont();
</script>
Please wait until it stops reloading...
</body>
</html>
''')
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="localhost", port=8000)
But I must've let this (and similar code) refresh thousands of times and the bug only occurred 3-4 times.
The most reliable way to repro this bug is to visit https://rio.dev/examples and refresh a bunch of times. I can usually get it within 5-20 refreshes there. (Refreshing quickly tends to work better, but sometimes you may have to slow down a bit.)
Comment 7•5 months ago
|
||
On nightly on Mac:
I ran the python script and initial result is this error when first loading the page:
downloadable font: GSUB: too large substitute: 65535 (font-family: "Roboto" ...
which seems to indicate an error with the downloaded font.
I changed the font to be downlaoded from: https://fonts.gstatic.com/s/roboto/v30/KFOiCnqEu92Fr1Mu51QrEz0dL-vwnYh2eg.woff2
and it started reloading the page. It ran for about 5 minutes and never stopped.
I may have inadvertently overstated the repro rate of the python script. What I wanted to express was "Don't even bother with this script, it's only here because I wanted to assure you guys that this isn't a bug in the web server".
Heads up: rio.dev now tries to work around this bug by re-loading the font if the promise doesn't resolve within a certain amount of time. If the bug does occur, you should see the website be blank (pure white) for about 2 seconds, then a warning saying "Timeout loading font face <url>, retrying" will be printed in the browser console, and then it will make another attempt to load the font. If you do see this warning, you can access the unresolved FontFace.load()
Promise in the browser console via the global variable p
.
Assignee | ||
Comment 10•5 months ago
|
||
I see that website is basically in https://github.com/rio-labs/rio/, right? If so, does running https://github.com/rio-labs/rio/ with https://github.com/rio-labs/rio/commit/76bfb9c8910b29667e5f3f64faf7215817493eb1 reverted repro locally? Feel free to send me steps to do so, I could try that.
That said, it seems I can repro that just fine, you should've started with: Load rio.dev/examples, and refresh!
Looking at it a bit closer:
- It doesn't seem a recent regression, Firefox 120 shows the same issue. I could try to dig further.
- Might it be the case that you keep alive the "loaded" promise, but the font face isn't kept alive while loading and thus gets GCd without resolving the promise? At a glance that'd explain why a reduced test-case doesn't quite work reliably.
I'll dig a bit closer as time allows.
Assignee | ||
Comment 11•5 months ago
|
||
Just to confirm, if you change the code to do something like (untested):
export async function registerFont(
name: string,
urls: string[]
): Promise<void> {
const VARIATIONS = [
{ weight: 'normal', style: 'normal' },
{ weight: 'bold', style: 'normal' },
{ weight: 'normal', style: 'italic' },
{ weight: 'bold', style: 'italic' },
];
let faces = new Map<string, FontFace>();
for (let [i, url] of urls.entries()) {
let fontFace = new FontFace(name, `url(${url})`, VARIATIONS[i]);
fontFace.load();
faces.set(url, fontFace);
}
for (let [url, face] of faces.entries()) {
let fontFace: FontFace;
console.log(`Loading font file ${url}`);
try {
fontFace = await face.loaded;
} catch (error) {
console.warn(`Failed to load font file ${url}: ${error}`);
continue;
}
document.fonts.add(fontFace);
}
}
Does it fix the bug for you? If so, that'd indicate at my (tentative) diagnosis being correct (the FontFace is GC'd while loading, and thus the promise doesn't resolve)
Assignee | ||
Comment 12•5 months ago
|
||
Yeah, a quick test shows comment 10 is exactly what's going on :)
Assignee | ||
Comment 13•5 months ago
|
||
Updated•5 months ago
|
Reporter | ||
Comment 14•5 months ago
|
||
Awesome, glad you figured it out so quickly! Now I feel stupid for wasting 3 times trying to create a reproducible example when all I had to do was tell you about the existing website :/
Updated•5 months ago
|
Assignee | ||
Comment 15•5 months ago
|
||
Ok, sent a fix, see the revision above for details. Hopefully I'm not missing anything and that is the right fix, but this code became a lot more complicated when we supported this API on workers so maybe try says something different :)
Comment 16•5 months ago
|
||
The severity field is not set for this bug.
:TYLin, could you have a look please?
For more information, please visit BugBot documentation.
Updated•5 months ago
|
Comment 17•4 months ago
|
||
Comment 18•4 months ago
•
|
||
Backed out for causing assertion failures on RuntimeService.cpp.
Later I also found this crashes on fontfaceset: https://treeherder.mozilla.org/logviewer?job_id=463738774&repo=autoland&lineNumber=2844
LATER EDIT: it seems that this has also caused some wpt leaks like this: https://treeherder.mozilla.org/logviewer?job_id=463765133&repo=autoland&lineNumber=15669
[task 2024-06-24T12:01:08.324Z] 12:01:08 INFO - TEST-START | /css/css-font-loading/fontfaceset-loading-worker-crash.html
[task 2024-06-24T12:01:08.378Z] 12:01:08 INFO - Setting pref layout.css.transition-behavior.enabled to true
[task 2024-06-24T12:01:08.841Z] 12:01:08 INFO - PID 13119 | [13430] Assertion failure: !globalScopeSentinel->IsAlive(), at /builds/worker/checkouts/gecko/dom/workers/RuntimeService.cpp:2208
[task 2024-06-24T12:01:08.925Z] 12:01:08 INFO - STDOUT: Initializing stack-fixing for the first stack frame, this may take a while...
[task 2024-06-24T12:01:09.700Z] 12:01:09 INFO - mozcrash Copy/paste: /task_171923000611071/fetches/minidump-stackwalk/minidump-stackwalk --symbols-url=https://symbols.mozilla.org/ --cyborg=/tmp/tmpk90evv5t/7fa0be95-473c-dc93-b3e0-463ddb560c57.trace /tmp/tmplu7i9e75/minidumps/7fa0be95-473c-dc93-b3e0-463ddb560c57.dmp /task_171923000611071/build/symbols
[task 2024-06-24T12:01:16.518Z] 12:01:16 INFO - mozcrash Saved minidump as /task_171923000611071/build/blobber_upload_dir/7fa0be95-473c-dc93-b3e0-463ddb560c57.dmp
[task 2024-06-24T12:01:16.518Z] 12:01:16 INFO - mozcrash Saved app info as /task_171923000611071/build/blobber_upload_dir/7fa0be95-473c-dc93-b3e0-463ddb560c57.extra
[task 2024-06-24T12:01:16.619Z] 12:01:16 INFO - PROCESS-CRASH | MOZ_ASSERT(!globalScopeSentinel->IsAlive()) [@ mozilla::dom::workerinternals::(anonymous namespace)::WorkerThreadPrimaryRunnable::Run] | /css/css-font-loading/fontfaceset-loading-worker-crash.html
[task 2024-06-24T12:01:16.619Z] 12:01:16 INFO - Process type: content
[task 2024-06-24T12:01:16.619Z] 12:01:16 INFO - Process pid: 13551
[task 2024-06-24T12:01:16.619Z] 12:01:16 INFO - Mozilla crash reason: MOZ_ASSERT(!globalScopeSentinel->IsAlive())
[task 2024-06-24T12:01:16.619Z] 12:01:16 INFO - Crash dump filename: /tmp/tmplu7i9e75/minidumps/7fa0be95-473c-dc93-b3e0-463ddb560c57.dmp
[task 2024-06-24T12:01:16.619Z] 12:01:16 INFO - Operating system: Linux
[task 2024-06-24T12:01:16.619Z] 12:01:16 INFO - 6.5.0-1020-gcp #20~22.04.1-Ubuntu SMP Wed May 1 02:03:24 UTC 2024
[task 2024-06-24T12:01:16.619Z] 12:01:16 INFO - CPU: amd64
[task 2024-06-24T12:01:16.619Z] 12:01:16 INFO - family 6 model 85 stepping 7
[task 2024-06-24T12:01:16.619Z] 12:01:16 INFO - 4 CPUs
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - Linux Ubuntu 22.04 - jammy (Ubuntu 22.04.4 LTS)
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO -
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - Crash reason: SIGSEGV / SEGV_MAPERR
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - Crash address: 0x0000000000000000
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - Crashing instruction: `mov dword [0x0], 0x8a0`
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - Memory accessed by instruction:
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - 0. Address: 0x0000000000000000
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - Size: 4
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - Process uptime: not available
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO -
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - Thread 18 DOM Worker (crashed)
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - 0 libxul.so!mozilla::dom::workerinternals::(anonymous namespace)::WorkerThreadPrimaryRunnable::Run() [RuntimeService.cpp:80e6f1d766c1cea2b750ef56f52fb0e9064ee5e3 : 2208 + 0x0]
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - rax = 0x00007fa1b779acaf rdx = 0x00007fa1c8a1b723
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - rcx = 0x00005bc780375978 rbx = 0x00007fa1b12c10c0
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - rsi = 0x0000000000000000 rdi = 0x00007fa1c8a1ca60
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - rbp = 0x00007fa1b08fe900 rsp = 0x00007fa1b08fe7b0
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - r8 = 0x0000000000000000 r9 = 0x0000000000000003
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - r10 = 0x0000000000000000 r11 = 0x0000000000000293
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - r12 = 0x00000000b12c1001 r13 = 0x0000000000000000
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - r14 = 0x00007fa1b0a73000 r15 = 0x0000000080004005
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - rip = 0x00007fa1bec16671
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - Found by: given as instruction pointer in context
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - 1 libxul.so!nsThread::ProcessNextEvent(bool, bool*) [nsThread.cpp:80e6f1d766c1cea2b750ef56f52fb0e9064ee5e3 : 1198 + 0x5]
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - rbx = 0x00007fa1b0a29e00 rbp = 0x00007fa1b08fea30
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - rsp = 0x00007fa1b08fe910 r12 = 0x00000060d84cb067
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - r13 = 0xaaaaaaaaaaaaaaaa r14 = 0x0000000000000000
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - r15 = 0x00007fa1b0a29f28 rip = 0x00007fa1ba98bb6d
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - Found by: call frame info
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - 2 libxul.so!NS_ProcessNextEvent(nsIThread*, bool) [nsThreadUtils.cpp:80e6f1d766c1cea2b750ef56f52fb0e9064ee5e3 : 480 + 0xf]
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - rbx = 0x0000000000000000 rbp = 0x00007fa1b08fea60
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - rsp = 0x00007fa1b08fea40 r12 = 0x00007fa1b08fea78
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - r13 = 0x00007fa1b1282e30 r14 = 0x00007fa1b08feb10
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - r15 = 0x00007fa1b0a29e00 rip = 0x00007fa1ba9926e0
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - Found by: call frame info
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - 3 libxul.so!mozilla::ipc::MessagePumpForNonMainThreads::Run(base::MessagePump::Delegate*) [MessagePump.cpp:80e6f1d766c1cea2b750ef56f52fb0e9064ee5e3 : 300 + 0x9]
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - rbx = 0x00007fa1b1282e00 rbp = 0x00007fa1b08feab0
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - rsp = 0x00007fa1b08fea70 r12 = 0x00007fa1b08fea78
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - r13 = 0x00007fa1b1282e30 r14 = 0x00007fa1b08feb10
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - r15 = 0x00007fa1b0a29e00 rip = 0x00007fa1bb323727
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - Found by: call frame info
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - 4 libxul.so!MessageLoop::RunHandler() [message_loop.cc:80e6f1d766c1cea2b750ef56f52fb0e9064ee5e3 : 363]
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - Found by: inlining
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - 5 libxul.so!MessageLoop::Run() [message_loop.cc:80e6f1d766c1cea2b750ef56f52fb0e9064ee5e3 : 345 + 0x4]
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - rbx = 0x00007fa1b0a3bd80 rbp = 0x00007fa1b08feae0
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - rsp = 0x00007fa1b08feac0 r12 = 0x00007fa1b0a3bd88
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - r13 = 0x0000000000000002 r14 = 0x000000000000000a
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - r15 = 0x00007fa1b08feb10 rip = 0x00007fa1bb2a61b2
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - Found by: call frame info
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - 6 libxul.so!nsThread::ThreadFunc(void*) [nsThread.cpp:80e6f1d766c1cea2b750ef56f52fb0e9064ee5e3 : 370 + 0x7]
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - rbx = 0x00007fa1b0a3bd80 rbp = 0x00007fa1b08fed10
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - rsp = 0x00007fa1b08feaf0 r12 = 0x00007fa1b0a3bd88
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - r13 = 0x0000000000000002 r14 = 0x000000000000000a
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - r15 = 0x00007fa1b08feb10 rip = 0x00007fa1ba986e64
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - Found by: call frame info
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - 7 libnspr4.so!_pt_root [ptthread.c:80e6f1d766c1cea2b750ef56f52fb0e9064ee5e3 : 201 + 0x6]
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - rbx = 0x00007fa1b47084c0 rbp = 0x00007fa1b08fed60
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - rsp = 0x00007fa1b08fed20 r12 = 0x00007fa1b08ff560
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - r13 = 0x0000000000000002 r14 = 0x00007fa1c861dc80
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - r15 = 0x00007fa1c8af8ce0 rip = 0x00007fa1c8af16f0
[task 2024-06-24T12:01:16.620Z] 12:01:16 INFO - Found by: call frame info
[task 2024-06-24T12:01:16.621Z] 12:01:16 INFO - 8 firefox-bin!set_alt_signal_stack_and_start(PthreadCreateParams*) [pthread_create_interposer.cpp:80e6f1d766c1cea2b750ef56f52fb0e9064ee5e3 : 81 + 0x5]
[task 2024-06-24T12:01:16.621Z] 12:01:16 INFO - rbx = 0x00007fa1b47084c0 rbp = 0x00007fa1b08fee10
[task 2024-06-24T12:01:16.621Z] 12:01:16 INFO - rsp = 0x00007fa1b08fed70 r12 = 0x00007fa1b08ff640
[task 2024-06-24T12:01:16.621Z] 12:01:16 INFO - r13 = 0x0000000000000000 r14 = 0x00007fa1b08fed80
[task 2024-06-24T12:01:16.621Z] 12:01:16 INFO - r15 = 0x00007fa1c8af1560 rip = 0x00005bc7802cfead
[task 2024-06-24T12:01:16.621Z] 12:01:16 INFO - Found by: call frame info
[task 2024-06-24T12:01:16.621Z] 12:01:16 INFO - 9 libc.so.6!start_thread [pthread_create.c : 442 + 0x11]
[task 2024-06-24T12:01:16.621Z] 12:01:16 INFO - rbx = 0x00007fa1b08ff640 rbp = 0x0000000000000000
[task 2024-06-24T12:01:16.621Z] 12:01:16 INFO - rsp = 0x00007fa1b08fee20 r12 = 0x00007fa1b08ff640
[task 2024-06-24T12:01:16.621Z] 12:01:16 INFO - r13 = 0x0000000000000000 r14 = 0x00007fa1c88947d0
[task 2024-06-24T12:01:16.621Z] 12:01:16 INFO - r15 = 0x00007ffcec446650 rip = 0x00007fa1c8894ac3
[task 2024-06-24T12:01:16.621Z] 12:01:16 INFO - Found by: call frame info
[task 2024-06-24T12:01:16.621Z] 12:01:16 INFO - 10 libc.so.6!__GI___clone + 0x43
[task 2024-06-24T12:01:16.621Z] 12:01:16 INFO - rbx = 0x00007ffcec4464f0 rbp = 0x0000000000000000
[task 2024-06-24T12:01:16.621Z] 12:01:16 INFO - rsp = 0x00007fa1b08feec0 r12 = 0x00007fa1b08ff640
[task 2024-06-24T12:01:16.621Z] 12:01:16 INFO - r13 = 0x0000000000000000 r14 = 0x00007fa1c88947d0
[task 2024-06-24T12:01:16.621Z] 12:01:16 INFO - r15 = 0x00007ffcec446650 rip = 0x00007fa1c8925a04
[task 2024-06-24T12:01:16.621Z] 12:01:16 INFO - Found by: call frame info
[task 2024-06-24T12:01:16.621Z] 12:01:16 INFO -
Assignee | ||
Comment 19•4 months ago
|
||
So I guess that there's an ongoing worker font load while we shutdown the worker...
Jens / Andrew, what's the best way to proceed here? Do I need to explicitly disconnect / remove the font face or so?
Once there's no wrapper to the promise we can remove the extra reference, but it's unclear what the best place to do that is given the current FontFaceSet set-up.
Comment 20•4 months ago
|
||
I'm not sure what you are asking exactly. You need to shut things down for a thread before you shut down the thread.
Assignee | ||
Comment 21•4 months ago
|
||
Yeah, sorry, the main issue is that we're keeping a platform object alive (intentionally to some extent), and that has a wrapper and promise which keeps the global alive.
Sorry, my question was a bit confused because I misunderstood the check we were doing. I was thinking we were checking JS stuff, but we're not, we're just checking that the WorkerGlobalScope
isn't kept alive...
Assignee | ||
Comment 22•4 months ago
|
||
The operator bool and IsAlive are equivalent.
Assignee | ||
Comment 23•4 months ago
|
||
We check that we can cc and kill the global object on shutdown.
At this point it doesn't really matter that the FontFace is still
loading, the promise will never resolve.
To fix this, implement the "keep alive" by using a target shutdown task.
Assignee | ||
Updated•4 months ago
|
Comment 24•4 months ago
|
||
IIUC you are discussing this already with :asuth on matrix.
Comment 25•4 months ago
|
||
Assignee | ||
Updated•4 months ago
|
Comment 26•4 months ago
|
||
bugherder |
Updated•4 months ago
|
Comment 27•4 months ago
|
||
Comment 28•4 months ago
|
||
bugherder |
Assignee | ||
Updated•4 months ago
|
Updated•4 months ago
|
Description
•