mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-06-11 01:25:53 -04:00
popAndInvoke captured the reactions queue as a slice at loop entry. If firing a reaction triggered a nested scope whose enqueue grew the underlying ArrayList, the captured slice became dangling and the next iteration read freed memory. Switch to indexed iteration so the queue pointer is re-read each step. Repros via wpt/custom-elements/ enqueue-custom-element-callback-reactions-inside-another-callback.html. The rest of the diff adds .ce_reactions = true to bridge declarations that were missing it per WebIDL [CEReactions] *and* whose Zig impl actually performs a DOM/attribute mutation (verified by reading each setter). Without the flag, the algorithm's enqueue path hits assertScopeActive and panics. Runtime-state-only setters (Input.value, Input.checked, Select.value, Option.selected, Media.muted/volume, etc.) are deliberately left untagged. Covered: - CustomElementRegistry.define, .upgrade - HTMLDocument: body, title, dir - Document: open, close - HTMLElement base: insertAdjacentHTML, dir, hidden, lang, tabIndex, title, innerText - Range: insertNode, deleteContents, extractContents, surroundContents, createContextualFragment - Selection: deleteFromDocument - HTMLOptionsCollection: add, remove - DOMStringMap (dataset): namedIndexed setter/deleter — required adding .ce_reactions to NamedIndexed.Opts in bridge.zig - ~28 HTMLxxxElement files: every settable attribute that resolves to setAttributeSafe/removeAttribute (Anchor, Button, Canvas, Data, Details, Dialog, FieldSet, Form, IFrame, Image, Input, Label, Link, LI, Media, Meta, OL, OptGroup, Option, Quote, Script, Select, Slot, Style, TableCell, Template, TextArea, Time, Track, Video)
63 lines
1.9 KiB
Zig
63 lines
1.9 KiB
Zig
const js = @import("../../../js/js.zig");
|
|
const Frame = @import("../../../Frame.zig");
|
|
|
|
const Node = @import("../../Node.zig");
|
|
const Element = @import("../../Element.zig");
|
|
const HtmlElement = @import("../Html.zig");
|
|
|
|
const Details = @This();
|
|
|
|
_proto: *HtmlElement,
|
|
|
|
pub fn asElement(self: *Details) *Element {
|
|
return self._proto._proto;
|
|
}
|
|
pub fn asConstElement(self: *const Details) *const Element {
|
|
return self._proto._proto;
|
|
}
|
|
pub fn asNode(self: *Details) *Node {
|
|
return self.asElement().asNode();
|
|
}
|
|
|
|
pub fn getOpen(self: *const Details) bool {
|
|
return self.asConstElement().getAttributeSafe(comptime .wrap("open")) != null;
|
|
}
|
|
|
|
pub fn setOpen(self: *Details, open: bool, frame: *Frame) !void {
|
|
if (open) {
|
|
try self.asElement().setAttributeSafe(comptime .wrap("open"), .wrap(""), frame);
|
|
} else {
|
|
try self.asElement().removeAttribute(comptime .wrap("open"), frame);
|
|
}
|
|
}
|
|
|
|
pub fn getName(self: *const Details) []const u8 {
|
|
return self.asConstElement().getAttributeSafe(comptime .wrap("name")) orelse "";
|
|
}
|
|
|
|
pub fn setName(self: *Details, value: []const u8, frame: *Frame) !void {
|
|
try self.asElement().setAttributeSafe(comptime .wrap("name"), .wrap(value), frame);
|
|
}
|
|
|
|
pub const JsApi = struct {
|
|
pub const bridge = js.Bridge(Details);
|
|
|
|
pub const Meta = struct {
|
|
pub const name = "HTMLDetailsElement";
|
|
pub const prototype_chain = bridge.prototypeChain();
|
|
pub var class_id: bridge.ClassId = undefined;
|
|
};
|
|
|
|
pub const open = bridge.accessor(Details.getOpen, Details.setOpen, .{ .ce_reactions = true });
|
|
pub const name = bridge.accessor(Details.getName, Details.setName, .{ .ce_reactions = true });
|
|
};
|
|
|
|
const testing = @import("../../../../testing.zig");
|
|
test "WebApi: HTML.Details" {
|
|
try testing.htmlRunner("element/html/details.html", .{});
|
|
}
|
|
|
|
test "WebApi: HTML.Summary click toggles parent details" {
|
|
try testing.htmlRunner("element/html/summary_click.html", .{});
|
|
}
|