mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-03-22 20:54:43 +00:00
parse style attribute on CSSStyleDeclaration init
To reflect the current style attribute, CSSStyleDeclaration now parses it on init. Moreover, this PR synchronizes the element's style attribute with the dynamic changes.
This commit is contained in:
@@ -205,3 +205,54 @@
|
|||||||
testing.expectEqual('', style.getPropertyPriority('content'));
|
testing.expectEqual('', style.getPropertyPriority('content'));
|
||||||
}
|
}
|
||||||
</script>
|
</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,27 @@ _properties: std.DoublyLinkedList = .{},
|
|||||||
_is_computed: bool = false,
|
_is_computed: bool = false,
|
||||||
|
|
||||||
pub fn init(element: ?*Element, is_computed: bool, page: *Page) !*CSSStyleDeclaration {
|
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,
|
._element = element,
|
||||||
._is_computed = is_computed,
|
._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(String.wrap("style"))) |attr_value| {
|
||||||
|
var it = CssParser.parseDeclarationsList(attr_value);
|
||||||
|
while (it.next()) |declaration| {
|
||||||
|
const priority: ?[]const u8 = if (declaration.important) "important" else null;
|
||||||
|
try self.setPropertyImpl(declaration.name, declaration.value, priority, page);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn length(self: *const CSSStyleDeclaration) u32 {
|
pub fn length(self: *const CSSStyleDeclaration) u32 {
|
||||||
@@ -76,8 +93,13 @@ 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 {
|
pub fn setProperty(self: *CSSStyleDeclaration, property_name: []const u8, value: []const u8, priority_: ?[]const u8, page: *Page) !void {
|
||||||
|
try self.setPropertyImpl(property_name, value, priority_, page);
|
||||||
|
try self.syncStyleAttribute(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setPropertyImpl(self: *CSSStyleDeclaration, property_name: []const u8, value: []const u8, priority_: ?[]const u8, page: *Page) !void {
|
||||||
if (value.len == 0) {
|
if (value.len == 0) {
|
||||||
_ = try self.removeProperty(property_name, page);
|
_ = try self.removePropertyImpl(property_name, page);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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 {
|
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 normalized = normalizePropertyName(property_name, &page.buf);
|
||||||
const prop = self.findProperty(normalized) orelse return "";
|
const prop = self.findProperty(normalized) orelse return "";
|
||||||
|
|
||||||
@@ -121,6 +149,14 @@ pub fn removeProperty(self: *CSSStyleDeclaration, property_name: []const u8, pag
|
|||||||
return old_value;
|
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(String.wrap("style"), String.wrap(css_text), page);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn getFloat(self: *const CSSStyleDeclaration, page: *Page) []const u8 {
|
pub fn getFloat(self: *const CSSStyleDeclaration, page: *Page) []const u8 {
|
||||||
return self.getPropertyValue("float", page);
|
return self.getPropertyValue("float", page);
|
||||||
}
|
}
|
||||||
@@ -154,8 +190,9 @@ pub fn setCssText(self: *CSSStyleDeclaration, text: []const u8, page: *Page) !vo
|
|||||||
var it = CssParser.parseDeclarationsList(text);
|
var it = CssParser.parseDeclarationsList(text);
|
||||||
while (it.next()) |declaration| {
|
while (it.next()) |declaration| {
|
||||||
const priority: ?[]const u8 = if (declaration.important) "important" else null;
|
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, priority, page);
|
||||||
}
|
}
|
||||||
|
try self.syncStyleAttribute(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn format(self: *const CSSStyleDeclaration, writer: *std.Io.Writer) !void {
|
pub fn format(self: *const CSSStyleDeclaration, writer: *std.Io.Writer) !void {
|
||||||
|
|||||||
Reference in New Issue
Block a user