webapi: SVG cleanup

This is a follow up to the large SVG change that has landed (1). Most changes
are mechanical or superficial (proper license headers, removing "pub" where not
needed, ...). But there were also a few finalizer/arena tweaks to tighten
lifetimes.

(1) https://github.com/lightpanda-io/browser/pull/3012
This commit is contained in:
Karl Seguin committed 2026-07-28 09:17:28 +08:00
1 parent 470c0ef782
commit 738ff60b57
13 files changed
+141 -55

No files matched your search

+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (C) 2023-2026 Lightpanda (Selecy SAS)
// Copyright (C) 2023-2026 Lightpanda (Selecy SAS)
//
// Francis Bouvier <francis@lightpanda.io>
// Pierre Tachoire <pierre@lightpanda.io>
+20 -1
View File
@@ -21,11 +21,14 @@ const lp = @import("lightpanda");
const js = @import("../../js/js.zig");
const Frame = @import("../../Frame.zig");
const Page = @import("../../Page.zig");
const Element = @import("../Element.zig");
const String = lp.String;
const Angle = @This();
_rc: lp.RC = .{},
_arena: std.mem.Allocator,
_value: f64 = 0,
_unit: Unit = .unspecified,
_element: ?*Element = null,
@@ -42,7 +45,23 @@ const Unit = enum(u16) {
};
pub fn detached(frame: *Frame) !*Angle {
return frame._factory.create(Angle{});
const arena = try frame._page.getArena(.tiny, "SVGAngle");
errdefer frame._page.releaseArena(arena);
const self = try arena.create(Angle);
self.* = .{ ._arena = arena };
return self;
}
pub fn deinit(self: *Angle, page: *Page) void {
page.releaseArena(self._arena);
}
pub fn acquireRef(self: *Angle) void {
self._rc.acquire();
}
pub fn releaseRef(self: *Angle, page: *Page) void {
self._rc.release(self, page);
}
pub fn getUnitType(self: *Angle) u16 {
@@ -138,7 +138,7 @@ pub fn getOrCreate(element: *Element, kind: Kind, frame: *Frame) !*AnimatedEnume
return gop.value_ptr.*;
}
pub fn create(
fn create(
element: *Element,
attr_name: lp.String,
entries: []const Entry,
+1 -10
View File
@@ -208,16 +208,7 @@ pub fn getOrCreate(element: *Element, kind: Kind, frame: *Frame) !*AnimatedLengt
return gop.value_ptr.*;
}
pub fn create(element: *Element, attr_name: lp.String, direction: Length.Direction, frame: *Frame) !*AnimatedLength {
const base_val = try Length.reflected(element, attr_name, direction, false, frame);
const anim_val = try Length.reflected(element, attr_name, direction, true, frame);
return frame._factory.create(AnimatedLength{
._base_val = base_val,
._anim_val = anim_val,
});
}
pub fn createConfigured(
fn createConfigured(
element: *Element,
attr_name: lp.String,
direction: Length.Direction,
@@ -39,7 +39,7 @@ pub fn getOrCreate(element: *Element, frame: *Frame) !*AnimatedPreserveAspectRat
return gop.value_ptr.*;
}
pub fn create(element: *Element, frame: *Frame) !*AnimatedPreserveAspectRatio {
fn create(element: *Element, frame: *Frame) !*AnimatedPreserveAspectRatio {
const base_val = try PreserveAspectRatio.create(element, false, frame);
const anim_val = try PreserveAspectRatio.create(element, true, frame);
return frame._factory.create(AnimatedPreserveAspectRatio{
@@ -1,9 +1,20 @@
// Copyright (C) 2023-2026 Lightpanda (Selecy SAS)
//
// Francis Bouvier <francis@lightpanda.io>
// Pierre Tachoire <pierre@lightpanda.io>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std");
const lp = @import("lightpanda");
@@ -50,10 +61,6 @@ pub fn getOrCreate(element: *Element, kind: Kind, frame: *Frame) !*AnimatedTrans
return gop.value_ptr.*;
}
pub fn create(element: *Element, frame: *Frame) !*AnimatedTransformList {
return createForAttribute(element, comptime .wrap("transform"), frame);
}
pub fn createForAttribute(element: *Element, attr_name: lp.String, frame: *Frame) !*AnimatedTransformList {
const base_val = try TransformList.createForAttribute(element, attr_name, false, frame);
const anim_val = try TransformList.createForAttribute(element, attr_name, true, frame);
+21 -8
View File
@@ -21,11 +21,17 @@ const lp = @import("lightpanda");
const js = @import("../../js/js.zig");
const Frame = @import("../../Frame.zig");
const Page = @import("../../Page.zig");
const Element = @import("../Element.zig");
const String = lp.String;
const Length = @This();
// A reflected length is created with one ref held by the frame's animated
// lookup cache, so a JS wrapper being collected can never free it. Only a
// detached length (own arena, zero initial refs) dies with its last wrapper.
_rc: lp.RC = .init(1),
_arena: ?std.mem.Allocator = null,
_value: f64 = 0,
_unit: Unit = .number,
_element: ?*Element = null,
@@ -58,16 +64,23 @@ pub const Unit = enum(u16) {
const MAX_ANCESTOR_DEPTH = 32;
pub fn detached(frame: *Frame) !*Length {
return frame._factory.create(Length{});
const arena = try frame._page.getArena(.tiny, "SVGLength");
errdefer frame._page.releaseArena(arena);
const self = try arena.create(Length);
self.* = .{ ._rc = .{}, ._arena = arena };
return self;
}
pub fn reflected(element: *Element, attr_name: String, direction: Direction, read_only: bool, frame: *Frame) !*Length {
return frame._factory.create(Length{
._element = element,
._attr_name = attr_name,
._direction = direction,
._read_only = read_only,
});
pub fn deinit(self: *Length, page: *Page) void {
page.releaseArena(self._arena.?);
}
pub fn acquireRef(self: *Length) void {
self._rc.acquire();
}
pub fn releaseRef(self: *Length, page: *Page) void {
self._rc.release(self, page);
}
pub fn reflectedConfigured(
+22 -1
View File
@@ -17,15 +17,36 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std");
const lp = @import("lightpanda");
const js = @import("../../js/js.zig");
const Frame = @import("../../Frame.zig");
const Page = @import("../../Page.zig");
const Number = @This();
_rc: lp.RC = .{},
_arena: std.mem.Allocator,
_value: f32 = 0,
pub fn detached(frame: *Frame) !*Number {
return frame._factory.create(Number{});
const arena = try frame._page.getArena(.tiny, "SVGNumber");
errdefer frame._page.releaseArena(arena);
const self = try arena.create(Number);
self.* = .{ ._arena = arena };
return self;
}
pub fn deinit(self: *Number, page: *Page) void {
page.releaseArena(self._arena);
}
pub fn acquireRef(self: *Number) void {
self._rc.acquire();
}
pub fn releaseRef(self: *Number, page: *Page) void {
self._rc.release(self, page);
}
pub fn getValue(self: *const Number) f32 {
+3 -2
View File
@@ -15,6 +15,7 @@
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std");
const Allocator = std.mem.Allocator;
@@ -138,12 +139,12 @@ pub const Path = struct {
try self.segments.append(allocator, .{ .line = .{ .start = start, .end = end } });
}
pub fn appendQuadratic(self: *Path, segment: Quadratic, allocator: Allocator) !void {
fn appendQuadratic(self: *Path, segment: Quadratic, allocator: Allocator) !void {
if (self.first_point == null) self.first_point = segment.start;
try self.segments.append(allocator, .{ .quadratic = segment });
}
pub fn appendCubic(self: *Path, segment: Cubic, allocator: Allocator) !void {
fn appendCubic(self: *Path, segment: Cubic, allocator: Allocator) !void {
if (self.first_point == null) self.first_point = segment.start;
try self.segments.append(allocator, .{ .cubic = segment });
}
+14 -3
View File
@@ -1,9 +1,20 @@
// Copyright (C) 2023-2026 Lightpanda (Selecy SAS)
//
// Francis Bouvier <francis@lightpanda.io>
// Pierre Tachoire <pierre@lightpanda.io>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std");
const lp = @import("lightpanda");
+15 -4
View File
@@ -1,9 +1,20 @@
// Copyright (C) 2023-2026 Lightpanda (Selecy SAS)
//
// Francis Bouvier <francis@lightpanda.io>
// Pierre Tachoire <pierre@lightpanda.io>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std");
const lp = @import("lightpanda");
@@ -14,7 +25,7 @@ const Element = @import("../Element.zig");
const StringList = @This();
pub const Delimiter = enum { whitespace, comma };
const Delimiter = enum { whitespace, comma };
_element: *Element,
_attribute_name: lp.String,
+14 -9
View File
@@ -66,9 +66,10 @@ pub const Attachment = struct {
mutate: *const fn (*anyopaque, *Transform, State) anyerror!void,
};
// The transform owns the matrix arena even when no JS wrapper currently
// references `matrix`. Forwarding the transform's bridge lifetime keeps the
// stable SameObject pointer valid across garbage collections.
// The transform lives in the matrix arena and owns it even when no JS wrapper
// currently references `matrix`. Forwarding the transform's bridge lifetime
// keeps the stable SameObject pointer valid across garbage collections, and
// releasing the last ref frees the transform together with its matrix.
pub fn acquireRef(self: *Transform) void {
self._matrix._proto.acquireRef();
}
@@ -80,7 +81,8 @@ pub fn releaseRef(self: *Transform, page: *Page) void {
pub fn detached(frame: *Frame) !*Transform {
const matrix = try DOMMatrix.create(RO.identity(), true, frame._page);
errdefer matrix._proto.deinit(frame._page);
const self = try frame._factory.create(Transform{ ._matrix = matrix });
const self = try matrix._proto._arena.create(Transform);
self.* = .{ ._matrix = matrix };
self.attachMatrix();
return self;
}
@@ -89,7 +91,8 @@ pub fn fromMatrix(init: ?DOMMatrix2DInit, frame: *Frame) !*Transform {
const parsed = try fixup2D(init orelse .{});
const matrix = try DOMMatrix.create(parsed.m, true, frame._page);
errdefer matrix._proto.deinit(frame._page);
const self = try frame._factory.create(Transform{ ._matrix = matrix });
const self = try matrix._proto._arena.create(Transform);
self.* = .{ ._matrix = matrix };
self.attachMatrix();
return self;
}
@@ -106,13 +109,14 @@ pub fn fromParsed(parsed: RO.ParsedTransform, frame: *Frame) !*Transform {
};
const matrix = try DOMMatrix.create(parsed.matrix, parsed.is_2d, frame._page);
errdefer matrix._proto.deinit(frame._page);
const self = try frame._factory.create(Transform{
const self = try matrix._proto._arena.create(Transform);
self.* = .{
._type = typ,
._angle = if (typ >= 4) parsed.values[0] else 0,
._cx = if (typ == 4 and parsed.count == 3) parsed.values[1] else 0,
._cy = if (typ == 4 and parsed.count == 3) parsed.values[2] else 0,
._matrix = matrix,
});
};
self.attachMatrix();
return self;
}
@@ -121,13 +125,14 @@ pub fn clone(self: *const Transform, frame: *Frame) !*Transform {
const current = self.getState();
const matrix = try DOMMatrix.create(current.matrix, current.is_2d, frame._page);
errdefer matrix._proto.deinit(frame._page);
const cloned = try frame._factory.create(Transform{
const cloned = try matrix._proto._arena.create(Transform);
cloned.* = .{
._type = current.typ,
._angle = current.angle,
._cx = current.cx,
._cy = current.cy,
._matrix = matrix,
});
};
cloned.attachMatrix();
return cloned;
}
+14 -7
View File
@@ -1,9 +1,20 @@
// Copyright (C) 2023-2026 Lightpanda (Selecy SAS)
//
// Francis Bouvier <francis@lightpanda.io>
// Pierre Tachoire <pierre@lightpanda.io>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std");
const lp = @import("lightpanda");
@@ -26,10 +37,6 @@ _snapshot: std.ArrayList(u8) = .empty,
_items: std.ArrayList(*Transform) = .empty,
_retired: std.ArrayList(*Transform) = .empty,
pub fn create(element: *Element, read_only: bool, frame: *Frame) !*TransformList {
return createForAttribute(element, comptime .wrap("transform"), read_only, frame);
}
pub fn createForAttribute(element: *Element, attr_name: lp.String, read_only: bool, frame: *Frame) !*TransformList {
return frame._factory.create(TransformList{
._frame = frame,