Merge branch 'main' into perf/skip-inline-style-alloc

This commit is contained in:
Adrià Arrufat
2026-07-01 00:23:11 +02:00
4 changed files with 57 additions and 3 deletions

View File

@@ -93,7 +93,7 @@ fn handleConnection(self: *TestHTTPServer, conn: std.net.Server.Connection) !voi
error.BrokenPipe => {},
else => {
std.debug.print("test http error '{s}': {}\n", .{ req.head.target, err });
try req.respond("server error", .{ .status = .internal_server_error });
req.respond("server error", .{ .status = .internal_server_error }) catch {};
},
}
return;

View File

@@ -875,6 +875,15 @@ fn getInlineStyleProperty(el: *Element, property_name: String, frame: *Frame) ?*
return style.asCSSStyleDeclaration().findProperty(property_name);
}
/// Resolved value of an element's inline `style=` declaration for `property_name`,
/// or null when the element has no such declaration. Reads the element's parsed
/// inline style (the same source `el.style` exposes), so `getComputedStyle` and
/// `el.style` agree on inline values instead of resolving them independently.
pub fn inlineStyleValue(self: *StyleManager, el: *Element, property_name: String) ?[]const u8 {
const property = getInlineStyleProperty(el, property_name, self.frame) orelse return null;
return property._value.str();
}
const testing = @import("../testing.zig");
test "StyleManager: computeSpecificity: element selector" {
// div -> (0, 0, 1)

View File

@@ -198,3 +198,30 @@
testing.expectEqual('1', divStyle.opacity);
}
</script>
<script id="computedStyleInline">
{
const div = document.createElement('div');
div.style.cssText = 'text-transform: uppercase; font-weight: 700; color: rgb(1, 2, 3)';
document.body.appendChild(div);
const cs = window.getComputedStyle(div);
testing.expectEqual('uppercase', cs.getPropertyValue('text-transform'));
testing.expectEqual('700', cs.getPropertyValue('font-weight'));
testing.expectEqual('rgb(1, 2, 3)', cs.getPropertyValue('color'));
testing.expectEqual('rgb(1, 2, 3)', div.style.getPropertyValue('color'));
testing.expectEqual('', cs.getPropertyValue('letter-spacing'));
const plainDiv = document.createElement('div');
document.body.appendChild(plainDiv);
testing.expectEqual('block', window.getComputedStyle(plainDiv).getPropertyValue('display'));
// A normal declaration must not override an earlier !important one, and the
// computed and inline (el.style) paths must agree on the resolved value.
const impDiv = document.createElement('div');
impDiv.setAttribute('style', 'color: red !important; color: blue');
document.body.appendChild(impDiv);
testing.expectEqual('red', window.getComputedStyle(impDiv).getPropertyValue('color'));
testing.expectEqual('red', impDiv.style.getPropertyValue('color'));
}
</script>

View File

@@ -49,7 +49,7 @@ pub fn init(element: ?*Element, is_computed: bool, frame: *Frame) !*CSSStyleDecl
if (el.getAttributeSafe(comptime .wrap("style"))) |attr_value| {
var it = CssParser.parseDeclarationsList(attr_value);
while (it.next()) |declaration| {
try self.setPropertyImpl(declaration.name, declaration.value, declaration.important, frame);
try self.applyParsedDeclaration(declaration, frame);
}
}
}
@@ -96,6 +96,11 @@ pub fn getPropertyValue(self: *const CSSStyleDeclaration, property_name: []const
const prop = self.findProperty(wrapped) orelse {
// Only return default values for computed styles
if (self._is_computed) {
if (self._element) |element| {
// Resolve inline `style=` declarations through the element's
// parsed inline style, so computed values match `el.style`.
if (frame._style_manager.inlineStyleValue(element, wrapped)) |value| return value;
}
return getDefaultPropertyValue(self, wrapped);
}
return "";
@@ -124,6 +129,19 @@ pub fn setProperty(self: *CSSStyleDeclaration, property_name: []const u8, value:
try self.syncStyleAttribute(frame);
}
/// Apply one declaration parsed from a `style=` block. Unlike the imperative
/// setProperty path, within a single declaration block a normal declaration must
/// not override an earlier !important one (CSS cascade precedence).
fn applyParsedDeclaration(self: *CSSStyleDeclaration, declaration: CssParser.Declaration, frame: *Frame) !void {
if (!declaration.important) {
const normalized = normalizePropertyName(declaration.name, &frame.buf);
if (self.findProperty(.wrap(normalized))) |existing| {
if (existing._important) return;
}
}
try self.setPropertyImpl(declaration.name, declaration.value, declaration.important, frame);
}
fn setPropertyImpl(self: *CSSStyleDeclaration, property_name: []const u8, value: []const u8, important: bool, frame: *Frame) !void {
if (value.len == 0) {
_ = try self.removePropertyImpl(property_name, frame);
@@ -207,7 +225,7 @@ pub fn setCssText(self: *CSSStyleDeclaration, text: []const u8, frame: *Frame) !
// Parse and set new properties
var it = CssParser.parseDeclarationsList(text);
while (it.next()) |declaration| {
try self.setPropertyImpl(declaration.name, declaration.value, declaration.important, frame);
try self.applyParsedDeclaration(declaration, frame);
}
try self.syncStyleAttribute(frame);
}