diff --git a/src/browser/js/bridge.zig b/src/browser/js/bridge.zig
index 2801f86b..fcbbea21 100644
--- a/src/browser/js/bridge.zig
+++ b/src/browser/js/bridge.zig
@@ -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"),
diff --git a/src/browser/tests/css/font_face.html b/src/browser/tests/css/font_face.html
new file mode 100644
index 00000000..b515984b
--- /dev/null
+++ b/src/browser/tests/css/font_face.html
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/browser/webapi/css/FontFace.zig b/src/browser/webapi/css/FontFace.zig
new file mode 100644
index 00000000..2196350f
--- /dev/null
+++ b/src/browser/webapi/css/FontFace.zig
@@ -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", .{});
+}
diff --git a/src/browser/webapi/css/FontFaceSet.zig b/src/browser/webapi/css/FontFaceSet.zig
index 28703080..c91ebd58 100644
--- a/src/browser/webapi/css/FontFaceSet.zig
+++ b/src/browser/webapi/css/FontFaceSet.zig
@@ -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");