dom: support css display: none in checkVisibility

Updates `Element.checkVisibility` to iterate through document
stylesheets and check for matching rules with `display: none`.
Also ensures `<style>` elements register their sheets and
initializes them immediately upon addition to the DOM.
This commit is contained in:
Adrià Arrufat
2026-03-12 20:55:44 +09:00
parent f58f6e8d65
commit 48dd80867b
4 changed files with 52 additions and 5 deletions

View File

@@ -1041,6 +1041,8 @@ pub fn parentElement(self: *Element) ?*Element {
return self._proto.parentElement(); return self._proto.parentElement();
} }
const CSSStyleRule = @import("css/CSSStyleRule.zig");
pub fn checkVisibility(self: *Element, page: *Page) bool { pub fn checkVisibility(self: *Element, page: *Page) bool {
var current: ?*Element = self; var current: ?*Element = self;
@@ -1051,9 +1053,40 @@ pub fn checkVisibility(self: *Element, page: *Page) bool {
return false; return false;
} }
} }
// Also check if any global stylesheet hides this element
const doc_sheets = page.document.getStyleSheets(page) catch null;
if (doc_sheets) |sheets| {
var i: usize = 0;
if (sheets.length() > 0) log.info(.dom, "css.visibility.check", .{});
while (i < sheets.length()) : (i += 1) {
const sheet = sheets.item(i) orelse continue;
const rules = sheet.getCssRules(page) catch continue;
var j: usize = 0;
while (j < rules.length()) : (j += 1) {
const rule = rules.item(j) orelse continue;
if (rule.is(CSSStyleRule)) |style_rule| {
const selector = style_rule.getSelectorText();
// log.info(.dom, "check_visibility.eval {s}", .{selector});
const does_match = el.matches(selector, page) catch |err| blk: {
log.info(.dom, "check_visibility.err", .{ .err = err });
break :blk false;
};
if (does_match) {
const style = (style_rule.getStyle(page) catch continue).asCSSStyleDeclaration();
const display = style.getPropertyValue("display", page);
log.info(.dom, "check_visibility.hit", .{});
if (std.mem.eql(u8, display, "none")) {
return false;
}
}
}
}
}
}
current = el.parentElement(); current = el.parentElement();
} }
return true; return true;
} }

View File

@@ -61,6 +61,7 @@ pub fn insertRule(self: *CSSStyleSheet, rule: []const u8, index: u32, page: *Pag
const style_rule = try CSSStyleRule.init(page); const style_rule = try CSSStyleRule.init(page);
try style_rule.setSelectorText(parsed_rule.selector, page); try style_rule.setSelectorText(parsed_rule.selector, page);
@import("../../../log.zig").info(.dom, "css.rule.add {s}", .{parsed_rule.selector});
const style_props = try style_rule.getStyle(page); const style_props = try style_rule.getStyle(page);
const style = style_props.asCSSStyleDeclaration(); const style = style_props.asCSSStyleDeclaration();
@@ -90,6 +91,7 @@ pub fn replaceSync(self: *CSSStyleSheet, text: []const u8, page: *Page) !void {
while (it.next()) |parsed_rule| { while (it.next()) |parsed_rule| {
const style_rule = try CSSStyleRule.init(page); const style_rule = try CSSStyleRule.init(page);
try style_rule.setSelectorText(parsed_rule.selector, page); try style_rule.setSelectorText(parsed_rule.selector, page);
@import("../../../log.zig").info(.dom, "css.rule.add {s}", .{parsed_rule.selector});
const style_props = try style_rule.getStyle(page); const style_props = try style_rule.getStyle(page);
const style = style_props.asCSSStyleDeclaration(); const style = style_props.asCSSStyleDeclaration();

View File

@@ -5,19 +5,24 @@ const CSSStyleSheet = @import("CSSStyleSheet.zig");
const StyleSheetList = @This(); const StyleSheetList = @This();
_sheets: []*CSSStyleSheet = &.{}, _sheets: std.ArrayListUnmanaged(*CSSStyleSheet) = .{},
pub fn init(page: *Page) !*StyleSheetList { pub fn init(page: *Page) !*StyleSheetList {
return page._factory.create(StyleSheetList{}); return page._factory.create(StyleSheetList{});
} }
pub fn length(self: *const StyleSheetList) u32 { pub fn length(self: *const StyleSheetList) u32 {
return @intCast(self._sheets.len); return @intCast(self._sheets.items.len);
} }
pub fn item(self: *const StyleSheetList, index: usize) ?*CSSStyleSheet { pub fn item(self: *const StyleSheetList, index: usize) ?*CSSStyleSheet {
if (index >= self._sheets.len) return null; if (index >= self._sheets.items.len) return null;
return self._sheets[index]; return self._sheets.items[index];
}
pub fn add(self: *StyleSheetList, sheet: *CSSStyleSheet, page: *Page) !void {
@import("../../../log.zig").info(.dom, "css.sheet.add", .{});
try self._sheets.append(page.arena, sheet);
} }
pub const JsApi = struct { pub const JsApi = struct {

View File

@@ -78,6 +78,7 @@ pub fn setDisabled(self: *Style, disabled: bool, page: *Page) !void {
const CSSStyleSheet = @import("../../css/CSSStyleSheet.zig"); const CSSStyleSheet = @import("../../css/CSSStyleSheet.zig");
pub fn getSheet(self: *Style, page: *Page) !?*CSSStyleSheet { pub fn getSheet(self: *Style, page: *Page) !?*CSSStyleSheet {
@import("../../../../log.zig").info(.dom, "css.style.get_sheet", .{});
// Per spec, sheet is null for disconnected elements or non-CSS types. // Per spec, sheet is null for disconnected elements or non-CSS types.
// Valid types: absent (defaults to "text/css"), empty string, or // Valid types: absent (defaults to "text/css"), empty string, or
// case-insensitive match for "text/css". // case-insensitive match for "text/css".
@@ -98,10 +99,16 @@ pub fn getSheet(self: *Style, page: *Page) !?*CSSStyleSheet {
const text = try self.asNode().getTextContentAlloc(page.call_arena); const text = try self.asNode().getTextContentAlloc(page.call_arena);
try sheet.replaceSync(text, page); try sheet.replaceSync(text, page);
const sheets = try page.document.getStyleSheets(page);
try sheets.add(sheet, page);
return sheet; return sheet;
} }
pub fn styleAddedCallback(self: *Style, page: *Page) !void { pub fn styleAddedCallback(self: *Style, page: *Page) !void {
// Force stylesheet initialization so rules are parsed immediately
_ = self.getSheet(page) catch null;
// if we're planning on navigating to another page, don't trigger load event. // if we're planning on navigating to another page, don't trigger load event.
if (page.isGoingAway()) { if (page.isGoingAway()) {
return; return;