Fix ce_reactions audit gaps and iterator-invalidation UAF

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)
This commit is contained in:
Karl Seguin
2026-05-12 15:19:50 +08:00
parent 6eeb97c220
commit 89814fd855
40 changed files with 178 additions and 146 deletions

View File

@@ -59,8 +59,12 @@ pub fn push(self: *Self) usize {
/// enqueued within a nested scope drain at that scope's pop, before this loop
/// sees them.
pub fn popAndInvoke(self: *Self, checkpoint: usize, frame: *Frame) void {
for (self.queue.items[checkpoint..]) |reaction| {
Custom.fireReaction(reaction, frame);
// Index, not slice: firing a reaction can recursively enqueue (via JS
// callbacks doing DOM mutations), which may realloc queue.items and
// invalidate any captured slice.
var i = checkpoint;
while (i < self.queue.items.len) : (i += 1) {
Custom.fireReaction(self.queue.items[i], frame);
}
self.queue.items.len = checkpoint;
self.active_scopes -= 1;

View File

@@ -308,6 +308,10 @@ pub const NamedIndexed = struct {
const Opts = struct {
as_typed_array: bool = false,
null_as_undefined: bool = false,
// Mirrors [CEReactions] on a named-property setter/deleter (e.g.,
// HTMLElement.dataset, which proxies setAttribute/removeAttribute).
// Only applies to setter and deleter; getters don't mutate.
ce_reactions: bool = false,
};
fn init(comptime T: type, comptime getter: anytype, setter: anytype, deleter: anytype, comptime opts: Opts) NamedIndexed {
@@ -336,6 +340,18 @@ pub const NamedIndexed = struct {
}
defer caller.deinit();
const ce_frame: ?*Frame = if (comptime opts.ce_reactions) switch (caller.local.ctx.global) {
.frame => |frame| frame,
.worker => null,
} else null;
var ce_checkpoint: usize = undefined;
if (comptime opts.ce_reactions) {
if (ce_frame) |frame| ce_checkpoint = frame._ce_reactions.push();
}
defer if (comptime opts.ce_reactions) {
if (ce_frame) |frame| frame._ce_reactions.popAndInvoke(ce_checkpoint, frame);
};
return caller.setNamedIndex(T, setter, c_name.?, c_value.?, handle.?, .{
.as_typed_array = opts.as_typed_array,
.null_as_undefined = opts.null_as_undefined,
@@ -352,6 +368,18 @@ pub const NamedIndexed = struct {
}
defer caller.deinit();
const ce_frame: ?*Frame = if (comptime opts.ce_reactions) switch (caller.local.ctx.global) {
.frame => |frame| frame,
.worker => null,
} else null;
var ce_checkpoint: usize = undefined;
if (comptime opts.ce_reactions) {
if (ce_frame) |frame| ce_checkpoint = frame._ce_reactions.push();
}
defer if (comptime opts.ce_reactions) {
if (ce_frame) |frame| frame._ce_reactions.popAndInvoke(ce_checkpoint, frame);
};
return caller.deleteNamedIndex(T, deleter, c_name.?, handle.?, .{
.as_typed_array = opts.as_typed_array,
.null_as_undefined = opts.null_as_undefined,

View File

@@ -260,9 +260,9 @@ pub const JsApi = struct {
pub const prototype_chain = bridge.prototypeChain();
};
pub const define = bridge.function(CustomElementRegistry.define, .{ .dom_exception = true });
pub const define = bridge.function(CustomElementRegistry.define, .{ .dom_exception = true, .ce_reactions = true });
pub const get = bridge.function(CustomElementRegistry.get, .{ .null_as_undefined = true });
pub const upgrade = bridge.function(CustomElementRegistry.upgrade, .{});
pub const upgrade = bridge.function(CustomElementRegistry.upgrade, .{ .ce_reactions = true });
pub const whenDefined = bridge.function(CustomElementRegistry.whenDefined, .{ .dom_exception = true });
};

View File

@@ -1171,8 +1171,8 @@ pub const JsApi = struct {
pub const elementsFromPoint = bridge.function(Document.elementsFromPoint, .{});
pub const write = bridge.function(Document.write, .{ .dom_exception = true, .ce_reactions = true });
pub const writeln = bridge.function(Document.writeln, .{ .dom_exception = true, .ce_reactions = true });
pub const open = bridge.function(Document.open, .{ .dom_exception = true });
pub const close = bridge.function(Document.close, .{ .dom_exception = true });
pub const open = bridge.function(Document.open, .{ .dom_exception = true, .ce_reactions = true });
pub const close = bridge.function(Document.close, .{ .dom_exception = true, .ce_reactions = true });
pub const doctype = bridge.accessor(Document.getDocType, null, .{});
pub const firstElementChild = bridge.accessor(Document.getFirstElementChild, null, .{});
pub const lastElementChild = bridge.accessor(Document.getLastElementChild, null, .{});

View File

@@ -288,11 +288,11 @@ pub const JsApi = struct {
});
}
pub const dir = bridge.accessor(HTMLDocument.getDir, HTMLDocument.setDir, .{});
pub const dir = bridge.accessor(HTMLDocument.getDir, HTMLDocument.setDir, .{ .ce_reactions = true });
pub const head = bridge.accessor(HTMLDocument.getHead, null, .{});
pub const body = bridge.accessor(HTMLDocument.getBody, HTMLDocument.setBody, .{ .dom_exception = true });
pub const body = bridge.accessor(HTMLDocument.getBody, HTMLDocument.setBody, .{ .dom_exception = true, .ce_reactions = true });
pub const lang = bridge.accessor(HTMLDocument.getLang, HTMLDocument.setLang, .{});
pub const title = bridge.accessor(HTMLDocument.getTitle, HTMLDocument.setTitle, .{});
pub const title = bridge.accessor(HTMLDocument.getTitle, HTMLDocument.setTitle, .{ .ce_reactions = true });
pub const images = bridge.accessor(HTMLDocument.getImages, null, .{});
pub const scripts = bridge.accessor(HTMLDocument.getScripts, null, .{});
pub const links = bridge.accessor(HTMLDocument.getLinks, null, .{});

View File

@@ -718,12 +718,12 @@ pub const JsApi = struct {
pub const isPointInRange = bridge.function(Range.isPointInRange, .{ .dom_exception = true });
pub const intersectsNode = bridge.function(Range.intersectsNode, .{});
pub const cloneRange = bridge.function(Range.cloneRange, .{ .dom_exception = true });
pub const insertNode = bridge.function(Range.insertNode, .{ .dom_exception = true });
pub const deleteContents = bridge.function(Range.deleteContents, .{ .dom_exception = true });
pub const insertNode = bridge.function(Range.insertNode, .{ .dom_exception = true, .ce_reactions = true });
pub const deleteContents = bridge.function(Range.deleteContents, .{ .dom_exception = true, .ce_reactions = true });
pub const cloneContents = bridge.function(Range.cloneContents, .{ .dom_exception = true });
pub const extractContents = bridge.function(Range.extractContents, .{ .dom_exception = true });
pub const surroundContents = bridge.function(Range.surroundContents, .{ .dom_exception = true });
pub const createContextualFragment = bridge.function(Range.createContextualFragment, .{ .dom_exception = true });
pub const extractContents = bridge.function(Range.extractContents, .{ .dom_exception = true, .ce_reactions = true });
pub const surroundContents = bridge.function(Range.surroundContents, .{ .dom_exception = true, .ce_reactions = true });
pub const createContextualFragment = bridge.function(Range.createContextualFragment, .{ .dom_exception = true, .ce_reactions = true });
pub const toString = bridge.function(Range.toString, .{ .dom_exception = true });
pub const getBoundingClientRect = bridge.function(Range.getBoundingClientRect, .{});
pub const getClientRects = bridge.function(Range.getClientRects, .{});

View File

@@ -734,7 +734,7 @@ pub const JsApi = struct {
pub const collapseToEnd = bridge.function(Selection.collapseToEnd, .{});
pub const collapseToStart = bridge.function(Selection.collapseToStart, .{ .dom_exception = true });
pub const containsNode = bridge.function(Selection.containsNode, .{});
pub const deleteFromDocument = bridge.function(Selection.deleteFromDocument, .{});
pub const deleteFromDocument = bridge.function(Selection.deleteFromDocument, .{ .ce_reactions = true });
pub const empty = bridge.function(Selection.removeAllRanges, .{});
pub const extend = bridge.function(Selection.extend, .{ .dom_exception = true });
// unimplemented: getComposedRanges

View File

@@ -106,6 +106,6 @@ pub const JsApi = struct {
pub const @"[str]" = bridge.namedIndexed(HTMLOptionsCollection.getByName, null, null, .{ .null_as_undefined = true });
pub const selectedIndex = bridge.accessor(HTMLOptionsCollection.getSelectedIndex, HTMLOptionsCollection.setSelectedIndex, .{});
pub const add = bridge.function(HTMLOptionsCollection.add, .{});
pub const remove = bridge.function(HTMLOptionsCollection.remove, .{});
pub const add = bridge.function(HTMLOptionsCollection.add, .{ .ce_reactions = true });
pub const remove = bridge.function(HTMLOptionsCollection.remove, .{ .ce_reactions = true });
};

View File

@@ -136,5 +136,5 @@ pub const JsApi = struct {
pub var class_id: bridge.ClassId = undefined;
};
pub const @"[]" = bridge.namedIndexed(getProperty, setProperty, deleteProperty, .{ .null_as_undefined = true });
pub const @"[]" = bridge.namedIndexed(getProperty, setProperty, deleteProperty, .{ .null_as_undefined = true, .ce_reactions = true });
};

View File

@@ -1234,21 +1234,21 @@ pub const JsApi = struct {
pub const constructor = bridge.constructor(HtmlElement.construct, .{ .new_target = true });
pub const innerText = bridge.accessor(_innerText, HtmlElement.setInnerText, .{});
pub const innerText = bridge.accessor(_innerText, HtmlElement.setInnerText, .{ .ce_reactions = true });
fn _innerText(self: *HtmlElement, frame: *const Frame) ![]const u8 {
var buf = std.Io.Writer.Allocating.init(frame.call_arena);
try self.getInnerText(&buf.writer);
return buf.written();
}
pub const insertAdjacentHTML = bridge.function(HtmlElement.insertAdjacentHTML, .{ .dom_exception = true });
pub const insertAdjacentHTML = bridge.function(HtmlElement.insertAdjacentHTML, .{ .dom_exception = true, .ce_reactions = true });
pub const click = bridge.function(HtmlElement.click, .{});
pub const dir = bridge.accessor(HtmlElement.getDir, HtmlElement.setDir, .{});
pub const hidden = bridge.accessor(HtmlElement.getHidden, HtmlElement.setHidden, .{});
pub const dir = bridge.accessor(HtmlElement.getDir, HtmlElement.setDir, .{ .ce_reactions = true });
pub const hidden = bridge.accessor(HtmlElement.getHidden, HtmlElement.setHidden, .{ .ce_reactions = true });
pub const isContentEditable = bridge.accessor(HtmlElement.getIsContentEditable, null, .{});
pub const lang = bridge.accessor(HtmlElement.getLang, HtmlElement.setLang, .{});
pub const tabIndex = bridge.accessor(HtmlElement.getTabIndex, HtmlElement.setTabIndex, .{});
pub const title = bridge.accessor(HtmlElement.getTitle, HtmlElement.setTitle, .{});
pub const lang = bridge.accessor(HtmlElement.getLang, HtmlElement.setLang, .{ .ce_reactions = true });
pub const tabIndex = bridge.accessor(HtmlElement.getTabIndex, HtmlElement.setTabIndex, .{ .ce_reactions = true });
pub const title = bridge.accessor(HtmlElement.getTitle, HtmlElement.setTitle, .{ .ce_reactions = true });
pub const onabort = bridge.accessor(HtmlElement.getOnAbort, HtmlElement.setOnAbort, .{});
pub const onanimationcancel = bridge.accessor(HtmlElement.getOnAnimationCancel, HtmlElement.setOnAnimationCancel, .{});

View File

@@ -214,20 +214,20 @@ pub const JsApi = struct {
pub var class_id: bridge.ClassId = undefined;
};
pub const href = bridge.accessor(Anchor.getHref, Anchor.setHref, .{});
pub const target = bridge.accessor(Anchor.getTarget, Anchor.setTarget, .{});
pub const name = bridge.accessor(Anchor.getName, Anchor.setName, .{});
pub const href = bridge.accessor(Anchor.getHref, Anchor.setHref, .{ .ce_reactions = true });
pub const target = bridge.accessor(Anchor.getTarget, Anchor.setTarget, .{ .ce_reactions = true });
pub const name = bridge.accessor(Anchor.getName, Anchor.setName, .{ .ce_reactions = true });
pub const origin = bridge.accessor(Anchor.getOrigin, null, .{});
pub const protocol = bridge.accessor(Anchor.getProtocol, Anchor.setProtocol, .{});
pub const host = bridge.accessor(Anchor.getHost, Anchor.setHost, .{});
pub const hostname = bridge.accessor(Anchor.getHostname, Anchor.setHostname, .{});
pub const port = bridge.accessor(Anchor.getPort, Anchor.setPort, .{});
pub const pathname = bridge.accessor(Anchor.getPathname, Anchor.setPathname, .{});
pub const search = bridge.accessor(Anchor.getSearch, Anchor.setSearch, .{});
pub const hash = bridge.accessor(Anchor.getHash, Anchor.setHash, .{});
pub const rel = bridge.accessor(Anchor.getRel, Anchor.setRel, .{});
pub const @"type" = bridge.accessor(Anchor.getType, Anchor.setType, .{});
pub const text = bridge.accessor(Anchor.getText, Anchor.setText, .{});
pub const protocol = bridge.accessor(Anchor.getProtocol, Anchor.setProtocol, .{ .ce_reactions = true });
pub const host = bridge.accessor(Anchor.getHost, Anchor.setHost, .{ .ce_reactions = true });
pub const hostname = bridge.accessor(Anchor.getHostname, Anchor.setHostname, .{ .ce_reactions = true });
pub const port = bridge.accessor(Anchor.getPort, Anchor.setPort, .{ .ce_reactions = true });
pub const pathname = bridge.accessor(Anchor.getPathname, Anchor.setPathname, .{ .ce_reactions = true });
pub const search = bridge.accessor(Anchor.getSearch, Anchor.setSearch, .{ .ce_reactions = true });
pub const hash = bridge.accessor(Anchor.getHash, Anchor.setHash, .{ .ce_reactions = true });
pub const rel = bridge.accessor(Anchor.getRel, Anchor.setRel, .{ .ce_reactions = true });
pub const @"type" = bridge.accessor(Anchor.getType, Anchor.setType, .{ .ce_reactions = true });
pub const text = bridge.accessor(Anchor.getText, Anchor.setText, .{ .ce_reactions = true });
pub const relList = bridge.accessor(_getRelList, null, .{ .null_as_undefined = true });
pub const toString = bridge.function(Anchor.getHref, .{});

View File

@@ -237,17 +237,17 @@ pub const JsApi = struct {
pub var class_id: bridge.ClassId = undefined;
};
pub const disabled = bridge.accessor(Button.getDisabled, Button.setDisabled, .{});
pub const name = bridge.accessor(Button.getName, Button.setName, .{});
pub const required = bridge.accessor(Button.getRequired, Button.setRequired, .{});
pub const disabled = bridge.accessor(Button.getDisabled, Button.setDisabled, .{ .ce_reactions = true });
pub const name = bridge.accessor(Button.getName, Button.setName, .{ .ce_reactions = true });
pub const required = bridge.accessor(Button.getRequired, Button.setRequired, .{ .ce_reactions = true });
pub const form = bridge.accessor(Button.getForm, null, .{});
pub const formAction = bridge.accessor(Button.getFormAction, Button.setFormAction, .{});
pub const formEnctype = bridge.accessor(Button.getFormEnctype, Button.setFormEnctype, .{});
pub const formMethod = bridge.accessor(Button.getFormMethod, Button.setFormMethod, .{});
pub const formNoValidate = bridge.accessor(Button.getFormNoValidate, Button.setFormNoValidate, .{});
pub const formTarget = bridge.accessor(Button.getFormTarget, Button.setFormTarget, .{});
pub const value = bridge.accessor(Button.getValue, Button.setValue, .{});
pub const @"type" = bridge.accessor(Button.getType, Button.setType, .{});
pub const formAction = bridge.accessor(Button.getFormAction, Button.setFormAction, .{.ce_reactions = true});
pub const formEnctype = bridge.accessor(Button.getFormEnctype, Button.setFormEnctype, .{.ce_reactions = true});
pub const formMethod = bridge.accessor(Button.getFormMethod, Button.setFormMethod, .{.ce_reactions = true});
pub const formNoValidate = bridge.accessor(Button.getFormNoValidate, Button.setFormNoValidate, .{.ce_reactions = true});
pub const formTarget = bridge.accessor(Button.getFormTarget, Button.setFormTarget, .{.ce_reactions = true});
pub const value = bridge.accessor(Button.getValue, Button.setValue, .{.ce_reactions = true});
pub const @"type" = bridge.accessor(Button.getType, Button.setType, .{.ce_reactions = true});
pub const labels = bridge.accessor(Button.getLabels, null, .{});
pub const willValidate = bridge.accessor(Button.getWillValidate, null, .{});
pub const validity = bridge.accessor(Button.getValidity, null, .{});

View File

@@ -120,8 +120,8 @@ pub const JsApi = struct {
pub var class_id: bridge.ClassId = undefined;
};
pub const width = bridge.accessor(Canvas.getWidth, Canvas.setWidth, .{});
pub const height = bridge.accessor(Canvas.getHeight, Canvas.setHeight, .{});
pub const width = bridge.accessor(Canvas.getWidth, Canvas.setWidth, .{ .ce_reactions = true });
pub const height = bridge.accessor(Canvas.getHeight, Canvas.setHeight, .{ .ce_reactions = true });
pub const getContext = bridge.function(Canvas.getContext, .{});
pub const transferControlToOffscreen = bridge.function(Canvas.transferControlToOffscreen, .{});
};

View File

@@ -52,5 +52,5 @@ pub const JsApi = struct {
pub var class_id: bridge.ClassId = undefined;
};
pub const value = bridge.accessor(Data.getValue, Data.setValue, .{});
pub const value = bridge.accessor(Data.getValue, Data.setValue, .{ .ce_reactions = true });
};

View File

@@ -48,8 +48,8 @@ pub const JsApi = struct {
pub var class_id: bridge.ClassId = undefined;
};
pub const open = bridge.accessor(Details.getOpen, Details.setOpen, .{});
pub const name = bridge.accessor(Details.getName, Details.setName, .{});
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");

View File

@@ -80,7 +80,7 @@ pub const JsApi = struct {
pub var class_id: bridge.ClassId = undefined;
};
pub const open = bridge.accessor(Dialog.getOpen, Dialog.setOpen, .{});
pub const open = bridge.accessor(Dialog.getOpen, Dialog.setOpen, .{ .ce_reactions = true });
pub const returnValue = bridge.accessor(Dialog.getReturnValue, Dialog.setReturnValue, .{});
pub const show = bridge.function(Dialog.show, .{});

View File

@@ -44,8 +44,8 @@ pub const JsApi = struct {
pub var class_id: bridge.ClassId = undefined;
};
pub const disabled = bridge.accessor(FieldSet.getDisabled, FieldSet.setDisabled, .{});
pub const name = bridge.accessor(FieldSet.getName, FieldSet.setName, .{});
pub const disabled = bridge.accessor(FieldSet.getDisabled, FieldSet.setDisabled, .{ .ce_reactions = true });
pub const name = bridge.accessor(FieldSet.getName, FieldSet.setName, .{ .ce_reactions = true });
};
const testing = @import("../../../../testing.zig");

View File

@@ -227,12 +227,12 @@ pub const JsApi = struct {
pub var class_id: bridge.ClassId = undefined;
};
pub const name = bridge.accessor(Form.getName, Form.setName, .{});
pub const method = bridge.accessor(Form.getMethod, Form.setMethod, .{});
pub const action = bridge.accessor(Form.getAction, Form.setAction, .{});
pub const target = bridge.accessor(Form.getTarget, Form.setTarget, .{});
pub const acceptCharset = bridge.accessor(Form.getAcceptCharset, Form.setAcceptCharset, .{});
pub const enctype = bridge.accessor(Form.getEnctype, Form.setEnctype, .{});
pub const name = bridge.accessor(Form.getName, Form.setName, .{ .ce_reactions = true });
pub const method = bridge.accessor(Form.getMethod, Form.setMethod, .{ .ce_reactions = true });
pub const action = bridge.accessor(Form.getAction, Form.setAction, .{ .ce_reactions = true });
pub const target = bridge.accessor(Form.getTarget, Form.setTarget, .{ .ce_reactions = true });
pub const acceptCharset = bridge.accessor(Form.getAcceptCharset, Form.setAcceptCharset, .{ .ce_reactions = true });
pub const enctype = bridge.accessor(Form.getEnctype, Form.setEnctype, .{ .ce_reactions = true });
pub const elements = bridge.accessor(Form.getElements, null, .{});
pub const length = bridge.accessor(Form.getLength, null, .{});
pub const submit = bridge.function(Form.submit, .{});

View File

@@ -81,8 +81,8 @@ pub const JsApi = struct {
pub var class_id: bridge.ClassId = undefined;
};
pub const src = bridge.accessor(IFrame.getSrc, IFrame.setSrc, .{});
pub const name = bridge.accessor(IFrame.getName, IFrame.setName, .{});
pub const src = bridge.accessor(IFrame.getSrc, IFrame.setSrc, .{ .ce_reactions = true });
pub const name = bridge.accessor(IFrame.getName, IFrame.setName, .{ .ce_reactions = true });
pub const contentWindow = bridge.accessor(IFrame.getContentWindow, null, .{});
pub const contentDocument = bridge.accessor(IFrame.getContentDocument, null, .{});
};

View File

@@ -142,13 +142,13 @@ pub const JsApi = struct {
};
pub const constructor = bridge.constructor(Image.constructor, .{});
pub const src = bridge.accessor(Image.getSrc, Image.setSrc, .{});
pub const src = bridge.accessor(Image.getSrc, Image.setSrc, .{ .ce_reactions = true });
pub const currentSrc = bridge.accessor(Image.getSrc, null, .{});
pub const alt = bridge.accessor(Image.getAlt, Image.setAlt, .{});
pub const width = bridge.accessor(Image.getWidth, Image.setWidth, .{});
pub const height = bridge.accessor(Image.getHeight, Image.setHeight, .{});
pub const crossOrigin = bridge.accessor(Image.getCrossOrigin, Image.setCrossOrigin, .{});
pub const loading = bridge.accessor(Image.getLoading, Image.setLoading, .{});
pub const alt = bridge.accessor(Image.getAlt, Image.setAlt, .{ .ce_reactions = true });
pub const width = bridge.accessor(Image.getWidth, Image.setWidth, .{ .ce_reactions = true });
pub const height = bridge.accessor(Image.getHeight, Image.setHeight, .{ .ce_reactions = true });
pub const crossOrigin = bridge.accessor(Image.getCrossOrigin, Image.setCrossOrigin, .{ .ce_reactions = true });
pub const loading = bridge.accessor(Image.getLoading, Image.setLoading, .{ .ce_reactions = true });
pub const naturalWidth = bridge.accessor(Image.getNaturalWidth, null, .{});
pub const naturalHeight = bridge.accessor(Image.getNaturalHeight, null, .{});
pub const complete = bridge.accessor(Image.getComplete, null, .{});

View File

@@ -1302,21 +1302,21 @@ pub const JsApi = struct {
}
pub const onselectionchange = bridge.accessor(Input.getOnSelectionChange, Input.setOnSelectionChange, .{});
pub const @"type" = bridge.accessor(Input.getType, Input.setType, .{});
pub const @"type" = bridge.accessor(Input.getType, Input.setType, .{ .ce_reactions = true });
pub const value = bridge.accessor(Input.getValue, setValueFromJS, .{ .dom_exception = true });
pub const defaultValue = bridge.accessor(Input.getDefaultValue, Input.setDefaultValue, .{});
pub const defaultValue = bridge.accessor(Input.getDefaultValue, Input.setDefaultValue, .{ .ce_reactions = true });
pub const checked = bridge.accessor(Input.getChecked, Input.setChecked, .{});
pub const defaultChecked = bridge.accessor(Input.getDefaultChecked, Input.setDefaultChecked, .{});
pub const disabled = bridge.accessor(Input.getDisabled, Input.setDisabled, .{});
pub const name = bridge.accessor(Input.getName, Input.setName, .{});
pub const required = bridge.accessor(Input.getRequired, Input.setRequired, .{});
pub const accept = bridge.accessor(Input.getAccept, Input.setAccept, .{});
pub const readOnly = bridge.accessor(Input.getReadonly, Input.setReadonly, .{});
pub const alt = bridge.accessor(Input.getAlt, Input.setAlt, .{});
pub const maxLength = bridge.accessor(Input.getMaxLength, Input.setMaxLength, .{ .dom_exception = true });
pub const minLength = bridge.accessor(Input.getMinLength, Input.setMinLength, .{ .dom_exception = true });
pub const size = bridge.accessor(Input.getSize, Input.setSize, .{});
pub const src = bridge.accessor(Input.getSrc, Input.setSrc, .{});
pub const defaultChecked = bridge.accessor(Input.getDefaultChecked, Input.setDefaultChecked, .{ .ce_reactions = true });
pub const disabled = bridge.accessor(Input.getDisabled, Input.setDisabled, .{ .ce_reactions = true });
pub const name = bridge.accessor(Input.getName, Input.setName, .{ .ce_reactions = true });
pub const required = bridge.accessor(Input.getRequired, Input.setRequired, .{ .ce_reactions = true });
pub const accept = bridge.accessor(Input.getAccept, Input.setAccept, .{ .ce_reactions = true });
pub const readOnly = bridge.accessor(Input.getReadonly, Input.setReadonly, .{ .ce_reactions = true });
pub const alt = bridge.accessor(Input.getAlt, Input.setAlt, .{ .ce_reactions = true });
pub const maxLength = bridge.accessor(Input.getMaxLength, Input.setMaxLength, .{ .dom_exception = true, .ce_reactions = true });
pub const minLength = bridge.accessor(Input.getMinLength, Input.setMinLength, .{ .dom_exception = true, .ce_reactions = true });
pub const size = bridge.accessor(Input.getSize, Input.setSize, .{ .ce_reactions = true });
pub const src = bridge.accessor(Input.getSrc, Input.setSrc, .{ .ce_reactions = true });
pub const form = bridge.accessor(Input.getForm, null, .{});
pub const formAction = bridge.accessor(Input.getFormAction, Input.setFormAction, .{});
pub const formEnctype = bridge.accessor(Input.getFormEnctype, Input.setFormEnctype, .{});
@@ -1325,13 +1325,13 @@ pub const JsApi = struct {
pub const formTarget = bridge.accessor(Input.getFormTarget, Input.setFormTarget, .{});
pub const labels = bridge.accessor(Input.getLabels, null, .{});
pub const indeterminate = bridge.accessor(Input.getIndeterminate, Input.setIndeterminate, .{});
pub const placeholder = bridge.accessor(Input.getPlaceholder, Input.setPlaceholder, .{});
pub const pattern = bridge.accessor(Input.getPattern, Input.setPattern, .{});
pub const min = bridge.accessor(Input.getMin, Input.setMin, .{});
pub const max = bridge.accessor(Input.getMax, Input.setMax, .{});
pub const step = bridge.accessor(Input.getStep, Input.setStep, .{});
pub const multiple = bridge.accessor(Input.getMultiple, Input.setMultiple, .{});
pub const autocomplete = bridge.accessor(Input.getAutocomplete, Input.setAutocomplete, .{});
pub const placeholder = bridge.accessor(Input.getPlaceholder, Input.setPlaceholder, .{ .ce_reactions = true });
pub const pattern = bridge.accessor(Input.getPattern, Input.setPattern, .{ .ce_reactions = true });
pub const min = bridge.accessor(Input.getMin, Input.setMin, .{ .ce_reactions = true });
pub const max = bridge.accessor(Input.getMax, Input.setMax, .{ .ce_reactions = true });
pub const step = bridge.accessor(Input.getStep, Input.setStep, .{ .ce_reactions = true });
pub const multiple = bridge.accessor(Input.getMultiple, Input.setMultiple, .{ .ce_reactions = true });
pub const autocomplete = bridge.accessor(Input.getAutocomplete, Input.setAutocomplete, .{ .ce_reactions = true });
pub const willValidate = bridge.accessor(Input.getWillValidate, null, .{});
pub const validity = bridge.accessor(Input.getValidity, null, .{});
pub const validationMessage = bridge.accessor(Input.getValidationMessage, null, .{});

View File

@@ -52,7 +52,7 @@ pub const JsApi = struct {
pub var class_id: bridge.ClassId = undefined;
};
pub const value = bridge.accessor(LI.getValue, LI.setValue, .{});
pub const value = bridge.accessor(LI.getValue, LI.setValue, .{ .ce_reactions = true });
};
const testing = @import("../../../../testing.zig");

View File

@@ -138,7 +138,7 @@ pub const JsApi = struct {
pub var class_id: bridge.ClassId = undefined;
};
pub const htmlFor = bridge.accessor(Label.getHtmlFor, Label.setHtmlFor, .{});
pub const htmlFor = bridge.accessor(Label.getHtmlFor, Label.setHtmlFor, .{ .ce_reactions = true });
pub const control = bridge.accessor(Label.getControl, null, .{});
};

View File

@@ -140,11 +140,11 @@ pub const JsApi = struct {
pub var class_id: bridge.ClassId = undefined;
};
pub const as = bridge.accessor(Link.getAs, Link.setAs, .{});
pub const rel = bridge.accessor(Link.getRel, Link.setRel, .{});
pub const media = bridge.accessor(Link.getMedia, Link.setMedia, .{});
pub const href = bridge.accessor(Link.getHref, Link.setHref, .{});
pub const crossOrigin = bridge.accessor(Link.getCrossOrigin, Link.setCrossOrigin, .{});
pub const as = bridge.accessor(Link.getAs, Link.setAs, .{ .ce_reactions = true });
pub const rel = bridge.accessor(Link.getRel, Link.setRel, .{ .ce_reactions = true });
pub const media = bridge.accessor(Link.getMedia, Link.setMedia, .{ .ce_reactions = true });
pub const href = bridge.accessor(Link.getHref, Link.setHref, .{ .ce_reactions = true });
pub const crossOrigin = bridge.accessor(Link.getCrossOrigin, Link.setCrossOrigin, .{ .ce_reactions = true });
pub const relList = bridge.accessor(_getRelList, null, .{ .null_as_undefined = true });
fn _getRelList(self: *Link, frame: *Frame) !?*@import("../../collections.zig").DOMTokenList {

View File

@@ -307,13 +307,13 @@ pub const JsApi = struct {
pub const HAVE_FUTURE_DATA = bridge.property(@intFromEnum(ReadyState.HAVE_FUTURE_DATA), .{ .template = true });
pub const HAVE_ENOUGH_DATA = bridge.property(@intFromEnum(ReadyState.HAVE_ENOUGH_DATA), .{ .template = true });
pub const src = bridge.accessor(Media.getSrc, Media.setSrc, .{});
pub const src = bridge.accessor(Media.getSrc, Media.setSrc, .{ .ce_reactions = true });
pub const currentSrc = bridge.accessor(Media.getSrc, null, .{});
pub const autoplay = bridge.accessor(Media.getAutoplay, Media.setAutoplay, .{});
pub const controls = bridge.accessor(Media.getControls, Media.setControls, .{});
pub const loop = bridge.accessor(Media.getLoop, Media.setLoop, .{});
pub const autoplay = bridge.accessor(Media.getAutoplay, Media.setAutoplay, .{ .ce_reactions = true });
pub const controls = bridge.accessor(Media.getControls, Media.setControls, .{ .ce_reactions = true });
pub const loop = bridge.accessor(Media.getLoop, Media.setLoop, .{ .ce_reactions = true });
pub const muted = bridge.accessor(Media.getMuted, Media.setMuted, .{});
pub const preload = bridge.accessor(Media.getPreload, Media.setPreload, .{});
pub const preload = bridge.accessor(Media.getPreload, Media.setPreload, .{ .ce_reactions = true });
pub const volume = bridge.accessor(Media.getVolume, Media.setVolume, .{});
pub const playbackRate = bridge.accessor(Media.getPlaybackRate, Media.setPlaybackRate, .{});
pub const currentTime = bridge.accessor(Media.getCurrentTime, Media.setCurrentTime, .{});

View File

@@ -77,8 +77,8 @@ pub const JsApi = struct {
pub var class_id: bridge.ClassId = undefined;
};
pub const name = bridge.accessor(MetaElement.getName, MetaElement.setName, .{});
pub const httpEquiv = bridge.accessor(MetaElement.getHttpEquiv, MetaElement.setHttpEquiv, .{});
pub const content = bridge.accessor(MetaElement.getContent, MetaElement.setContent, .{});
pub const media = bridge.accessor(MetaElement.getMedia, MetaElement.setMedia, .{});
pub const name = bridge.accessor(MetaElement.getName, MetaElement.setName, .{ .ce_reactions = true });
pub const httpEquiv = bridge.accessor(MetaElement.getHttpEquiv, MetaElement.setHttpEquiv, .{ .ce_reactions = true });
pub const content = bridge.accessor(MetaElement.getContent, MetaElement.setContent, .{ .ce_reactions = true });
pub const media = bridge.accessor(MetaElement.getMedia, MetaElement.setMedia, .{ .ce_reactions = true });
};

View File

@@ -72,9 +72,9 @@ pub const JsApi = struct {
pub var class_id: bridge.ClassId = undefined;
};
pub const start = bridge.accessor(OL.getStart, OL.setStart, .{});
pub const reversed = bridge.accessor(OL.getReversed, OL.setReversed, .{});
pub const @"type" = bridge.accessor(OL.getType, OL.setType, .{});
pub const start = bridge.accessor(OL.getStart, OL.setStart, .{ .ce_reactions = true });
pub const reversed = bridge.accessor(OL.getReversed, OL.setReversed, .{ .ce_reactions = true });
pub const @"type" = bridge.accessor(OL.getType, OL.setType, .{ .ce_reactions = true });
};
const testing = @import("../../../../testing.zig");

View File

@@ -44,8 +44,8 @@ pub const JsApi = struct {
pub var class_id: bridge.ClassId = undefined;
};
pub const disabled = bridge.accessor(OptGroup.getDisabled, OptGroup.setDisabled, .{});
pub const label = bridge.accessor(OptGroup.getLabel, OptGroup.setLabel, .{});
pub const disabled = bridge.accessor(OptGroup.getDisabled, OptGroup.setDisabled, .{ .ce_reactions = true });
pub const label = bridge.accessor(OptGroup.getLabel, OptGroup.setLabel, .{ .ce_reactions = true });
};
const testing = @import("../../../../testing.zig");

View File

@@ -117,12 +117,12 @@ pub const JsApi = struct {
pub var class_id: bridge.ClassId = undefined;
};
pub const value = bridge.accessor(Option.getValue, Option.setValue, .{});
pub const text = bridge.accessor(Option.getText, Option.setText, .{});
pub const value = bridge.accessor(Option.getValue, Option.setValue, .{ .ce_reactions = true });
pub const text = bridge.accessor(Option.getText, Option.setText, .{ .ce_reactions = true });
pub const selected = bridge.accessor(Option.getSelected, Option.setSelected, .{});
pub const defaultSelected = bridge.accessor(Option.getDefaultSelected, null, .{});
pub const disabled = bridge.accessor(Option.getDisabled, Option.setDisabled, .{});
pub const name = bridge.accessor(Option.getName, Option.setName, .{});
pub const disabled = bridge.accessor(Option.getDisabled, Option.setDisabled, .{ .ce_reactions = true });
pub const name = bridge.accessor(Option.getName, Option.setName, .{ .ce_reactions = true });
};
pub const Build = struct {

View File

@@ -41,7 +41,7 @@ pub const JsApi = struct {
pub var class_id: bridge.ClassId = undefined;
};
pub const cite = bridge.accessor(Quote.getCite, Quote.setCite, .{});
pub const cite = bridge.accessor(Quote.getCite, Quote.setCite, .{ .ce_reactions = true });
};
const testing = @import("../../../../testing.zig");

View File

@@ -124,20 +124,20 @@ pub const JsApi = struct {
pub var class_id: bridge.ClassId = undefined;
};
pub const src = bridge.accessor(Script.getSrc, Script.setSrc, .{});
pub const @"defer" = bridge.accessor(Script.getDefer, Script.setDefer, .{});
pub const async = bridge.accessor(Script.getAsync, Script.setAsync, .{});
pub const @"type" = bridge.accessor(Script.getType, Script.setType, .{});
pub const nonce = bridge.accessor(Script.getNonce, Script.setNonce, .{});
pub const charset = bridge.accessor(Script.getCharset, Script.setCharset, .{});
pub const src = bridge.accessor(Script.getSrc, Script.setSrc, .{ .ce_reactions = true });
pub const @"defer" = bridge.accessor(Script.getDefer, Script.setDefer, .{ .ce_reactions = true });
pub const async = bridge.accessor(Script.getAsync, Script.setAsync, .{ .ce_reactions = true });
pub const @"type" = bridge.accessor(Script.getType, Script.setType, .{ .ce_reactions = true });
pub const nonce = bridge.accessor(Script.getNonce, Script.setNonce, .{ .ce_reactions = true });
pub const charset = bridge.accessor(Script.getCharset, Script.setCharset, .{ .ce_reactions = true });
pub const noModule = bridge.accessor(Script.getNoModule, null, .{});
pub const innerText = bridge.accessor(_innerText, Script.setInnerText, .{});
pub const innerText = bridge.accessor(_innerText, Script.setInnerText, .{ .ce_reactions = true });
fn _innerText(self: *Script, frame: *const Frame) ![]const u8 {
var buf = std.Io.Writer.Allocating.init(frame.call_arena);
try self.asNode().getTextContent(&buf.writer);
return buf.written();
}
pub const text = bridge.accessor(_text, Script.setInnerText, .{});
pub const text = bridge.accessor(_text, Script.setInnerText, .{ .ce_reactions = true });
fn _text(self: *Script, frame: *const Frame) ![]const u8 {
var buf = std.Io.Writer.Allocating.init(frame.call_arena);
try self.asNode().getChildTextContent(&buf.writer);

View File

@@ -319,14 +319,14 @@ pub const JsApi = struct {
pub const value = bridge.accessor(Select.getValue, Select.setValue, .{});
pub const selectedIndex = bridge.accessor(Select.getSelectedIndex, Select.setSelectedIndex, .{});
pub const multiple = bridge.accessor(Select.getMultiple, Select.setMultiple, .{});
pub const disabled = bridge.accessor(Select.getDisabled, Select.setDisabled, .{});
pub const name = bridge.accessor(Select.getName, Select.setName, .{});
pub const required = bridge.accessor(Select.getRequired, Select.setRequired, .{});
pub const multiple = bridge.accessor(Select.getMultiple, Select.setMultiple, .{ .ce_reactions = true });
pub const disabled = bridge.accessor(Select.getDisabled, Select.setDisabled, .{ .ce_reactions = true });
pub const name = bridge.accessor(Select.getName, Select.setName, .{ .ce_reactions = true });
pub const required = bridge.accessor(Select.getRequired, Select.setRequired, .{ .ce_reactions = true });
pub const options = bridge.accessor(Select.getOptions, null, .{});
pub const selectedOptions = bridge.accessor(Select.getSelectedOptions, null, .{});
pub const form = bridge.accessor(Select.getForm, null, .{});
pub const size = bridge.accessor(Select.getSize, Select.setSize, .{});
pub const size = bridge.accessor(Select.getSize, Select.setSize, .{ .ce_reactions = true });
pub const length = bridge.accessor(Select.getLength, null, .{});
pub const labels = bridge.accessor(Select.getLabels, null, .{});
pub const willValidate = bridge.accessor(Select.getWillValidate, null, .{});

View File

@@ -155,7 +155,7 @@ pub const JsApi = struct {
pub var class_id: bridge.ClassId = undefined;
};
pub const name = bridge.accessor(Slot.getName, Slot.setName, .{});
pub const name = bridge.accessor(Slot.getName, Slot.setName, .{ .ce_reactions = true });
pub const assignedNodes = bridge.function(Slot.assignedNodes, .{});
pub const assignedElements = bridge.function(Slot.assignedElements, .{});
pub const assign = bridge.function(Slot.assign, .{});

View File

@@ -125,10 +125,10 @@ pub const JsApi = struct {
pub var class_id: bridge.ClassId = undefined;
};
pub const blocking = bridge.accessor(Style.getBlocking, Style.setBlocking, .{});
pub const media = bridge.accessor(Style.getMedia, Style.setMedia, .{});
pub const @"type" = bridge.accessor(Style.getType, Style.setType, .{});
pub const disabled = bridge.accessor(Style.getDisabled, Style.setDisabled, .{});
pub const blocking = bridge.accessor(Style.getBlocking, Style.setBlocking, .{ .ce_reactions = true });
pub const media = bridge.accessor(Style.getMedia, Style.setMedia, .{ .ce_reactions = true });
pub const @"type" = bridge.accessor(Style.getType, Style.setType, .{ .ce_reactions = true });
pub const disabled = bridge.accessor(Style.getDisabled, Style.setDisabled, .{ .ce_reactions = true });
pub const sheet = bridge.accessor(Style.getSheet, null, .{});
};

View File

@@ -54,8 +54,8 @@ pub const JsApi = struct {
pub var class_id: bridge.ClassId = undefined;
};
pub const colSpan = bridge.accessor(TableCell.getColSpan, TableCell.setColSpan, .{});
pub const rowSpan = bridge.accessor(TableCell.getRowSpan, TableCell.setRowSpan, .{});
pub const colSpan = bridge.accessor(TableCell.getColSpan, TableCell.setColSpan, .{ .ce_reactions = true });
pub const rowSpan = bridge.accessor(TableCell.getRowSpan, TableCell.setRowSpan, .{ .ce_reactions = true });
};
const testing = @import("../../../../testing.zig");

View File

@@ -48,7 +48,7 @@ pub const JsApi = struct {
};
pub const content = bridge.accessor(Template.getContent, null, .{});
pub const innerHTML = bridge.accessor(_getInnerHTML, Template.setInnerHTML, .{});
pub const innerHTML = bridge.accessor(_getInnerHTML, Template.setInnerHTML, .{ .ce_reactions = true });
pub const outerHTML = bridge.accessor(_getOuterHTML, null, .{});
fn _getInnerHTML(self: *Template, frame: *Frame) ![]const u8 {

View File

@@ -405,12 +405,12 @@ pub const JsApi = struct {
pub const setCustomValidity = bridge.function(TextArea.setCustomValidity, .{});
pub const onselectionchange = bridge.accessor(TextArea.getOnSelectionChange, TextArea.setOnSelectionChange, .{});
pub const value = bridge.accessor(TextArea.getValue, TextArea.setValue, .{});
pub const defaultValue = bridge.accessor(TextArea.getDefaultValue, TextArea.setDefaultValue, .{});
pub const disabled = bridge.accessor(TextArea.getDisabled, TextArea.setDisabled, .{});
pub const name = bridge.accessor(TextArea.getName, TextArea.setName, .{});
pub const required = bridge.accessor(TextArea.getRequired, TextArea.setRequired, .{});
pub const maxLength = bridge.accessor(TextArea.getMaxLength, TextArea.setMaxLength, .{ .dom_exception = true });
pub const minLength = bridge.accessor(TextArea.getMinLength, TextArea.setMinLength, .{ .dom_exception = true });
pub const defaultValue = bridge.accessor(TextArea.getDefaultValue, TextArea.setDefaultValue, .{ .ce_reactions = true });
pub const disabled = bridge.accessor(TextArea.getDisabled, TextArea.setDisabled, .{ .ce_reactions = true });
pub const name = bridge.accessor(TextArea.getName, TextArea.setName, .{ .ce_reactions = true });
pub const required = bridge.accessor(TextArea.getRequired, TextArea.setRequired, .{ .ce_reactions = true });
pub const maxLength = bridge.accessor(TextArea.getMaxLength, TextArea.setMaxLength, .{ .dom_exception = true, .ce_reactions = true });
pub const minLength = bridge.accessor(TextArea.getMinLength, TextArea.setMinLength, .{ .dom_exception = true, .ce_reactions = true });
pub const form = bridge.accessor(TextArea.getForm, null, .{});
pub const select = bridge.function(TextArea.select, .{});

View File

@@ -32,7 +32,7 @@ pub const JsApi = struct {
pub var class_id: bridge.ClassId = undefined;
};
pub const dateTime = bridge.accessor(Time.getDateTime, Time.setDateTime, .{});
pub const dateTime = bridge.accessor(Time.getDateTime, Time.setDateTime, .{ .ce_reactions = true });
};
const testing = @import("../../../../testing.zig");

View File

@@ -83,7 +83,7 @@ pub const JsApi = struct {
pub var class_id: bridge.ClassId = undefined;
};
pub const kind = bridge.accessor(Track.getKind, Track.setKind, .{});
pub const kind = bridge.accessor(Track.getKind, Track.setKind, .{ .ce_reactions = true });
pub const NONE = bridge.property(@as(u16, @intFromEnum(ReadyState.none)), .{ .template = true });
pub const LOADING = bridge.property(@as(u16, @intFromEnum(ReadyState.loading)), .{ .template = true });

View File

@@ -73,7 +73,7 @@ pub const JsApi = struct {
pub var class_id: bridge.ClassId = undefined;
};
pub const poster = bridge.accessor(Video.getPoster, Video.setPoster, .{});
pub const poster = bridge.accessor(Video.getPoster, Video.setPoster, .{ .ce_reactions = true });
pub const videoWidth = bridge.accessor(Video.getVideoWidth, null, .{});
pub const videoHeight = bridge.accessor(Video.getVideoHeight, null, .{});
};