From 3ca82b9ab53133f113758a6364e1dd321769dec1 Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Wed, 24 Dec 2025 15:04:06 +0800 Subject: [PATCH] Tweak CSS Give default styles to visibility properties. Unblocks various playwright behavior --- src/browser/tests/element/styles.html | 71 +++++++++++++++++++ src/browser/webapi/Window.zig | 6 +- .../webapi/css/CSSStyleDeclaration.zig | 52 +++++++++++++- src/browser/webapi/css/CSSStyleProperties.zig | 14 ++-- 4 files changed, 135 insertions(+), 8 deletions(-) diff --git a/src/browser/tests/element/styles.html b/src/browser/tests/element/styles.html index 89946038..e4b7c489 100644 --- a/src/browser/tests/element/styles.html +++ b/src/browser/tests/element/styles.html @@ -127,3 +127,74 @@ testing.expectEqual('important', div.style.getPropertyPriority('color')); } + + + + diff --git a/src/browser/webapi/Window.zig b/src/browser/webapi/Window.zig index b9060aaf..6f9a03c4 100644 --- a/src/browser/webapi/Window.zig +++ b/src/browser/webapi/Window.zig @@ -40,7 +40,7 @@ const MessageEvent = @import("event/MessageEvent.zig"); const MediaQueryList = @import("css/MediaQueryList.zig"); const storage = @import("storage/storage.zig"); const Element = @import("Element.zig"); -const CSSStyleDeclaration = @import("css/CSSStyleDeclaration.zig"); +const CSSStyleProperties = @import("css/CSSStyleProperties.zig"); const CustomElementRegistry = @import("CustomElementRegistry.zig"); const Window = @This(); @@ -319,8 +319,8 @@ pub fn matchMedia(_: *const Window, query: []const u8, page: *Page) !*MediaQuery }); } -pub fn getComputedStyle(_: *const Window, _: *Element, page: *Page) !*CSSStyleDeclaration { - return CSSStyleDeclaration.init(null, page); +pub fn getComputedStyle(_: *const Window, element: *Element, page: *Page) !*CSSStyleProperties { + return CSSStyleProperties.init(element, page); } pub fn postMessage(self: *Window, message: js.Object, target_origin: ?[]const u8, page: *Page) !void { diff --git a/src/browser/webapi/css/CSSStyleDeclaration.zig b/src/browser/webapi/css/CSSStyleDeclaration.zig index 536fa737..afa83202 100644 --- a/src/browser/webapi/css/CSSStyleDeclaration.zig +++ b/src/browser/webapi/css/CSSStyleDeclaration.zig @@ -55,7 +55,9 @@ pub fn item(self: *const CSSStyleDeclaration, index: u32) []const u8 { pub fn getPropertyValue(self: *const CSSStyleDeclaration, property_name: []const u8, page: *Page) []const u8 { const normalized = normalizePropertyName(property_name, &page.buf); - const prop = self.findProperty(normalized) orelse return ""; + const prop = self.findProperty(normalized) orelse { + return getDefaultPropertyValue(self, normalized); + }; return prop._value.str(); } @@ -192,6 +194,54 @@ fn normalizePropertyName(name: []const u8, buf: []u8) []const u8 { return std.ascii.lowerString(buf, name); } +fn getDefaultPropertyValue(self: *const CSSStyleDeclaration, normalized_name: []const u8) []const u8 { + if (std.mem.eql(u8, normalized_name, "visibility")) { + return "visible"; + } + if (std.mem.eql(u8, normalized_name, "opacity")) { + return "1"; + } + if (std.mem.eql(u8, normalized_name, "display")) { + const element = self._element orelse return ""; + return getDefaultDisplay(element); + } + + return ""; +} + +fn getDefaultDisplay(element: *const Element) []const u8 { + switch (element._type) { + .html => |html| { + return switch (html._type) { + .anchor, .br => "inline", + .body, .div, .p, .heading, .form, .button, .canvas, .dialog, .embed, .head, .html, .hr, .iframe, .img, .input, .li, .link, .meta, .ol, .option, .script, .select, .slot, .style, .template, .textarea, .title, .ul, .media => "block", + .generic, .custom, .unknown, .data => blk: { + const tag = element.getTagNameLower(); + if (isInlineTag(tag)) break :blk "inline"; + break :blk "block"; + }, + }; + }, + .svg => return "inline", + } +} + +fn isInlineTag(tag_name: []const u8) bool { + const inline_tags = [_][]const u8{ + "abbr", "b", "bdi", "bdo", "cite", "code", "dfn", + "em", "i", "kbd", "mark", "q", "s", "samp", + "small", "span", "strong", "sub", "sup", "time", "u", + "var", "wbr", + }; + + for (inline_tags) |inline_tag| { + if (std.mem.eql(u8, tag_name, inline_tag)) { + return true; + } + } + return false; +} + pub const Property = struct { _name: String, _value: String, diff --git a/src/browser/webapi/css/CSSStyleProperties.zig b/src/browser/webapi/css/CSSStyleProperties.zig index 6a92295a..bb1ec70c 100644 --- a/src/browser/webapi/css/CSSStyleProperties.zig +++ b/src/browser/webapi/css/CSSStyleProperties.zig @@ -106,6 +106,8 @@ fn isKnownCSSProperty(dash_case: []const u8) bool { .{ "width", {} }, .{ "height", {} }, .{ "display", {} }, + .{ "visibility", {} }, + .{ "opacity", {} }, .{ "position", {} }, .{ "top", {} }, .{ "bottom", {} }, @@ -139,10 +141,14 @@ fn camelCaseToDashCase(name: []const u8, buf: []u8) []const u8 { } // Check for vendor prefixes: webkitTransform -> -webkit-transform - const has_vendor_prefix = name.len > 6 and (std.mem.startsWith(u8, name, "webkit") or - std.mem.startsWith(u8, name, "moz") or - std.mem.startsWith(u8, name, "ms") or - std.mem.startsWith(u8, name, "o")); + // Must have uppercase letter after the prefix + const has_vendor_prefix = blk: { + if (name.len > 6 and std.mem.startsWith(u8, name, "webkit") and std.ascii.isUpper(name[6])) break :blk true; + if (name.len > 3 and std.mem.startsWith(u8, name, "moz") and std.ascii.isUpper(name[3])) break :blk true; + if (name.len > 2 and std.mem.startsWith(u8, name, "ms") and std.ascii.isUpper(name[2])) break :blk true; + if (name.len > 1 and std.mem.startsWith(u8, name, "o") and std.ascii.isUpper(name[1])) break :blk true; + break :blk false; + }; var write_pos: usize = 0;