diff --git a/src/browser/Page.zig b/src/browser/Page.zig index 28c1fcac..80500316 100644 --- a/src/browser/Page.zig +++ b/src/browser/Page.zig @@ -1024,6 +1024,12 @@ pub fn createElement(self: *Page, ns_: ?[]const u8, name: []const u8, attribute_ else => {}, }, 4 => switch (@as(u32, @bitCast(name[0..4].*))) { + asUint("span") => return self.createHtmlElementT( + Element.Html.Generic, + namespace, + attribute_iterator, + .{ ._proto = undefined, ._tag_name = String.init(undefined, "span", .{}) catch unreachable, ._tag = .span }, + ), asUint("meta") => return self.createHtmlElementT( Element.Html.Meta, namespace, @@ -1036,6 +1042,12 @@ pub fn createElement(self: *Page, ns_: ?[]const u8, name: []const u8, attribute_ attribute_iterator, .{ ._proto = undefined }, ), + asUint("slot") => return self.createHtmlElementT( + Element.Html.Slot, + namespace, + attribute_iterator, + .{ ._proto = undefined }, + ), asUint("html") => return self.createHtmlElementT( Element.Html.Html, namespace, @@ -1066,12 +1078,6 @@ pub fn createElement(self: *Page, ns_: ?[]const u8, name: []const u8, attribute_ attribute_iterator, .{ ._proto = undefined, ._tag_name = String.init(undefined, "main", .{}) catch unreachable, ._tag = .main }, ), - asUint("span") => return self.createHtmlElementT( - Element.Html.Generic, - namespace, - attribute_iterator, - .{ ._proto = undefined, ._tag_name = String.init(undefined, "span", .{}) catch unreachable, ._tag = .span }, - ), else => {}, }, 5 => switch (@as(u40, @bitCast(name[0..5].*))) { @@ -1787,3 +1793,7 @@ const testing = @import("../testing.zig"); test "WebApi: Page" { try testing.htmlRunner("page", .{}); } + +test "WebApi: Integration" { + try testing.htmlRunner("integration", .{}); +} diff --git a/src/browser/ScriptManager.zig b/src/browser/ScriptManager.zig index 632be5f2..0d421db1 100644 --- a/src/browser/ScriptManager.zig +++ b/src/browser/ScriptManager.zig @@ -249,11 +249,14 @@ pub fn addFromElement(self: *ScriptManager, script_element: *Element.Html.Script .error_callback = Script.errorCallback, }); - log.debug(.http, "script queue", .{ - .ctx = ctx, - .url = remote_url.?, - .stack = page.js.stackTrace() catch "???", - }); + if (comptime IS_DEBUG) { + log.debug(.http, "script queue", .{ + .ctx = ctx, + .url = remote_url.?, + .element = element, + .stack = page.js.stackTrace() catch "???", + }); + } } if (script.mode != .normal) { @@ -326,12 +329,14 @@ pub fn preloadImport(self: *ScriptManager, url: [:0]const u8, referrer: []const var headers = try self.client.newHeaders(); try self.page.requestCookie(.{}).headersForRequest(self.page.arena, url, &headers); - log.debug(.http, "script queue", .{ - .url = url, - .ctx = "module", - .referrer = referrer, - .stack = self.page.js.stackTrace() catch "???", - }); + if (comptime IS_DEBUG) { + log.debug(.http, "script queue", .{ + .url = url, + .ctx = "module", + .referrer = referrer, + .stack = self.page.js.stackTrace() catch "???", + }); + } try self.client.request(.{ .url = url, @@ -403,12 +408,14 @@ pub fn getAsyncImport(self: *ScriptManager, url: [:0]const u8, cb: ImportAsync.C var headers = try self.client.newHeaders(); try self.page.requestCookie(.{}).headersForRequest(self.page.arena, url, &headers); - log.debug(.http, "script queue", .{ - .url = url, - .ctx = "dynamic module", - .referrer = referrer, - .stack = self.page.js.stackTrace() catch "???", - }); + if (comptime IS_DEBUG) { + log.debug(.http, "script queue", .{ + .url = url, + .ctx = "dynamic module", + .referrer = referrer, + .stack = self.page.js.stackTrace() catch "???", + }); + } // It's possible, but unlikely, for client.request to immediately finish // a request, thus calling our callback. We generally don't want a call @@ -617,11 +624,13 @@ const Script = struct { return; } - log.debug(.http, "script header", .{ - .req = transfer, - .status = header.status, - .content_type = header.contentType(), - }); + if (comptime IS_DEBUG) { + log.debug(.http, "script header", .{ + .req = transfer, + .status = header.status, + .content_type = header.contentType(), + }); + } // If this isn't true, then we'll likely leak memory. If you don't // set `CURLOPT_SUPPRESS_CONNECT_HEADERS` and CONNECT to a proxy, this @@ -649,7 +658,9 @@ const Script = struct { fn doneCallback(ctx: *anyopaque) !void { const self: *Script = @ptrCast(@alignCast(ctx)); self.complete = true; - log.debug(.http, "script fetch complete", .{ .req = self.url }); + if (comptime IS_DEBUG) { + log.debug(.http, "script fetch complete", .{ .req = self.url }); + } const manager = self.manager; if (self.mode == .async or self.mode == .import_async) { diff --git a/src/browser/js/bridge.zig b/src/browser/js/bridge.zig index 7ac4f0ad..b93c3ec0 100644 --- a/src/browser/js/bridge.zig +++ b/src/browser/js/bridge.zig @@ -538,6 +538,7 @@ pub const JsApis = flattenTypes(&.{ @import("../webapi/element/html/Paragraph.zig"), @import("../webapi/element/html/Script.zig"), @import("../webapi/element/html/Select.zig"), + @import("../webapi/element/html/Slot.zig"), @import("../webapi/element/html/Style.zig"), @import("../webapi/element/html/Template.zig"), @import("../webapi/element/html/TextArea.zig"), @@ -574,4 +575,5 @@ pub const JsApis = flattenTypes(&.{ @import("../webapi/Blob.zig"), @import("../webapi/File.zig"), @import("../webapi/Screen.zig"), + @import("../webapi/PerformanceObserver.zig"), }); diff --git a/src/browser/tests/element/html/slot.html b/src/browser/tests/element/html/slot.html new file mode 100644 index 00000000..af2b0808 --- /dev/null +++ b/src/browser/tests/element/html/slot.html @@ -0,0 +1,384 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/browser/tests/integration/custom_element_composition.html b/src/browser/tests/integration/custom_element_composition.html new file mode 100644 index 00000000..3559d9f8 --- /dev/null +++ b/src/browser/tests/integration/custom_element_composition.html @@ -0,0 +1,456 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/browser/tests/window/window.html b/src/browser/tests/window/window.html index c378c130..9cd74b37 100644 --- a/src/browser/tests/window/window.html +++ b/src/browser/tests/window/window.html @@ -102,4 +102,3 @@ testing.expectEqual(24, screen.pixelDepth); testing.expectEqual(screen, window.screen); - diff --git a/src/browser/webapi/Element.zig b/src/browser/webapi/Element.zig index 023da510..fb9b927e 100644 --- a/src/browser/webapi/Element.zig +++ b/src/browser/webapi/Element.zig @@ -148,6 +148,7 @@ pub fn getTagNameLower(self: *const Element) []const u8 { .p => "p", .script => "script", .select => "select", + .slot => "slot", .style => "style", .template => "template", .text_area => "textarea", @@ -192,6 +193,7 @@ pub fn getTagNameSpec(self: *const Element, buf: []u8) []const u8 { .p => "P", .script => "SCRIPT", .select => "SELECT", + .slot => "SLOT", .style => "STYLE", .template => "TEMPLATE", .text_area => "TEXTAREA", @@ -790,6 +792,7 @@ pub fn getTag(self: *const Element) Tag { .generic => |g| g._tag, .script => .script, .select => .select, + .slot => .slot, .option => .option, .template => .template, .text_area => .textarea, @@ -855,6 +858,7 @@ pub const Tag = enum { rect, script, select, + slot, span, strong, style, diff --git a/src/browser/webapi/Performance.zig b/src/browser/webapi/Performance.zig index 3ac87871..60b972a3 100644 --- a/src/browser/webapi/Performance.zig +++ b/src/browser/webapi/Performance.zig @@ -1,6 +1,13 @@ const js = @import("../js/js.zig"); const datetime = @import("../../datetime.zig"); +pub fn registerTypes() []const type { + return &.{ + Performance, + Entry, + }; +} + const Performance = @This(); _time_origin: u64, @@ -34,6 +41,65 @@ pub const JsApi = struct { pub const timeOrigin = bridge.accessor(Performance.getTimeOrigin, null, .{}); }; +pub const Entry = struct { + _duration: f64 = 0.0, + _entry_type: Type, + _name: []const u8, + _start_time: f64 = 0.0, + + const Type = enum { + element, + event, + first_input, + largest_contentful_paint, + layout_shift, + long_animation_frame, + longtask, + mark, + measure, + navigation, + paint, + resource, + taskattribution, + visibility_state, + }; + + pub fn getDuration(self: *const Entry) f64 { + return self._duration; + } + + pub fn getEntryType(self: *const Entry) []const u8 { + return switch (self._entry_type) { + .first_input => "first-input", + .largest_contentful_paint => "largest-contentful-paint", + .layout_shift => "layout-shift", + .long_animation_frame => "long-animation-frame", + .visibility_state => "visibility-state", + else => |t| @tagName(t), + }; + } + + pub fn getName(self: *const Entry) []const u8 { + return self._name; + } + + pub fn getStartTime(self: *const Entry) f64 { + return self._start_time; + } + + pub const JsApi = struct { + pub const bridge = js.Bridge(Entry); + + pub const Meta = struct { + pub const name = "PerformanceEntry"; + pub const prototype_chain = bridge.prototypeChain(); + pub var class_id: bridge.ClassId = undefined; + }; + pub const duration = bridge.accessor(Entry.getDuration, null, .{}); + pub const entryType = bridge.accessor(Entry.getEntryType, null, .{}); + }; +}; + const testing = @import("../../testing.zig"); test "WebApi: Performance" { try testing.htmlRunner("performance.html", .{}); diff --git a/src/browser/webapi/PerformanceObserver.zig b/src/browser/webapi/PerformanceObserver.zig new file mode 100644 index 00000000..08bc2733 --- /dev/null +++ b/src/browser/webapi/PerformanceObserver.zig @@ -0,0 +1,67 @@ +// Copyright (C) 2023-2025 Lightpanda (Selecy SAS) +// +// Francis Bouvier