diff --git a/src/browser/js/bridge.zig b/src/browser/js/bridge.zig
index 3a991313..7640d0a1 100644
--- a/src/browser/js/bridge.zig
+++ b/src/browser/js/bridge.zig
@@ -713,6 +713,7 @@ pub const JsApis = flattenTypes(&.{
@import("../webapi/css/CSSStyleRule.zig"),
@import("../webapi/css/CSSStyleSheet.zig"),
@import("../webapi/css/CSSStyleProperties.zig"),
+ @import("../webapi/css/FontFaceSet.zig"),
@import("../webapi/css/MediaQueryList.zig"),
@import("../webapi/css/StyleSheetList.zig"),
@import("../webapi/Document.zig"),
diff --git a/src/browser/tests/css/font_face_set.html b/src/browser/tests/css/font_face_set.html
new file mode 100644
index 00000000..a860669e
--- /dev/null
+++ b/src/browser/tests/css/font_face_set.html
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/browser/webapi/Document.zig b/src/browser/webapi/Document.zig
index e92f0dcf..7a7989ab 100644
--- a/src/browser/webapi/Document.zig
+++ b/src/browser/webapi/Document.zig
@@ -34,6 +34,7 @@ const DOMTreeWalker = @import("DOMTreeWalker.zig");
const DOMNodeIterator = @import("DOMNodeIterator.zig");
const DOMImplementation = @import("DOMImplementation.zig");
const StyleSheetList = @import("css/StyleSheetList.zig");
+const FontFaceSet = @import("css/FontFaceSet.zig");
const Selection = @import("Selection.zig");
pub const XMLDocument = @import("XMLDocument.zig");
@@ -52,6 +53,7 @@ _elements_by_id: std.StringHashMapUnmanaged(*Element) = .empty,
_removed_ids: std.StringHashMapUnmanaged(void) = .empty,
_active_element: ?*Element = null,
_style_sheets: ?*StyleSheetList = null,
+_fonts: ?*FontFaceSet = null,
_write_insertion_point: ?*Node = null,
_script_created_parser: ?Parser.Streaming = null,
_adopted_style_sheets: ?js.Object.Global = null,
@@ -422,6 +424,15 @@ pub fn getStyleSheets(self: *Document, page: *Page) !*StyleSheetList {
return sheets;
}
+pub fn getFonts(self: *Document, page: *Page) !*FontFaceSet {
+ if (self._fonts) |fonts| {
+ return fonts;
+ }
+ const fonts = try FontFaceSet.init(page);
+ self._fonts = fonts;
+ return fonts;
+}
+
pub fn adoptNode(_: *const Document, node: *Node, page: *Page) !*Node {
if (node._type == .document) {
return error.NotSupported;
@@ -955,6 +966,7 @@ pub const JsApi = struct {
pub const implementation = bridge.accessor(Document.getImplementation, null, .{});
pub const activeElement = bridge.accessor(Document.getActiveElement, null, .{});
pub const styleSheets = bridge.accessor(Document.getStyleSheets, null, .{});
+ pub const fonts = bridge.accessor(Document.getFonts, null, .{});
pub const contentType = bridge.accessor(Document.getContentType, null, .{});
pub const domain = bridge.accessor(Document.getDomain, null, .{});
pub const createElement = bridge.function(Document.createElement, .{ .dom_exception = true });
diff --git a/src/browser/webapi/css/FontFaceSet.zig b/src/browser/webapi/css/FontFaceSet.zig
new file mode 100644
index 00000000..cd3cf2b1
--- /dev/null
+++ b/src/browser/webapi/css/FontFaceSet.zig
@@ -0,0 +1,59 @@
+const std = @import("std");
+const js = @import("../../js/js.zig");
+const Page = @import("../../Page.zig");
+
+const FontFaceSet = @This();
+
+// Padding to avoid zero-size struct, which causes identity_map pointer collisions.
+_pad: bool = false,
+
+pub fn init(page: *Page) !*FontFaceSet {
+ return page._factory.create(FontFaceSet{});
+}
+
+// FontFaceSet.ready - returns an already-resolved Promise.
+// In a headless browser there is no font loading, so fonts are always ready.
+pub fn getReady(_: *FontFaceSet, page: *Page) !js.Promise {
+ return page.js.local.?.resolvePromise({});
+}
+
+pub fn getStatus(_: *const FontFaceSet) []const u8 {
+ return "loaded";
+}
+
+pub fn getSize(_: *const FontFaceSet) u32 {
+ return 0;
+}
+
+// check(font, text?) - always true; headless has no real fonts to check.
+pub fn check(_: *const FontFaceSet, font: []const u8) bool {
+ _ = font;
+ return true;
+}
+
+// load(font, text?) - resolves immediately with an empty array.
+pub fn load(_: *FontFaceSet, font: []const u8, page: *Page) !js.Promise {
+ _ = font;
+ return page.js.local.?.resolvePromise({});
+}
+
+pub const JsApi = struct {
+ pub const bridge = js.Bridge(FontFaceSet);
+
+ pub const Meta = struct {
+ pub const name = "FontFaceSet";
+ pub const prototype_chain = bridge.prototypeChain();
+ pub var class_id: bridge.ClassId = undefined;
+ };
+
+ pub const ready = bridge.accessor(FontFaceSet.getReady, null, .{});
+ pub const status = bridge.accessor(FontFaceSet.getStatus, null, .{});
+ pub const size = bridge.accessor(FontFaceSet.getSize, null, .{});
+ pub const check = bridge.function(FontFaceSet.check, .{});
+ pub const load = bridge.function(FontFaceSet.load, .{});
+};
+
+const testing = @import("../../../testing.zig");
+test "WebApi: FontFaceSet" {
+ try testing.htmlRunner("css/font_face_set.html", .{});
+}