mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-03-31 01:28:55 +00:00
Introduce StyleManager
A Page now has a StyleManager. The StyleManager currently answers two questions: 1 - Is an element hidden 2 - Does an element have pointer-events == none This is used in calls such as element.checkVisibility which, on some pages, can be called tens of thousands of times (often through other methods, like element.getBoundingClientRect). This _can_ be a bottleneck. The StyleManager keeps a list of rules. The rules include the selector, specificity, and properties that we care about. Rules in a stylesheet that contain no properties of interest are ignored. This is the first and likely most significant optimization. Presumably, most CSS rules don't have a display/visibility/opacity or pointer-events property. The list is rules is cached until stylesheets are modified or delete. When this happens, the StyleManager is flagged as "dirty" and rebuilt on-demand in the next query. This is our second major optimization. For now, to check if an element is visible, we still need to scan all rules. But having a pre-build subset of all the rules is a first step. The next step might be to optimize the matching, or possibly optimizing common cases (e.g. id and/or simple class selector)
This commit is contained in:
@@ -45,8 +45,9 @@ pub fn jsonStringify(self: @This(), jw: *std.json.Stringify) error{WriteFailed}!
|
||||
log.err(.app, "listener map failed", .{ .err = err });
|
||||
return error.WriteFailed;
|
||||
};
|
||||
var css_cache: Element.CssCache = .empty;
|
||||
self.walk(self.dom_node, &xpath_buffer, null, &visitor, 1, listener_targets, &css_cache) catch |err| {
|
||||
var visibility_cache: Element.VisibilityCache = .empty;
|
||||
var pointer_events_cache: Element.PointerEventsCache = .empty;
|
||||
self.walk(self.dom_node, &xpath_buffer, null, &visitor, 1, listener_targets, &visibility_cache, &pointer_events_cache) catch |err| {
|
||||
log.err(.app, "semantic tree json dump failed", .{ .err = err });
|
||||
return error.WriteFailed;
|
||||
};
|
||||
@@ -59,8 +60,9 @@ pub fn textStringify(self: @This(), writer: *std.Io.Writer) error{WriteFailed}!v
|
||||
log.err(.app, "listener map failed", .{ .err = err });
|
||||
return error.WriteFailed;
|
||||
};
|
||||
var css_cache: Element.CssCache = .empty;
|
||||
self.walk(self.dom_node, &xpath_buffer, null, &visitor, 1, listener_targets, &css_cache) catch |err| {
|
||||
var visibility_cache: Element.VisibilityCache = .empty;
|
||||
var pointer_events_cache: Element.PointerEventsCache = .empty;
|
||||
self.walk(self.dom_node, &xpath_buffer, null, &visitor, 1, listener_targets, &visibility_cache, &pointer_events_cache) catch |err| {
|
||||
log.err(.app, "semantic tree text dump failed", .{ .err = err });
|
||||
return error.WriteFailed;
|
||||
};
|
||||
@@ -84,7 +86,7 @@ const NodeData = struct {
|
||||
node_name: []const u8,
|
||||
};
|
||||
|
||||
fn walk(self: @This(), node: *Node, xpath_buffer: *std.ArrayList(u8), parent_name: ?[]const u8, visitor: anytype, index: usize, listener_targets: interactive.ListenerTargetMap, css_cache: ?*Element.CssCache) !void {
|
||||
fn walk(self: @This(), node: *Node, xpath_buffer: *std.ArrayList(u8), parent_name: ?[]const u8, visitor: anytype, index: usize, listener_targets: interactive.ListenerTargetMap, visibility_cache: ?*Element.VisibilityCache, pointer_events_cache: ?*Element.PointerEventsCache) !void {
|
||||
// 1. Skip non-content nodes
|
||||
if (node.is(Element)) |el| {
|
||||
const tag = el.getTag();
|
||||
@@ -94,7 +96,7 @@ fn walk(self: @This(), node: *Node, xpath_buffer: *std.ArrayList(u8), parent_nam
|
||||
if (tag == .datalist or tag == .option or tag == .optgroup) return;
|
||||
|
||||
// Check visibility using the engine's checkVisibility which handles CSS display: none
|
||||
if (!el.checkVisibilityCached(css_cache, self.page)) {
|
||||
if (!el.checkVisibilityCached(visibility_cache, self.page)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -135,7 +137,7 @@ fn walk(self: @This(), node: *Node, xpath_buffer: *std.ArrayList(u8), parent_nam
|
||||
}
|
||||
|
||||
if (el.is(Element.Html)) |html_el| {
|
||||
if (interactive.classifyInteractivity(self.page, el, html_el, listener_targets, css_cache) != null) {
|
||||
if (interactive.classifyInteractivity(self.page, el, html_el, listener_targets, pointer_events_cache) != null) {
|
||||
is_interactive = true;
|
||||
}
|
||||
}
|
||||
@@ -215,7 +217,7 @@ fn walk(self: @This(), node: *Node, xpath_buffer: *std.ArrayList(u8), parent_nam
|
||||
}
|
||||
gop.value_ptr.* += 1;
|
||||
|
||||
try self.walk(child, xpath_buffer, name, visitor, gop.value_ptr.*, listener_targets, css_cache);
|
||||
try self.walk(child, xpath_buffer, name, visitor, gop.value_ptr.*, listener_targets, visibility_cache, pointer_events_cache);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user