diff --git a/src/TestHTTPServer.zig b/src/TestHTTPServer.zig index d2d00a077..73ee07907 100644 --- a/src/TestHTTPServer.zig +++ b/src/TestHTTPServer.zig @@ -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; diff --git a/src/browser/StyleManager.zig b/src/browser/StyleManager.zig index 231de0e6f..977c7bc22 100644 --- a/src/browser/StyleManager.zig +++ b/src/browser/StyleManager.zig @@ -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) diff --git a/src/browser/tests/element/styles.html b/src/browser/tests/element/styles.html index 05dfa34f0..b4e81aa2b 100644 --- a/src/browser/tests/element/styles.html +++ b/src/browser/tests/element/styles.html @@ -198,3 +198,30 @@ testing.expectEqual('1', divStyle.opacity); } + + diff --git a/src/browser/webapi/css/CSSStyleDeclaration.zig b/src/browser/webapi/css/CSSStyleDeclaration.zig index f1c915506..393243187 100644 --- a/src/browser/webapi/css/CSSStyleDeclaration.zig +++ b/src/browser/webapi/css/CSSStyleDeclaration.zig @@ -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); }