diff --git a/src/browser/StyleManager.zig b/src/browser/StyleManager.zig
index d9672e67..723a9a51 100644
--- a/src/browser/StyleManager.zig
+++ b/src/browser/StyleManager.zig
@@ -45,6 +45,7 @@ pub const PointerEventsCache = std.AutoHashMapUnmanaged(*Element, bool);
const StyleManager = @This();
const Tag = Element.Tag;
+const Input = Element.Html.Input;
const RuleList = std.MultiArrayList(VisibilityRule);
frame: *Frame,
@@ -234,14 +235,44 @@ pub fn isHidden(self: *StyleManager, el: *Element, cache: ?*VisibilityCache, opt
}
/// Computed display:none for a single element (own property, no ancestor walk).
-/// Also honors the HTML `hidden` attribute, matching the UA stylesheet rule
-/// `[hidden] { display: none }`.
+/// Honors the UA stylesheet rules per HTML Rendering §15.3.1 "Hidden elements"
+/// via `isElementHidden`.
pub fn hasDisplayNone(self: *StyleManager, el: *Element) bool {
self.rebuildIfDirty() catch return false;
- if (el.hasAttributeSafe(comptime .wrap("hidden"))) return true;
return self.isElementHidden(el, .{});
}
+/// Centralizes UA-stylesheet display:none truth so `getComputedStyle().display`
+/// (via `hasDisplayNone`) and `el.checkVisibility()` (via `isHidden`) agree.
+/// Spec: HTML Rendering §15.3.1 "Hidden elements".
+fn matchesUaDisplayNoneRule(el: *Element) bool {
+ // Tag check first: O(1) switch, exits for the ~95% of elements with
+ // ordinary tags before we touch the attribute list.
+ const tag = el.getTag();
+ if (tag.isHiddenByUaStylesheet()) return true;
+
+ if (el.hasAttributeSafe(comptime .wrap("hidden"))) return true;
+
+ // input[type="hidden" i] { display: none !important }
+ // _input_type is parsed case-insensitively at attribute-set time.
+ if (tag == .input) {
+ if (el.is(Input)) |input| {
+ if (input._input_type == .hidden) return true;
+ }
+ }
+
+ // details:not([open]) > *:not(summary) { display: none }
+ if (tag != .summary) {
+ if (el.parentElement()) |parent| {
+ if (parent.getTag() == .details and !parent.hasAttributeSafe(comptime .wrap("open"))) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
/// Computed visibility:hidden for an element, considering only the `visibility`
/// chain (walks ancestors since `visibility` inherits by default). Ignores
/// display:none: an ancestor with display:none means the element isn't
@@ -313,6 +344,16 @@ fn isElementHidden(self: *StyleManager, el: *Element, options: CheckVisibilityOp
opacity_priority = INLINE_PRIORITY;
}
+ // UA stylesheet display:none rules (HTML Rendering §15.3.1 "Hidden elements").
+ // Skipped when an inline `display` is set so author overrides win per cascade.
+ // All UA rules are short-circuited here; the spec marks some as `!important`
+ // (`input[type=hidden]`, `noscript`) and others as normal-origin, but author
+ // CSS overriding `
+
+
+
+
+
+
+
+
+
+