mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-03-22 04:34:44 +00:00
Merge pull request #1602 from lightpanda-io/css-delcaration
parse style attribute on CSSStyleDeclaration init
This commit is contained in:
@@ -205,3 +205,54 @@
|
||||
testing.expectEqual('', style.getPropertyPriority('content'));
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="CSSStyleDeclaration_style_syncs_to_attribute">
|
||||
{
|
||||
// JS style modifications must be reflected in getAttribute.
|
||||
const div = document.createElement('div');
|
||||
|
||||
// Named property assignment (element.style.X = ...)
|
||||
div.style.opacity = '0';
|
||||
testing.expectEqual('opacity: 0;', div.getAttribute('style'));
|
||||
|
||||
// Update existing property
|
||||
div.style.opacity = '1';
|
||||
testing.expectEqual('opacity: 1;', div.getAttribute('style'));
|
||||
|
||||
// Add a second property
|
||||
div.style.color = 'red';
|
||||
testing.expectTrue(div.getAttribute('style').includes('opacity: 1'));
|
||||
testing.expectTrue(div.getAttribute('style').includes('color: red'));
|
||||
|
||||
// removeProperty syncs back
|
||||
div.style.removeProperty('opacity');
|
||||
testing.expectTrue(!div.getAttribute('style').includes('opacity'));
|
||||
testing.expectTrue(div.getAttribute('style').includes('color: red'));
|
||||
|
||||
// setCssText syncs back
|
||||
div.style.cssText = 'filter: blur(0px)';
|
||||
testing.expectEqual('filter: blur(0px);', div.getAttribute('style'));
|
||||
|
||||
// setCssText with empty string clears attribute
|
||||
div.style.cssText = '';
|
||||
testing.expectEqual('', div.getAttribute('style'));
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="CSSStyleDeclaration_outerHTML_reflects_style_changes">
|
||||
{
|
||||
// outerHTML must reflect JS-modified styles (regression test for
|
||||
// DOM serialization reading stale HTML-parsed attribute values).
|
||||
const div = document.createElement('div');
|
||||
div.setAttribute('style', 'filter:blur(10px);opacity:0');
|
||||
|
||||
div.style.filter = 'blur(0px)';
|
||||
div.style.opacity = '1';
|
||||
|
||||
const html = div.outerHTML;
|
||||
testing.expectTrue(html.includes('filter: blur(0px)'));
|
||||
testing.expectTrue(html.includes('opacity: 1'));
|
||||
testing.expectTrue(!html.includes('blur(10px)'));
|
||||
testing.expectTrue(!html.includes('opacity:0'));
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -33,10 +33,26 @@ _properties: std.DoublyLinkedList = .{},
|
||||
_is_computed: bool = false,
|
||||
|
||||
pub fn init(element: ?*Element, is_computed: bool, page: *Page) !*CSSStyleDeclaration {
|
||||
return page._factory.create(CSSStyleDeclaration{
|
||||
const self = try page._factory.create(CSSStyleDeclaration{
|
||||
._element = element,
|
||||
._is_computed = is_computed,
|
||||
});
|
||||
|
||||
// Parse the element's existing style attribute into _properties so that
|
||||
// subsequent JS reads and writes see all CSS properties, not just newly
|
||||
// added ones. Computed styles have no inline attribute to parse.
|
||||
if (!is_computed) {
|
||||
if (element) |el| {
|
||||
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, page);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn length(self: *const CSSStyleDeclaration) u32 {
|
||||
@@ -76,15 +92,8 @@ pub fn getPropertyPriority(self: *const CSSStyleDeclaration, property_name: []co
|
||||
}
|
||||
|
||||
pub fn setProperty(self: *CSSStyleDeclaration, property_name: []const u8, value: []const u8, priority_: ?[]const u8, page: *Page) !void {
|
||||
if (value.len == 0) {
|
||||
_ = try self.removeProperty(property_name, page);
|
||||
return;
|
||||
}
|
||||
|
||||
const normalized = normalizePropertyName(property_name, &page.buf);
|
||||
const priority = priority_ orelse "";
|
||||
|
||||
// Validate priority
|
||||
const priority = priority_ orelse "";
|
||||
const important = if (priority.len > 0) blk: {
|
||||
if (!std.ascii.eqlIgnoreCase(priority, "important")) {
|
||||
return;
|
||||
@@ -92,6 +101,19 @@ pub fn setProperty(self: *CSSStyleDeclaration, property_name: []const u8, value:
|
||||
break :blk true;
|
||||
} else false;
|
||||
|
||||
try self.setPropertyImpl(property_name, value, important, page);
|
||||
|
||||
try self.syncStyleAttribute(page);
|
||||
}
|
||||
|
||||
fn setPropertyImpl(self: *CSSStyleDeclaration, property_name: []const u8, value: []const u8, important: bool, page: *Page) !void {
|
||||
if (value.len == 0) {
|
||||
_ = try self.removePropertyImpl(property_name, page);
|
||||
return;
|
||||
}
|
||||
|
||||
const normalized = normalizePropertyName(property_name, &page.buf);
|
||||
|
||||
// Find existing property
|
||||
if (self.findProperty(normalized)) |existing| {
|
||||
existing._value = try String.init(page.arena, value, .{});
|
||||
@@ -110,6 +132,12 @@ pub fn setProperty(self: *CSSStyleDeclaration, property_name: []const u8, value:
|
||||
}
|
||||
|
||||
pub fn removeProperty(self: *CSSStyleDeclaration, property_name: []const u8, page: *Page) ![]const u8 {
|
||||
const result = try self.removePropertyImpl(property_name, page);
|
||||
try self.syncStyleAttribute(page);
|
||||
return result;
|
||||
}
|
||||
|
||||
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 "";
|
||||
|
||||
@@ -121,12 +149,21 @@ pub fn removeProperty(self: *CSSStyleDeclaration, property_name: []const u8, pag
|
||||
return old_value;
|
||||
}
|
||||
|
||||
// Serialize current properties back to the element's style attribute so that
|
||||
// DOM serialization (outerHTML, getAttribute) reflects JS-modified styles.
|
||||
fn syncStyleAttribute(self: *CSSStyleDeclaration, page: *Page) !void {
|
||||
const element = self._element orelse return;
|
||||
const css_text = try self.getCssText(page);
|
||||
try element.setAttributeSafe(comptime .wrap("style"), .wrap(css_text), page);
|
||||
}
|
||||
|
||||
pub fn getFloat(self: *const CSSStyleDeclaration, page: *Page) []const u8 {
|
||||
return self.getPropertyValue("float", page);
|
||||
}
|
||||
|
||||
pub fn setFloat(self: *CSSStyleDeclaration, value_: ?[]const u8, page: *Page) !void {
|
||||
return self.setProperty("float", value_ orelse "", null, page);
|
||||
try self.setPropertyImpl("float", value_ orelse "", false, page);
|
||||
try self.syncStyleAttribute(page);
|
||||
}
|
||||
|
||||
pub fn getCssText(self: *const CSSStyleDeclaration, page: *Page) ![]const u8 {
|
||||
@@ -153,9 +190,9 @@ pub fn setCssText(self: *CSSStyleDeclaration, text: []const u8, page: *Page) !vo
|
||||
// Parse and set new properties
|
||||
var it = CssParser.parseDeclarationsList(text);
|
||||
while (it.next()) |declaration| {
|
||||
const priority: ?[]const u8 = if (declaration.important) "important" else null;
|
||||
try self.setProperty(declaration.name, declaration.value, priority, page);
|
||||
try self.setPropertyImpl(declaration.name, declaration.value, declaration.important, page);
|
||||
}
|
||||
try self.syncStyleAttribute(page);
|
||||
}
|
||||
|
||||
pub fn format(self: *const CSSStyleDeclaration, writer: *std.Io.Writer) !void {
|
||||
|
||||
Reference in New Issue
Block a user