Batch option creation

This commit is contained in:
Karl Seguin committed 2026-09-24 07:10:43 +08:00
1 parent 3262e78aec
commit 4bada6d3df
3 files changed
+43 -5

No files matched your search

+8
View File
@@ -443,6 +443,14 @@ fn GetterQuery(comptime getter: anytype, comptime attrs: u32) type {
const params = @typeInfo(@TypeOf(getter)).@"fn".params;
const Self = params[0].type.?;
const Index = params[1].type.?;
// A getter that can return neither null nor error.NotHandled would report
// every index as present.
switch (@typeInfo(@typeInfo(@TypeOf(getter)).@"fn".return_type.?)) {
.optional, .error_union => {},
else => @compileError(@typeName(Self) ++ ": an indexed getter that can't return null or error.NotHandled needs an explicit query"),
}
return struct {
const query = if (params.len == 3) withGlobal else plain;
@@ -313,6 +313,25 @@
}
</script>
<script id="options_indexed_setter_padding">
{
const sel = document.createElement('select')
const make = (v) => { const o = document.createElement('option'); o.value = v; return o }
const records = []
new MutationObserver((r) => records.push(...r)).observe(sel, { childList: true })
// The spec pads with one DocumentFragment (we do, so 2 records); Chrome
// appends each blank option on its own. Only what was added is common.
sel.options[3] = make('d')
testing.expectEqual(4, sel.options.length)
testing.onload(() => {
const added = records.flatMap((r) => Array.from(r.addedNodes, (n) => n.value))
testing.expectEqual(['', '', '', 'd'], added)
})
}
</script>
<script id="select_add">
{
const sel = document.createElement('select')
@@ -16,6 +16,8 @@
// 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 lp = @import("lightpanda");
const js = @import("../../js/js.zig");
const Page = @import("../../Page.zig");
const Frame = @import("../../Frame.zig");
@@ -105,7 +107,8 @@ pub fn remove(self: *HTMLOptionsCollection, index: i32, frame: *Frame) void {
}
// Chrome's cap (kMaxListItems): padding up to a huge index would otherwise
// create that many options.
// create that many options. Past it, Chrome ignores the set (with a console
// warning); Firefox has no cap.
const max_list_items = 100_000;
// The indexed setter: null removes the option at index; an index past the
@@ -127,14 +130,22 @@ fn setAtIndex(self: *HTMLOptionsCollection, index: u32, option_: ?*Option, frame
}
if (index >= max_list_items) {
lp.log.warn(.js, "select overflow", .{ .max_list_items = max_list_items, .request = index });
return;
}
const select_node = self._select.asNode();
const doc = select_node.ownerDocument(frame).?;
for (self.length(frame)..index) |_| {
const blank = try doc.createElementNS("http://www.w3.org/1999/xhtml", "option", frame);
_ = try select_node.appendChild(blank.asNode(), frame);
const len = self.length(frame);
if (index > len) {
// Per spec, the padding goes in as one DocumentFragment, so observers
// get one record rather than one per blank option.
const doc = select_node.ownerDocument(frame).?;
const fragment = (try Node.DocumentFragment.init(doc, frame)).asNode();
for (len..index) |_| {
const blank = try doc.createElementNS("http://www.w3.org/1999/xhtml", "option", frame);
_ = try fragment.appendChild(blank.asNode(), frame);
}
_ = try select_node.appendChild(fragment, frame);
}
_ = try select_node.appendChild(option, frame);
}