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:
@@ -77,10 +77,11 @@ pub fn item(self: *const CSSStyleDeclaration, index: u32) []const u8 {
|
||||
|
||||
pub fn getPropertyValue(self: *const CSSStyleDeclaration, property_name: []const u8, page: *Page) []const u8 {
|
||||
const normalized = normalizePropertyName(property_name, &page.buf);
|
||||
const prop = self.findProperty(normalized) orelse {
|
||||
const wrapped = String.wrap(normalized);
|
||||
const prop = self.findProperty(wrapped) orelse {
|
||||
// Only return default values for computed styles
|
||||
if (self._is_computed) {
|
||||
return getDefaultPropertyValue(self, normalized);
|
||||
return getDefaultPropertyValue(self, wrapped);
|
||||
}
|
||||
return "";
|
||||
};
|
||||
@@ -89,7 +90,7 @@ pub fn getPropertyValue(self: *const CSSStyleDeclaration, property_name: []const
|
||||
|
||||
pub fn getPropertyPriority(self: *const CSSStyleDeclaration, property_name: []const u8, page: *Page) []const u8 {
|
||||
const normalized = normalizePropertyName(property_name, &page.buf);
|
||||
const prop = self.findProperty(normalized) orelse return "";
|
||||
const prop = self.findProperty(.wrap(normalized)) orelse return "";
|
||||
return if (prop._important) "important" else "";
|
||||
}
|
||||
|
||||
@@ -120,7 +121,7 @@ fn setPropertyImpl(self: *CSSStyleDeclaration, property_name: []const u8, value:
|
||||
const normalized_value = try normalizePropertyValue(page.call_arena, normalized, value);
|
||||
|
||||
// Find existing property
|
||||
if (self.findProperty(normalized)) |existing| {
|
||||
if (self.findProperty(.wrap(normalized))) |existing| {
|
||||
existing._value = try String.init(page.arena, normalized_value, .{});
|
||||
existing._important = important;
|
||||
return;
|
||||
@@ -144,7 +145,7 @@ pub fn removeProperty(self: *CSSStyleDeclaration, property_name: []const u8, pag
|
||||
|
||||
fn removePropertyImpl(self: *CSSStyleDeclaration, property_name: []const u8, page: *Page) ![]const u8 {
|
||||
const normalized = normalizePropertyName(property_name, &page.buf);
|
||||
const prop = self.findProperty(normalized) orelse return "";
|
||||
const prop = self.findProperty(.wrap(normalized)) orelse return "";
|
||||
|
||||
// the value might not be on the heap (it could be inlined in the small string
|
||||
// optimization), so we need to dupe it.
|
||||
@@ -208,11 +209,11 @@ pub fn format(self: *const CSSStyleDeclaration, writer: *std.Io.Writer) !void {
|
||||
}
|
||||
}
|
||||
|
||||
fn findProperty(self: *const CSSStyleDeclaration, name: []const u8) ?*Property {
|
||||
pub fn findProperty(self: *const CSSStyleDeclaration, name: String) ?*Property {
|
||||
var node = self._properties.first;
|
||||
while (node) |n| {
|
||||
const prop = Property.fromNodeLink(n);
|
||||
if (prop._name.eqlSlice(name)) {
|
||||
if (prop._name.eql(name)) {
|
||||
return prop;
|
||||
}
|
||||
node = n.next;
|
||||
@@ -617,26 +618,36 @@ fn isLengthProperty(name: []const u8) bool {
|
||||
return length_properties.has(name);
|
||||
}
|
||||
|
||||
fn getDefaultPropertyValue(self: *const CSSStyleDeclaration, normalized_name: []const u8) []const u8 {
|
||||
if (std.mem.eql(u8, normalized_name, "visibility")) {
|
||||
return "visible";
|
||||
fn getDefaultPropertyValue(self: *const CSSStyleDeclaration, name: String) []const u8 {
|
||||
switch (name.len) {
|
||||
5 => {
|
||||
if (name.eql(comptime .wrap("color"))) {
|
||||
const element = self._element orelse return "";
|
||||
return getDefaultColor(element);
|
||||
}
|
||||
},
|
||||
7 => {
|
||||
if (name.eql(comptime .wrap("opacity"))) {
|
||||
return "1";
|
||||
}
|
||||
if (name.eql(comptime .wrap("display"))) {
|
||||
const element = self._element orelse return "";
|
||||
return getDefaultDisplay(element);
|
||||
}
|
||||
},
|
||||
10 => {
|
||||
if (name.eql(comptime .wrap("visibility"))) {
|
||||
return "visible";
|
||||
}
|
||||
},
|
||||
16 => {
|
||||
if (name.eqlSlice("background-color")) {
|
||||
// transparent
|
||||
return "rgba(0, 0, 0, 0)";
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
if (std.mem.eql(u8, normalized_name, "opacity")) {
|
||||
return "1";
|
||||
}
|
||||
if (std.mem.eql(u8, normalized_name, "display")) {
|
||||
const element = self._element orelse return "";
|
||||
return getDefaultDisplay(element);
|
||||
}
|
||||
if (std.mem.eql(u8, normalized_name, "color")) {
|
||||
const element = self._element orelse return "";
|
||||
return getDefaultColor(element);
|
||||
}
|
||||
if (std.mem.eql(u8, normalized_name, "background-color")) {
|
||||
// transparent
|
||||
return "rgba(0, 0, 0, 0)";
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user