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;