LibWeb: Implement FontFaceSet.check()

This returns true if the given text can be rendered with the fonts in
the set that are fully loaded.
This commit is contained in:
Tim Ledbetter
2026-03-27 14:19:36 +00:00
committed by Sam Atkins
parent 14a0f00400
commit 657060ccc2
Notes: github-actions[bot] 2026-03-27 15:30:12 +00:00
5 changed files with 66 additions and 1 deletions

View File

@@ -295,6 +295,35 @@ JS::ThrowCompletionOr<GC::Ref<WebIDL::Promise>> FontFaceSet::load(String const&
return promise;
}
// https://drafts.csswg.org/css-font-loading/#dom-fontfaceset-check
WebIDL::ExceptionOr<bool> FontFaceSet::check(String const& font, String const& text)
{
// 1. Let font face set be the FontFaceSet object this method was called on.
GC::Ref font_face_set = *this;
auto& realm = this->realm();
// 2. Find the matching font faces from font face set using the font and text arguments passed to the function, and
// including system fonts, and let font face list be the returned list of font faces, and found faces be the
// returned found faces flag. If a syntax error was returned, throw a SyntaxError exception and terminate these
// steps.
auto result = TRY(find_matching_font_faces(realm, font_face_set, font, text));
// 3. If font face list is empty, or all fonts in the font face list either have a status attribute of "loaded" or
// are system fonts, return true. Otherwise, return false.
if (result->set_size() == 0)
return true;
for (auto font_face_value : *result) {
auto& font_face = as<FontFace>(font_face_value.key.as_object());
// FIXME: We should check if the font face is a system font here.
if (font_face.status() != Bindings::FontFaceLoadStatus::Loaded)
return false;
}
return true;
}
// https://drafts.csswg.org/css-font-loading/#font-face-set-ready
GC::Ref<WebIDL::Promise> FontFaceSet::ready() const
{

View File

@@ -40,6 +40,7 @@ public:
WebIDL::CallbackType* onloadingerror();
JS::ThrowCompletionOr<GC::Ref<WebIDL::Promise>> load(String const& font, String const& text);
WebIDL::ExceptionOr<bool> check(String const& font, String const& text);
Vector<GC::Ref<FontFace>>& loading_fonts() { return m_loading_fonts; }
Vector<GC::Ref<FontFace>>& loaded_fonts() { return m_loaded_fonts; }

View File

@@ -24,7 +24,7 @@ interface FontFaceSet : EventTarget {
// return whether all fonts in the fontlist are loaded
// (does not initiate load if not available)
// FIXME: boolean check(CSSOMString font, optional CSSOMString text = " ");
[FIXME] boolean check(CSSOMString font, optional CSSOMString text = "");
boolean check(CSSOMString font, optional CSSOMString text = "");
// async notification that font loading and layout operations are done
readonly attribute Promise<FontFaceSet> ready;

View File

@@ -0,0 +1,5 @@
Check invalid font: PASS
Check CSS keyword as font: PASS
Check non-existent font: PASS
Check unloaded font: PASS
Check loaded font: PASS

View File

@@ -0,0 +1,30 @@
<!DOCTYPE html>
<script src="../include.js"></script>
<script>
promiseTest(async () => {
const fontFaceSet = document.fonts;
try {
fontFaceSet.check("invalid");
println("Check invalid font: FAIL");
} catch (e) {
println(`Check invalid font: ${e.name === "SyntaxError" ? "PASS" : "FAIL"}`);
}
try {
fontFaceSet.check("revert");
println("Check CSS keyword as font: FAIL");
} catch (e) {
println(`Check CSS keyword as font: ${e.name === "SyntaxError" ? "PASS" : "FAIL"}`);
}
println(`Check non-existent font: ${fontFaceSet.check("10px NonExistentFont") === true ? "PASS" : "FAIL"}`);
const fontFace = new FontFace("Hash Sans", "url(../../../Assets/HashSans.woff)");
fontFaceSet.add(fontFace);
println(`Check unloaded font: ${fontFaceSet.check("1em Hash Sans") === false ? "PASS" : "FAIL"}`);
await fontFace.load();
println(`Check loaded font: ${fontFaceSet.check("1em Hash Sans") === true ? "PASS" : "FAIL"}`);
});
</script>