add FontFace constructor and FontFaceSet.add()

headless stub for the FontFace API — constructor stores family/source,
status is always "loaded", load() resolves immediately. enables sites
that use new FontFace() for programmatic font loading (e.g. boursorama).
This commit is contained in:
egrs
2026-03-09 08:14:41 +01:00
parent 0227afffc8
commit aa051434cb
4 changed files with 169 additions and 0 deletions

View File

@@ -730,6 +730,7 @@ pub const JsApis = flattenTypes(&.{
@import("../webapi/css/CSSStyleRule.zig"),
@import("../webapi/css/CSSStyleSheet.zig"),
@import("../webapi/css/CSSStyleProperties.zig"),
@import("../webapi/css/FontFace.zig"),
@import("../webapi/css/FontFaceSet.zig"),
@import("../webapi/css/MediaQueryList.zig"),
@import("../webapi/css/StyleSheetList.zig"),

View File

@@ -0,0 +1,63 @@
<!DOCTYPE html>
<script src="../testing.js"></script>
<script id="constructor_basic">
{
const face = new FontFace("TestFont", "url(test.woff)");
testing.expectTrue(face instanceof FontFace);
}
</script>
<script id="constructor_name">
{
testing.expectEqual('FontFace', FontFace.name);
}
</script>
<script id="family_property">
{
const face = new FontFace("MyFont", "url(font.woff2)");
testing.expectEqual("MyFont", face.family);
}
</script>
<script id="status_is_loaded">
{
const face = new FontFace("F", "url(f.woff)");
testing.expectEqual("loaded", face.status);
}
</script>
<script id="loaded_is_promise">
{
const face = new FontFace("F", "url(f.woff)");
testing.expectTrue(face.loaded instanceof Promise);
}
</script>
<script id="load_returns_promise">
{
const face = new FontFace("F", "url(f.woff)");
testing.expectTrue(face.load() instanceof Promise);
}
</script>
<script id="default_descriptors">
{
const face = new FontFace("F", "url(f.woff)");
testing.expectEqual("normal", face.style);
testing.expectEqual("normal", face.weight);
testing.expectEqual("normal", face.stretch);
testing.expectEqual("normal", face.variant);
testing.expectEqual("normal", face.featureSettings);
testing.expectEqual("auto", face.display);
}
</script>
<script id="document_fonts_add">
{
const face = new FontFace("AddedFont", "url(added.woff)");
const result = document.fonts.add(face);
testing.expectTrue(result === document.fonts);
}
</script>

View File

@@ -0,0 +1,98 @@
const js = @import("../../js/js.zig");
const Page = @import("../../Page.zig");
const FontFace = @This();
_family: []const u8,
_source: []const u8,
_style: []const u8,
_weight: []const u8,
_stretch: []const u8,
_unicode_range: []const u8,
_variant: []const u8,
_feature_settings: []const u8,
_display: []const u8,
pub fn init(family: []const u8, source: []const u8, page: *Page) !*FontFace {
return page._factory.create(FontFace{
._family = try page.dupeString(family),
._source = try page.dupeString(source),
._style = "normal",
._weight = "normal",
._stretch = "normal",
._unicode_range = "U+0-10FFFF",
._variant = "normal",
._feature_settings = "normal",
._display = "auto",
});
}
pub fn getFamily(self: *const FontFace) []const u8 {
return self._family;
}
pub fn getStyle(self: *const FontFace) []const u8 {
return self._style;
}
pub fn getWeight(self: *const FontFace) []const u8 {
return self._weight;
}
pub fn getStretch(self: *const FontFace) []const u8 {
return self._stretch;
}
pub fn getUnicodeRange(self: *const FontFace) []const u8 {
return self._unicode_range;
}
pub fn getVariant(self: *const FontFace) []const u8 {
return self._variant;
}
pub fn getFeatureSettings(self: *const FontFace) []const u8 {
return self._feature_settings;
}
pub fn getDisplay(self: *const FontFace) []const u8 {
return self._display;
}
// load() - resolves immediately; headless browser has no real font loading.
pub fn load(_: *FontFace, page: *Page) !js.Promise {
return page.js.local.?.resolvePromise({});
}
// loaded - returns an already-resolved Promise.
pub fn getLoaded(_: *FontFace, page: *Page) !js.Promise {
return page.js.local.?.resolvePromise({});
}
pub const JsApi = struct {
pub const bridge = js.Bridge(FontFace);
pub const Meta = struct {
pub const name = "FontFace";
pub const prototype_chain = bridge.prototypeChain();
pub var class_id: bridge.ClassId = undefined;
};
pub const constructor = bridge.constructor(FontFace.init, .{});
pub const family = bridge.accessor(FontFace.getFamily, null, .{});
pub const style = bridge.accessor(FontFace.getStyle, null, .{});
pub const weight = bridge.accessor(FontFace.getWeight, null, .{});
pub const stretch = bridge.accessor(FontFace.getStretch, null, .{});
pub const unicodeRange = bridge.accessor(FontFace.getUnicodeRange, null, .{});
pub const variant = bridge.accessor(FontFace.getVariant, null, .{});
pub const featureSettings = bridge.accessor(FontFace.getFeatureSettings, null, .{});
pub const display = bridge.accessor(FontFace.getDisplay, null, .{});
pub const status = bridge.property("loaded", .{ .template = false, .readonly = true });
pub const loaded = bridge.accessor(FontFace.getLoaded, null, .{});
pub const load = bridge.function(FontFace.load, .{});
};
const testing = @import("../../../testing.zig");
test "WebApi: FontFace" {
try testing.htmlRunner("css/font_face.html", .{});
}

View File

@@ -1,6 +1,7 @@
const std = @import("std");
const js = @import("../../js/js.zig");
const Page = @import("../../Page.zig");
const FontFace = @import("FontFace.zig");
const FontFaceSet = @This();
@@ -29,6 +30,11 @@ pub fn load(_: *FontFaceSet, font: []const u8, page: *Page) !js.Promise {
return page.js.local.?.resolvePromise({});
}
// add(fontFace) - no-op; headless browser does not track loaded fonts.
pub fn add(self: *FontFaceSet, _: *FontFace) *FontFaceSet {
return self;
}
pub const JsApi = struct {
pub const bridge = js.Bridge(FontFaceSet);
@@ -43,6 +49,7 @@ pub const JsApi = struct {
pub const ready = bridge.accessor(FontFaceSet.getReady, null, .{});
pub const check = bridge.function(FontFaceSet.check, .{});
pub const load = bridge.function(FontFaceSet.load, .{});
pub const add = bridge.function(FontFaceSet.add, .{});
};
const testing = @import("../../../testing.zig");