mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-02-04 14:33:47 +00:00
Merge remote-tracking branch 'origin/main' into wp/mrdimidium/multicontext
This commit is contained in:
4
.github/actions/install/action.yml
vendored
4
.github/actions/install/action.yml
vendored
@@ -13,7 +13,7 @@ inputs:
|
||||
zig-v8:
|
||||
description: 'zig v8 version to install'
|
||||
required: false
|
||||
default: 'v0.2.5'
|
||||
default: 'v0.2.6'
|
||||
v8:
|
||||
description: 'v8 version to install'
|
||||
required: false
|
||||
@@ -32,7 +32,7 @@ runs:
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y wget xz-utils python3 ca-certificates git pkg-config libglib2.0-dev gperf libexpat1-dev cmake clang
|
||||
sudo apt-get install -y wget xz-utils ca-certificates gcc make git
|
||||
|
||||
# Zig version used from the `minimum_zig_version` field in build.zig.zon
|
||||
- uses: mlugg/setup-zig@v2
|
||||
|
||||
@@ -3,12 +3,12 @@ FROM debian:stable-slim
|
||||
ARG MINISIG=0.12
|
||||
ARG ZIG_MINISIG=RWSGOq2NVecA2UPNdBUZykf1CCb147pkmdtYxgb3Ti+JO/wCYvhbAb/U
|
||||
ARG V8=14.0.365.4
|
||||
ARG ZIG_V8=v0.2.5
|
||||
ARG ZIG_V8=v0.2.6
|
||||
ARG TARGETPLATFORM
|
||||
|
||||
RUN apt-get update -yq && \
|
||||
apt-get install -yq xz-utils ca-certificates \
|
||||
clang make curl git
|
||||
gcc make curl git
|
||||
|
||||
# Get Rust
|
||||
RUN curl https://sh.rustup.rs -sSf | sh -s -- --profile minimal -y
|
||||
|
||||
@@ -178,7 +178,7 @@ For **Debian/Ubuntu based Linux**:
|
||||
|
||||
```
|
||||
sudo apt install xz-utils ca-certificates \
|
||||
clang make curl git
|
||||
gcc make curl git
|
||||
```
|
||||
You also need to [install Rust](https://rust-lang.org/tools/install/).
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
.minimum_zig_version = "0.15.2",
|
||||
.dependencies = .{
|
||||
.v8 = .{
|
||||
.url = "https://github.com/lightpanda-io/zig-v8-fork/archive/v0.2.5.tar.gz",
|
||||
.hash = "v8-0.0.0-xddH641NBAC3MqKV44YCkwvnUenhQyGlgJI8OScx0tlP",
|
||||
.url = "https://github.com/lightpanda-io/zig-v8-fork/archive/v0.2.6.tar.gz",
|
||||
.hash = "v8-0.0.0-xddH60NRBAAWmpZq9nWdfFAEqVJ9zqJnvr1Nl9m2AbcY",
|
||||
},
|
||||
//.v8 = .{ .path = "../zig-v8-fork" },
|
||||
.@"boringssl-zig" = .{
|
||||
|
||||
@@ -99,7 +99,7 @@ pub fn closeSession(self: *Browser) void {
|
||||
session.deinit();
|
||||
self.session = null;
|
||||
_ = self.session_arena.reset(.{ .retain_with_limit = 1 * 1024 * 1024 });
|
||||
self.env.lowMemoryNotification();
|
||||
self.env.memoryPressureNotification(.critical);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -103,6 +103,20 @@ _element_shadow_roots: Element.ShadowRootLookup = .{},
|
||||
_node_owner_documents: Node.OwnerDocumentLookup = .{},
|
||||
_element_assigned_slots: Element.AssignedSlotLookup = .{},
|
||||
|
||||
/// Lazily-created inline event listeners (or listeners provided as attributes).
|
||||
/// Avoids bloating all elements with extra function fields for rare usage.
|
||||
///
|
||||
/// Use this when a listener provided like these:
|
||||
///
|
||||
/// ```html
|
||||
/// <img onload="(() => { ... })()" />
|
||||
/// ```
|
||||
///
|
||||
/// ```js
|
||||
/// img.onload = () => { ... };
|
||||
/// ```
|
||||
_element_attr_listeners: Element.AttrListenerLookup = .{},
|
||||
|
||||
_script_manager: ScriptManager,
|
||||
|
||||
// List of active MutationObservers
|
||||
@@ -175,7 +189,10 @@ call_arena: Allocator,
|
||||
arena_pool: *ArenaPool,
|
||||
// In Debug, we use this to see if anything fails to release an arena back to
|
||||
// the pool.
|
||||
_arena_pool_leak_track: (if (IS_DEBUG) std.AutoHashMapUnmanaged(usize, []const u8) else void),
|
||||
_arena_pool_leak_track: (if (IS_DEBUG) std.AutoHashMapUnmanaged(usize, struct {
|
||||
owner: []const u8,
|
||||
count: usize,
|
||||
}) else void),
|
||||
|
||||
window: *Window,
|
||||
document: *Document,
|
||||
@@ -237,7 +254,9 @@ pub fn deinit(self: *Page) void {
|
||||
if (comptime IS_DEBUG) {
|
||||
var it = self._arena_pool_leak_track.valueIterator();
|
||||
while (it.next()) |value_ptr| {
|
||||
log.err(.bug, "ArenaPool Leak", .{ .owner = value_ptr.* });
|
||||
if (value_ptr.count > 0) {
|
||||
log.err(.bug, "ArenaPool Leak", .{ .owner = value_ptr.owner });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,6 +264,11 @@ pub fn deinit(self: *Page) void {
|
||||
}
|
||||
|
||||
fn reset(self: *Page, comptime initializing: bool) !void {
|
||||
if (comptime initializing == false) {
|
||||
self._session.executor.removeContext();
|
||||
|
||||
// removing a context can trigger finalizers, so we can only check for
|
||||
// a leak after the above.
|
||||
if (comptime IS_DEBUG) {
|
||||
var it = self._arena_pool_leak_track.valueIterator();
|
||||
while (it.next()) |value_ptr| {
|
||||
@@ -253,20 +277,10 @@ fn reset(self: *Page, comptime initializing: bool) !void {
|
||||
self._arena_pool_leak_track.clearRetainingCapacity();
|
||||
}
|
||||
|
||||
if (comptime initializing == false) {
|
||||
// Removins the context triggers the linked inspector.
|
||||
// It seems to append a collect task to the message loop.
|
||||
self._session.executor.removeContext();
|
||||
|
||||
// We force running the message loop after removing the context b/c we
|
||||
// will force a GC run just after. If we remove this part, the task
|
||||
// will run after the GC and we will use memory after free.
|
||||
self._session.browser.runMessageLoop();
|
||||
|
||||
// We force a garbage collection with lowMemoryNotification between
|
||||
// page navigations to keep v8 memory usage as low as possible.
|
||||
// Calling immediately after a runMessageLoop ensure
|
||||
self._session.browser.env.lowMemoryNotification();
|
||||
// We force a garbage collection between page navigations to keep v8
|
||||
// memory usage as low as possible.
|
||||
self._session.browser.env.memoryPressureNotification(.moderate);
|
||||
|
||||
self._script_manager.shutdown = true;
|
||||
self._session.browser.http_client.abort();
|
||||
@@ -317,6 +331,9 @@ fn reset(self: *Page, comptime initializing: bool) !void {
|
||||
self._element_shadow_roots = .{};
|
||||
self._node_owner_documents = .{};
|
||||
self._element_assigned_slots = .{};
|
||||
|
||||
self._element_attr_listeners = .{};
|
||||
|
||||
self._notified_network_idle = .init;
|
||||
self._notified_network_almost_idle = .init;
|
||||
|
||||
@@ -379,14 +396,23 @@ const GetArenaOpts = struct {
|
||||
pub fn getArena(self: *Page, comptime opts: GetArenaOpts) !Allocator {
|
||||
const allocator = try self.arena_pool.acquire();
|
||||
if (comptime IS_DEBUG) {
|
||||
try self._arena_pool_leak_track.put(self.arena, @intFromPtr(allocator.ptr), opts.debug);
|
||||
const gop = try self._arena_pool_leak_track.getOrPut(self.arena, @intFromPtr(allocator.ptr));
|
||||
if (gop.found_existing) {
|
||||
std.debug.assert(gop.value_ptr.count == 0);
|
||||
}
|
||||
gop.value_ptr.* = .{ .owner = opts.debug, .count = 1 };
|
||||
}
|
||||
return allocator;
|
||||
}
|
||||
|
||||
pub fn releaseArena(self: *Page, allocator: Allocator) void {
|
||||
if (comptime IS_DEBUG) {
|
||||
_ = self._arena_pool_leak_track.remove(@intFromPtr(allocator.ptr));
|
||||
const found = self._arena_pool_leak_track.getPtr(@intFromPtr(allocator.ptr)).?;
|
||||
if (found.count != 1) {
|
||||
log.err(.bug, "ArenaPool Double Free", .{ .owner = found.owner, .count = found.count });
|
||||
return;
|
||||
}
|
||||
found.count = 0;
|
||||
}
|
||||
return self.arena_pool.release(allocator);
|
||||
}
|
||||
@@ -746,7 +772,10 @@ fn pageDoneCallback(ctx: *anyopaque) !void {
|
||||
|
||||
switch (self._parse_state) {
|
||||
.html => |buf| {
|
||||
var parser = Parser.init(self.arena, self.document.asNode(), self);
|
||||
const parse_arena = try self.getArena(.{ .debug = "Page.parse" });
|
||||
defer self.releaseArena(parse_arena);
|
||||
|
||||
var parser = Parser.init(parse_arena, self.document.asNode(), self);
|
||||
parser.parse(buf.items);
|
||||
self._script_manager.staticScriptsDone();
|
||||
if (self._script_manager.isDone()) {
|
||||
@@ -758,7 +787,11 @@ fn pageDoneCallback(ctx: *anyopaque) !void {
|
||||
},
|
||||
.text => |*buf| {
|
||||
try buf.appendSlice(self.arena, "</pre></body></html>");
|
||||
var parser = Parser.init(self.arena, self.document.asNode(), self);
|
||||
|
||||
const parse_arena = try self.getArena(.{ .debug = "Page.parse" });
|
||||
defer self.releaseArena(parse_arena);
|
||||
|
||||
var parser = Parser.init(parse_arena, self.document.asNode(), self);
|
||||
parser.parse(buf.items);
|
||||
self.documentIsComplete();
|
||||
},
|
||||
@@ -1135,6 +1168,35 @@ pub fn getElementByIdFromNode(self: *Page, node: *Node, id: []const u8) ?*Elemen
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Sets an inline event listener (`onload`, `onclick`, `onwheel` etc.);
|
||||
/// overrides the listener if there's already one.
|
||||
pub fn setAttrListener(
|
||||
self: *Page,
|
||||
element: *Element,
|
||||
listener_type: Element.KnownListener,
|
||||
listener_callback: JS.Function.Global,
|
||||
) !void {
|
||||
if (comptime IS_DEBUG) {
|
||||
log.debug(.event, "Page.setAttrListener", .{
|
||||
.element = element,
|
||||
.listener_type = listener_type,
|
||||
});
|
||||
}
|
||||
|
||||
const key = element.calcAttrListenerKey(listener_type);
|
||||
const gop = try self._element_attr_listeners.getOrPut(self.arena, key);
|
||||
gop.value_ptr.* = listener_callback;
|
||||
}
|
||||
|
||||
/// Returns the inline event listener by an element and listener type.
|
||||
pub fn getAttrListener(
|
||||
self: *const Page,
|
||||
element: *Element,
|
||||
listener_type: Element.KnownListener,
|
||||
) ?JS.Function.Global {
|
||||
return self._element_attr_listeners.get(element.calcAttrListenerKey(listener_type));
|
||||
}
|
||||
|
||||
pub fn registerPerformanceObserver(self: *Page, observer: *PerformanceObserver) !void {
|
||||
return self._performance_observers.append(self.arena, observer);
|
||||
}
|
||||
@@ -2174,6 +2236,236 @@ fn populateElementAttributes(self: *Page, element: *Element, list: anytype) !voi
|
||||
}
|
||||
var attributes = try element.createAttributeList(self);
|
||||
while (list.next()) |attr| {
|
||||
// Event handlers can be provided like attributes; here we check if there's such.
|
||||
const name = attr.name.local;
|
||||
lp.assert(name.len != 0, "populateElementAttributes: 0-length attr name", .{ .attr = attr });
|
||||
// Idea here is to make this check as cheap as possible.
|
||||
const has_on_prefix = @as(u16, @bitCast([2]u8{ name.ptr[0], name.ptr[1 % name.len] })) == asUint("on");
|
||||
// We may have found an event handler.
|
||||
if (has_on_prefix) {
|
||||
// Must be usable as function.
|
||||
const func = try self.js.stringToPersistedFunction(attr.value.slice());
|
||||
|
||||
// Longest known listener kind is 32 bytes long.
|
||||
const remaining: u6 = @truncate(name.len -| 2);
|
||||
const unsafe = name.ptr + 2;
|
||||
const Vec16x8 = @Vector(16, u8);
|
||||
const Vec32x8 = @Vector(32, u8);
|
||||
|
||||
switch (remaining) {
|
||||
3 => if (@as(u24, @bitCast(unsafe[0..3].*)) == asUint("cut")) {
|
||||
try self.setAttrListener(element, .cut, func);
|
||||
},
|
||||
4 => switch (@as(u32, @bitCast(unsafe[0..4].*))) {
|
||||
asUint("blur") => try self.setAttrListener(element, .blur, func),
|
||||
asUint("copy") => try self.setAttrListener(element, .copy, func),
|
||||
asUint("drag") => try self.setAttrListener(element, .drag, func),
|
||||
asUint("drop") => try self.setAttrListener(element, .drop, func),
|
||||
asUint("load") => try self.setAttrListener(element, .load, func),
|
||||
asUint("play") => try self.setAttrListener(element, .play, func),
|
||||
else => {},
|
||||
},
|
||||
5 => switch (@as(u40, @bitCast(unsafe[0..5].*))) {
|
||||
asUint("abort") => try self.setAttrListener(element, .abort, func),
|
||||
asUint("click") => try self.setAttrListener(element, .click, func),
|
||||
asUint("close") => try self.setAttrListener(element, .close, func),
|
||||
asUint("ended") => try self.setAttrListener(element, .ended, func),
|
||||
asUint("error") => try self.setAttrListener(element, .@"error", func),
|
||||
asUint("focus") => try self.setAttrListener(element, .focus, func),
|
||||
asUint("input") => try self.setAttrListener(element, .input, func),
|
||||
asUint("keyup") => try self.setAttrListener(element, .keyup, func),
|
||||
asUint("paste") => try self.setAttrListener(element, .paste, func),
|
||||
asUint("pause") => try self.setAttrListener(element, .pause, func),
|
||||
asUint("reset") => try self.setAttrListener(element, .reset, func),
|
||||
asUint("wheel") => try self.setAttrListener(element, .wheel, func),
|
||||
else => {},
|
||||
},
|
||||
6 => switch (@as(u48, @bitCast(unsafe[0..6].*))) {
|
||||
asUint("cancel") => try self.setAttrListener(element, .cancel, func),
|
||||
asUint("change") => try self.setAttrListener(element, .change, func),
|
||||
asUint("resize") => try self.setAttrListener(element, .resize, func),
|
||||
asUint("scroll") => try self.setAttrListener(element, .scroll, func),
|
||||
asUint("seeked") => try self.setAttrListener(element, .seeked, func),
|
||||
asUint("select") => try self.setAttrListener(element, .select, func),
|
||||
asUint("submit") => try self.setAttrListener(element, .submit, func),
|
||||
asUint("toggle") => try self.setAttrListener(element, .toggle, func),
|
||||
else => {},
|
||||
},
|
||||
7 => switch (@as(u56, @bitCast(unsafe[0..7].*))) {
|
||||
asUint("canplay") => try self.setAttrListener(element, .canplay, func),
|
||||
asUint("command") => try self.setAttrListener(element, .command, func),
|
||||
asUint("dragend") => try self.setAttrListener(element, .dragend, func),
|
||||
asUint("emptied") => try self.setAttrListener(element, .emptied, func),
|
||||
asUint("invalid") => try self.setAttrListener(element, .invalid, func),
|
||||
asUint("keydown") => try self.setAttrListener(element, .keydown, func),
|
||||
asUint("mouseup") => try self.setAttrListener(element, .mouseup, func),
|
||||
asUint("playing") => try self.setAttrListener(element, .playing, func),
|
||||
asUint("seeking") => try self.setAttrListener(element, .seeking, func),
|
||||
asUint("stalled") => try self.setAttrListener(element, .stalled, func),
|
||||
asUint("suspend") => try self.setAttrListener(element, .@"suspend", func),
|
||||
asUint("waiting") => try self.setAttrListener(element, .waiting, func),
|
||||
else => {},
|
||||
},
|
||||
8 => switch (@as(u64, @bitCast(unsafe[0..8].*))) {
|
||||
asUint("auxclick") => try self.setAttrListener(element, .auxclick, func),
|
||||
asUint("dblclick") => try self.setAttrListener(element, .dblclick, func),
|
||||
asUint("dragexit") => try self.setAttrListener(element, .dragexit, func),
|
||||
asUint("dragover") => try self.setAttrListener(element, .dragover, func),
|
||||
asUint("formdata") => try self.setAttrListener(element, .formdata, func),
|
||||
asUint("keypress") => try self.setAttrListener(element, .keypress, func),
|
||||
asUint("mouseout") => try self.setAttrListener(element, .mouseout, func),
|
||||
asUint("progress") => try self.setAttrListener(element, .progress, func),
|
||||
else => {},
|
||||
},
|
||||
// Won't fit to 64-bit integer; we do 2 checks.
|
||||
9 => switch (@as(u64, @bitCast(unsafe[0..8].*))) {
|
||||
asUint("cuechang") => if (unsafe[8] == 'e') try self.setAttrListener(element, .cuechange, func),
|
||||
asUint("dragente") => if (unsafe[8] == 'r') try self.setAttrListener(element, .dragenter, func),
|
||||
asUint("dragleav") => if (unsafe[8] == 'e') try self.setAttrListener(element, .dragleave, func),
|
||||
asUint("dragstar") => if (unsafe[8] == 't') try self.setAttrListener(element, .dragstart, func),
|
||||
asUint("loadstar") => if (unsafe[8] == 't') try self.setAttrListener(element, .loadstart, func),
|
||||
asUint("mousedow") => if (unsafe[8] == 'n') try self.setAttrListener(element, .mousedown, func),
|
||||
asUint("mousemov") => if (unsafe[8] == 'e') try self.setAttrListener(element, .mousemove, func),
|
||||
asUint("mouseove") => if (unsafe[8] == 'r') try self.setAttrListener(element, .mouseover, func),
|
||||
asUint("pointeru") => if (unsafe[8] == 'p') try self.setAttrListener(element, .pointerup, func),
|
||||
asUint("scrollen") => if (unsafe[8] == 'd') try self.setAttrListener(element, .scrollend, func),
|
||||
else => {},
|
||||
},
|
||||
10 => switch (@as(u64, @bitCast(unsafe[0..8].*))) {
|
||||
asUint("loadedda") => if (asUint("ta") == @as(u16, @bitCast(unsafe[8..10].*)))
|
||||
try self.setAttrListener(element, .loadeddata, func),
|
||||
asUint("pointero") => if (asUint("ut") == @as(u16, @bitCast(unsafe[8..10].*)))
|
||||
try self.setAttrListener(element, .pointerout, func),
|
||||
asUint("ratechan") => if (asUint("ge") == @as(u16, @bitCast(unsafe[8..10].*)))
|
||||
try self.setAttrListener(element, .ratechange, func),
|
||||
asUint("slotchan") => if (asUint("ge") == @as(u16, @bitCast(unsafe[8..10].*)))
|
||||
try self.setAttrListener(element, .slotchange, func),
|
||||
asUint("timeupda") => if (asUint("te") == @as(u16, @bitCast(unsafe[8..10].*)))
|
||||
try self.setAttrListener(element, .timeupdate, func),
|
||||
else => {},
|
||||
},
|
||||
11 => switch (@as(u64, @bitCast(unsafe[0..8].*))) {
|
||||
asUint("beforein") => if (asUint("put") == @as(u24, @bitCast(unsafe[8..11].*)))
|
||||
try self.setAttrListener(element, .beforeinput, func),
|
||||
asUint("beforema") => if (asUint("tch") == @as(u24, @bitCast(unsafe[8..11].*)))
|
||||
try self.setAttrListener(element, .beforematch, func),
|
||||
asUint("contextl") => if (asUint("ost") == @as(u24, @bitCast(unsafe[8..11].*)))
|
||||
try self.setAttrListener(element, .contextlost, func),
|
||||
asUint("contextm") => if (asUint("enu") == @as(u24, @bitCast(unsafe[8..11].*)))
|
||||
try self.setAttrListener(element, .contextmenu, func),
|
||||
asUint("pointerd") => if (asUint("own") == @as(u24, @bitCast(unsafe[8..11].*)))
|
||||
try self.setAttrListener(element, .pointerdown, func),
|
||||
asUint("pointerm") => if (asUint("ove") == @as(u24, @bitCast(unsafe[8..11].*)))
|
||||
try self.setAttrListener(element, .pointermove, func),
|
||||
asUint("pointero") => if (asUint("ver") == @as(u24, @bitCast(unsafe[8..11].*)))
|
||||
try self.setAttrListener(element, .pointerover, func),
|
||||
asUint("selectst") => if (asUint("art") == @as(u24, @bitCast(unsafe[8..11].*)))
|
||||
try self.setAttrListener(element, .selectstart, func),
|
||||
else => {},
|
||||
},
|
||||
12 => switch (@as(u64, @bitCast(unsafe[0..8].*))) {
|
||||
asUint("animatio") => if (asUint("nend") == @as(u32, @bitCast(unsafe[8..12].*)))
|
||||
try self.setAttrListener(element, .animationend, func),
|
||||
asUint("beforeto") => if (asUint("ggle") == @as(u32, @bitCast(unsafe[8..12].*)))
|
||||
try self.setAttrListener(element, .beforetoggle, func),
|
||||
asUint("pointere") => if (asUint("nter") == @as(u32, @bitCast(unsafe[8..12].*)))
|
||||
try self.setAttrListener(element, .pointerenter, func),
|
||||
asUint("pointerl") => if (asUint("eave") == @as(u32, @bitCast(unsafe[8..12].*)))
|
||||
try self.setAttrListener(element, .pointerleave, func),
|
||||
asUint("volumech") => if (asUint("ange") == @as(u32, @bitCast(unsafe[8..12].*)))
|
||||
try self.setAttrListener(element, .volumechange, func),
|
||||
else => {},
|
||||
},
|
||||
13 => switch (@as(u64, @bitCast(unsafe[0..8].*))) {
|
||||
asUint("pointerc") => if (asUint("ancel") == @as(u40, @bitCast(unsafe[8..13].*)))
|
||||
try self.setAttrListener(element, .pointercancel, func),
|
||||
asUint("transiti") => switch (@as(u40, @bitCast(unsafe[8..13].*))) {
|
||||
asUint("onend") => try self.setAttrListener(element, .transitionend, func),
|
||||
asUint("onrun") => try self.setAttrListener(element, .transitionrun, func),
|
||||
else => {},
|
||||
},
|
||||
else => {},
|
||||
},
|
||||
14 => switch (@as(u64, @bitCast(unsafe[0..8].*))) {
|
||||
asUint("animatio") => if (asUint("nstart") == @as(u48, @bitCast(unsafe[8..14].*)))
|
||||
try self.setAttrListener(element, .animationstart, func),
|
||||
asUint("canplayt") => if (asUint("hrough") == @as(u48, @bitCast(unsafe[8..14].*)))
|
||||
try self.setAttrListener(element, .canplaythrough, func),
|
||||
asUint("duration") => if (asUint("change") == @as(u48, @bitCast(unsafe[8..14].*)))
|
||||
try self.setAttrListener(element, .durationchange, func),
|
||||
asUint("loadedme") => if (asUint("tadata") == @as(u48, @bitCast(unsafe[8..14].*)))
|
||||
try self.setAttrListener(element, .loadedmetadata, func),
|
||||
else => {},
|
||||
},
|
||||
15 => switch (@as(u64, @bitCast(unsafe[0..8].*))) {
|
||||
asUint("animatio") => if (asUint("ncancel") == @as(u56, @bitCast(unsafe[8..15].*)))
|
||||
try self.setAttrListener(element, .animationcancel, func),
|
||||
asUint("contextr") => if (asUint("estored") == @as(u56, @bitCast(unsafe[8..15].*)))
|
||||
try self.setAttrListener(element, .contextrestored, func),
|
||||
asUint("fullscre") => if (asUint("enerror") == @as(u56, @bitCast(unsafe[8..15].*)))
|
||||
try self.setAttrListener(element, .fullscreenerror, func),
|
||||
asUint("selectio") => if (asUint("nchange") == @as(u56, @bitCast(unsafe[8..15].*)))
|
||||
try self.setAttrListener(element, .selectionchange, func),
|
||||
asUint("transiti") => if (asUint("onstart") == @as(u56, @bitCast(unsafe[8..15].*)))
|
||||
try self.setAttrListener(element, .transitionstart, func),
|
||||
else => {},
|
||||
},
|
||||
// Can't switch on vector types.
|
||||
16 => {
|
||||
const as_vector: Vec16x8 = unsafe[0..16].*;
|
||||
|
||||
if (@reduce(.And, as_vector == @as(Vec16x8, "fullscreenchange".*))) {
|
||||
try self.setAttrListener(element, .fullscreenchange, func);
|
||||
} else if (@reduce(.And, as_vector == @as(Vec16x8, "pointerrawupdate".*))) {
|
||||
try self.setAttrListener(element, .pointerrawupdate, func);
|
||||
} else if (@reduce(.And, as_vector == @as(Vec16x8, "transitioncancel".*))) {
|
||||
try self.setAttrListener(element, .transitioncancel, func);
|
||||
}
|
||||
},
|
||||
17 => {
|
||||
const as_vector: Vec16x8 = unsafe[0..16].*;
|
||||
|
||||
const dirty = @reduce(.And, as_vector == @as(Vec16x8, "gotpointercaptur".*)) and
|
||||
unsafe[16] == 'e';
|
||||
if (dirty) {
|
||||
try self.setAttrListener(element, .gotpointercapture, func);
|
||||
}
|
||||
},
|
||||
18 => {
|
||||
const as_vector: Vec16x8 = unsafe[0..16].*;
|
||||
|
||||
const is_animationiteration = @reduce(.And, as_vector == @as(Vec16x8, "animationiterati".*)) and
|
||||
asUint("on") == @as(u16, @bitCast(unsafe[16..18].*));
|
||||
if (is_animationiteration) {
|
||||
try self.setAttrListener(element, .animationiteration, func);
|
||||
} else {
|
||||
const is_lostpointercapture = @reduce(.And, as_vector == @as(Vec16x8, "lostpointercaptu".*)) and
|
||||
asUint("re") == @as(u16, @bitCast(unsafe[16..18].*));
|
||||
if (is_lostpointercapture) {
|
||||
try self.setAttrListener(element, .lostpointercapture, func);
|
||||
}
|
||||
}
|
||||
},
|
||||
23 => {
|
||||
const as_vector: Vec16x8 = unsafe[0..16].*;
|
||||
|
||||
const dirty = @reduce(.And, as_vector == @as(Vec16x8, "securitypolicyvi".*)) and
|
||||
asUint("olation") == @as(u56, @bitCast(unsafe[16..23].*));
|
||||
if (dirty) {
|
||||
try self.setAttrListener(element, .securitypolicyviolation, func);
|
||||
}
|
||||
},
|
||||
32 => {
|
||||
const as_vector: Vec32x8 = unsafe[0..32].*;
|
||||
|
||||
if (@reduce(.And, as_vector == @as(Vec32x8, "contentvisibilityautostatechange".*))) {
|
||||
try self.setAttrListener(element, .contentvisibilityautostatechange, func);
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
try attributes.putNew(attr.name.local.slice(), attr.value.slice(), self);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ const log = @import("../../log.zig");
|
||||
|
||||
const bridge = @import("bridge.zig");
|
||||
const Context = @import("Context.zig");
|
||||
const Isolate = @import("Isolate.zig");
|
||||
const Platform = @import("Platform.zig");
|
||||
const Snapshot = @import("Snapshot.zig");
|
||||
const Inspector = @import("Inspector.zig");
|
||||
@@ -193,6 +194,8 @@ pub fn newExecutionWorld(self: *Env) !ExecutionWorld {
|
||||
// a Context, it's managed by the garbage collector. We use the
|
||||
// `lowMemoryNotification` call on the isolate to encourage v8 to free
|
||||
// any contexts which have been freed.
|
||||
// This GC is very aggressive. Use memoryPressureNotification for less
|
||||
// aggressive GC passes.
|
||||
pub fn lowMemoryNotification(self: *Env) void {
|
||||
var handle_scope: js.HandleScope = undefined;
|
||||
handle_scope.init(self.isolate);
|
||||
@@ -200,6 +203,21 @@ pub fn lowMemoryNotification(self: *Env) void {
|
||||
self.isolate.lowMemoryNotification();
|
||||
}
|
||||
|
||||
// V8 doesn't immediately free memory associated with
|
||||
// a Context, it's managed by the garbage collector. We use the
|
||||
// `memoryPressureNotification` call on the isolate to encourage v8 to free
|
||||
// any contexts which have been freed.
|
||||
// The level indicates the aggressivity of the GC required:
|
||||
// moderate speeds up incremental GC
|
||||
// critical runs one full GC
|
||||
// For a more aggressive GC, use lowMemoryNotification.
|
||||
pub fn memoryPressureNotification(self: *Env, level: Isolate.MemoryPressureLevel) void {
|
||||
var handle_scope: js.HandleScope = undefined;
|
||||
handle_scope.init(self.isolate);
|
||||
defer handle_scope.deinit();
|
||||
self.isolate.memoryPressureNotification(level);
|
||||
}
|
||||
|
||||
pub fn dumpMemoryStats(self: *Env) void {
|
||||
const stats = self.isolate.getHeapStatistics();
|
||||
std.debug.print(
|
||||
|
||||
@@ -20,6 +20,8 @@ const std = @import("std");
|
||||
const js = @import("js.zig");
|
||||
const v8 = js.v8;
|
||||
|
||||
const log = @import("../../log.zig");
|
||||
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const Function = @This();
|
||||
@@ -69,25 +71,30 @@ pub fn newInstance(self: *const Function, caught: *js.TryCatch.Caught) !js.Objec
|
||||
|
||||
pub fn call(self: *const Function, comptime T: type, args: anytype) !T {
|
||||
var caught: js.TryCatch.Caught = undefined;
|
||||
return self._tryCallWithThis(T, self.getThis(), args, &caught, false);
|
||||
return self._tryCallWithThis(T, self.getThis(), args, &caught) catch |err| {
|
||||
log.warn(.js, "call caught", .{ .err = err, .caught = caught });
|
||||
return err;
|
||||
};
|
||||
}
|
||||
|
||||
pub fn callWithThis(self: *const Function, comptime T: type, this: anytype, args: anytype) !T {
|
||||
var caught: js.TryCatch.Caught = undefined;
|
||||
return self._tryCallWithThis(T, this, args, &caught, false);
|
||||
return self._tryCallWithThis(T, this, args, &caught) catch |err| {
|
||||
log.warn(.js, "callWithThis caught", .{ .err = err, .caught = caught });
|
||||
return err;
|
||||
};
|
||||
}
|
||||
|
||||
pub fn tryCall(self: *const Function, comptime T: type, args: anytype, caught: *js.TryCatch.Caught) !T {
|
||||
caught.* = .{};
|
||||
return self._tryCallWithThis(T, self.getThis(), args, caught, true);
|
||||
return self._tryCallWithThis(T, self.getThis(), args, caught);
|
||||
}
|
||||
|
||||
pub fn tryCallWithThis(self: *const Function, comptime T: type, this: anytype, args: anytype, caught: *js.TryCatch.Caught) !T {
|
||||
caught.* = .{};
|
||||
return self._tryCallWithThis(T, this, args, caught, true);
|
||||
return self._tryCallWithThis(T, this, args, caught);
|
||||
}
|
||||
|
||||
pub fn _tryCallWithThis(self: *const Function, comptime T: type, this: anytype, args: anytype, caught: *js.TryCatch.Caught, comptime need_caught: bool) !T {
|
||||
pub fn _tryCallWithThis(self: *const Function, comptime T: type, this: anytype, args: anytype, caught: *js.TryCatch.Caught) !T {
|
||||
caught.* = .{};
|
||||
const local = self.local;
|
||||
|
||||
// When we're calling a function from within JavaScript itself, this isn't
|
||||
@@ -140,11 +147,7 @@ pub fn _tryCallWithThis(self: *const Function, comptime T: type, this: anytype,
|
||||
defer try_catch.deinit();
|
||||
|
||||
const handle = v8.v8__Function__Call(self.handle, local.handle, js_this.handle, @as(c_int, @intCast(js_args.len)), c_args) orelse {
|
||||
if (comptime need_caught) {
|
||||
// relatively expensive, so if the caller knows caught won't be needed,
|
||||
// we can leave it uninitialized.
|
||||
caught.* = try_catch.caughtOrError(local.call_arena, error.JSExecCallback);
|
||||
}
|
||||
return error.JSExecCallback;
|
||||
};
|
||||
|
||||
|
||||
@@ -151,6 +151,18 @@ pub fn contextCreated(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn contextDestroyed(self: *Inspector, local: *const js.Local) void {
|
||||
v8.v8_inspector__Inspector__ContextDestroyed(self.handle, local.handle);
|
||||
}
|
||||
|
||||
pub fn resetContextGroup(self: *const Inspector) void {
|
||||
var hs: v8.HandleScope = undefined;
|
||||
v8.v8__HandleScope__CONSTRUCT(&hs, self.isolate);
|
||||
defer v8.v8__HandleScope__DESTRUCT(&hs);
|
||||
|
||||
v8.v8_inspector__Inspector__ResetContextGroup(self.handle, CONTEXT_GROUP_ID);
|
||||
}
|
||||
|
||||
// Retrieves the RemoteObject for a given value.
|
||||
// The value is loaded through the ExecutionWorld's mapZigInstanceToJs function,
|
||||
// just like a method return value. Therefore, if we've mapped this
|
||||
|
||||
@@ -57,6 +57,16 @@ pub fn lowMemoryNotification(self: Isolate) void {
|
||||
v8.v8__Isolate__LowMemoryNotification(self.handle);
|
||||
}
|
||||
|
||||
pub const MemoryPressureLevel = enum(u32) {
|
||||
none = v8.kNone,
|
||||
moderate = v8.kModerate,
|
||||
critical = v8.kCritical,
|
||||
};
|
||||
|
||||
pub fn memoryPressureNotification(self: Isolate, level: MemoryPressureLevel) void {
|
||||
v8.v8__Isolate__MemoryPressureNotification(self.handle, @intFromEnum(level));
|
||||
}
|
||||
|
||||
pub fn notifyContextDisposed(self: Isolate) void {
|
||||
_ = v8.v8__Isolate__ContextDisposedNotification(self.handle);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ const Page = @import("../Page.zig");
|
||||
const Node = @import("../webapi/Node.zig");
|
||||
const Element = @import("../webapi/Element.zig");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const IS_DEBUG = @import("builtin").mode == .Debug;
|
||||
|
||||
pub const ParsedNode = struct {
|
||||
node: *Node,
|
||||
@@ -373,6 +374,17 @@ fn _appendCallback(self: *Parser, parent: *Node, node_or_text: h5e.NodeOrText) !
|
||||
switch (node_or_text.toUnion()) {
|
||||
.node => |cpn| {
|
||||
const child = getNode(cpn);
|
||||
if (child._parent) |previous_parent| {
|
||||
// html5ever says this can't happen, but we might be screwing up
|
||||
// the node on our side. We shouldn't be, but we're seeing this
|
||||
// in the wild, and I'm not sure why. In debug, let's crash so
|
||||
// we can try to figure it out. In release, let's disconnect
|
||||
// the child first.
|
||||
if (comptime IS_DEBUG) {
|
||||
unreachable;
|
||||
}
|
||||
self.page.removeNode(previous_parent, child, .{ .will_be_reconnected = parent.isConnected() });
|
||||
}
|
||||
try self.page.appendNew(parent, .{ .node = child });
|
||||
},
|
||||
.text => |txt| try self.page.appendNew(parent, .{ .text = txt }),
|
||||
|
||||
490
src/browser/tests/element/html/event_listeners.html
Normal file
490
src/browser/tests/element/html/event_listeners.html
Normal file
@@ -0,0 +1,490 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="../../testing.js"></script>
|
||||
|
||||
<!-- Test inline event listeners set via HTML attributes -->
|
||||
<div id="attr-click" onclick="window.x = 1"></div>
|
||||
<div id="attr-load" onload="window.x = 1"></div>
|
||||
<div id="attr-error" onerror="window.x = 1"></div>
|
||||
<div id="attr-focus" onfocus="window.x = 1"></div>
|
||||
<div id="attr-blur" onblur="window.x = 1"></div>
|
||||
<div id="attr-keydown" onkeydown="window.x = 1"></div>
|
||||
<div id="attr-mousedown" onmousedown="window.x = 1"></div>
|
||||
<div id="attr-submit" onsubmit="window.x = 1"></div>
|
||||
<div id="attr-wheel" onwheel="window.x = 1"></div>
|
||||
<div id="attr-scroll" onscroll="window.x = 1"></div>
|
||||
<div id="attr-contextmenu" oncontextmenu="window.x = 1"></div>
|
||||
<div id="no-listeners"></div>
|
||||
|
||||
<script id="attr_listener_returns_function">
|
||||
{
|
||||
// Inline listeners set via HTML attributes should return a function
|
||||
testing.expectEqual('function', typeof $('#attr-click').onclick);
|
||||
testing.expectEqual('function', typeof $('#attr-load').onload);
|
||||
testing.expectEqual('function', typeof $('#attr-error').onerror);
|
||||
testing.expectEqual('function', typeof $('#attr-focus').onfocus);
|
||||
testing.expectEqual('function', typeof $('#attr-blur').onblur);
|
||||
testing.expectEqual('function', typeof $('#attr-keydown').onkeydown);
|
||||
testing.expectEqual('function', typeof $('#attr-mousedown').onmousedown);
|
||||
testing.expectEqual('function', typeof $('#attr-submit').onsubmit);
|
||||
testing.expectEqual('function', typeof $('#attr-wheel').onwheel);
|
||||
testing.expectEqual('function', typeof $('#attr-scroll').onscroll);
|
||||
testing.expectEqual('function', typeof $('#attr-contextmenu').oncontextmenu);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="no_attr_listener_returns_null">
|
||||
{
|
||||
// Elements without inline listeners should return null
|
||||
const div = $('#no-listeners');
|
||||
testing.expectEqual(null, div.onclick);
|
||||
testing.expectEqual(null, div.onload);
|
||||
testing.expectEqual(null, div.onerror);
|
||||
testing.expectEqual(null, div.onfocus);
|
||||
testing.expectEqual(null, div.onblur);
|
||||
testing.expectEqual(null, div.onkeydown);
|
||||
testing.expectEqual(null, div.onmousedown);
|
||||
testing.expectEqual(null, div.onsubmit);
|
||||
testing.expectEqual(null, div.onwheel);
|
||||
testing.expectEqual(null, div.onscroll);
|
||||
testing.expectEqual(null, div.oncontextmenu);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="js_setter_getter">
|
||||
{
|
||||
// Test setting and getting listeners via JavaScript property
|
||||
const div = document.createElement('div');
|
||||
|
||||
// Initially null
|
||||
testing.expectEqual(null, div.onclick);
|
||||
testing.expectEqual(null, div.onload);
|
||||
testing.expectEqual(null, div.onerror);
|
||||
|
||||
// Set listeners
|
||||
const clickHandler = () => {};
|
||||
const loadHandler = () => {};
|
||||
const errorHandler = () => {};
|
||||
|
||||
div.onclick = clickHandler;
|
||||
div.onload = loadHandler;
|
||||
div.onerror = errorHandler;
|
||||
|
||||
// Verify they can be retrieved and are functions
|
||||
testing.expectEqual('function', typeof div.onclick);
|
||||
testing.expectEqual('function', typeof div.onload);
|
||||
testing.expectEqual('function', typeof div.onerror);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="js_listener_invoke">
|
||||
{
|
||||
// Test that JS-set listeners can be invoked directly
|
||||
const div = document.createElement('div');
|
||||
window.jsInvokeResult = 0;
|
||||
|
||||
div.onclick = () => { window.jsInvokeResult = 100; };
|
||||
div.onclick();
|
||||
testing.expectEqual(100, window.jsInvokeResult);
|
||||
|
||||
div.onload = () => { window.jsInvokeResult = 200; };
|
||||
div.onload();
|
||||
testing.expectEqual(200, window.jsInvokeResult);
|
||||
|
||||
div.onfocus = () => { window.jsInvokeResult = 300; };
|
||||
div.onfocus();
|
||||
testing.expectEqual(300, window.jsInvokeResult);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="js_listener_invoke_with_return">
|
||||
{
|
||||
// Test that JS-set listeners return values when invoked
|
||||
const div = document.createElement('div');
|
||||
|
||||
div.onclick = () => { return 'click-result'; };
|
||||
testing.expectEqual('click-result', div.onclick());
|
||||
|
||||
div.onload = () => { return 42; };
|
||||
testing.expectEqual(42, div.onload());
|
||||
|
||||
div.onfocus = () => { return { key: 'value' }; };
|
||||
testing.expectEqual('value', div.onfocus().key);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="js_listener_invoke_with_args">
|
||||
{
|
||||
// Test that JS-set listeners can receive arguments when invoked
|
||||
const div = document.createElement('div');
|
||||
|
||||
div.onclick = (a, b) => { return a + b; };
|
||||
testing.expectEqual(15, div.onclick(10, 5));
|
||||
|
||||
div.onload = (msg) => { return 'Hello, ' + msg; };
|
||||
testing.expectEqual('Hello, World', div.onload('World'));
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="js_setter_override">
|
||||
{
|
||||
// Test that setting a new listener overrides the old one
|
||||
const div = document.createElement('div');
|
||||
|
||||
const first = () => { return 1; };
|
||||
const second = () => { return 2; };
|
||||
|
||||
div.onclick = first;
|
||||
testing.expectEqual('function', typeof div.onclick);
|
||||
testing.expectEqual(1, div.onclick());
|
||||
|
||||
div.onclick = second;
|
||||
testing.expectEqual('function', typeof div.onclick);
|
||||
testing.expectEqual(2, div.onclick());
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="different_event_types_independent">
|
||||
{
|
||||
// Test that different event types are stored independently
|
||||
const div = document.createElement('div');
|
||||
|
||||
const clickFn = () => {};
|
||||
const focusFn = () => {};
|
||||
const blurFn = () => {};
|
||||
|
||||
div.onclick = clickFn;
|
||||
testing.expectEqual('function', typeof div.onclick);
|
||||
testing.expectEqual(null, div.onfocus);
|
||||
testing.expectEqual(null, div.onblur);
|
||||
|
||||
div.onfocus = focusFn;
|
||||
testing.expectEqual('function', typeof div.onclick);
|
||||
testing.expectEqual('function', typeof div.onfocus);
|
||||
testing.expectEqual(null, div.onblur);
|
||||
|
||||
div.onblur = blurFn;
|
||||
testing.expectEqual('function', typeof div.onclick);
|
||||
testing.expectEqual('function', typeof div.onfocus);
|
||||
testing.expectEqual('function', typeof div.onblur);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="keyboard_event_listeners">
|
||||
{
|
||||
// Test keyboard event listener getters/setters
|
||||
const div = document.createElement('div');
|
||||
|
||||
testing.expectEqual(null, div.onkeydown);
|
||||
testing.expectEqual(null, div.onkeyup);
|
||||
testing.expectEqual(null, div.onkeypress);
|
||||
|
||||
div.onkeydown = () => {};
|
||||
div.onkeyup = () => {};
|
||||
div.onkeypress = () => {};
|
||||
|
||||
testing.expectEqual('function', typeof div.onkeydown);
|
||||
testing.expectEqual('function', typeof div.onkeyup);
|
||||
testing.expectEqual('function', typeof div.onkeypress);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="mouse_event_listeners">
|
||||
{
|
||||
// Test mouse event listener getters/setters
|
||||
const div = document.createElement('div');
|
||||
|
||||
testing.expectEqual(null, div.onmousedown);
|
||||
testing.expectEqual(null, div.onmouseup);
|
||||
testing.expectEqual(null, div.onmousemove);
|
||||
testing.expectEqual(null, div.onmouseover);
|
||||
testing.expectEqual(null, div.onmouseout);
|
||||
testing.expectEqual(null, div.ondblclick);
|
||||
|
||||
div.onmousedown = () => {};
|
||||
div.onmouseup = () => {};
|
||||
div.onmousemove = () => {};
|
||||
div.onmouseover = () => {};
|
||||
div.onmouseout = () => {};
|
||||
div.ondblclick = () => {};
|
||||
|
||||
testing.expectEqual('function', typeof div.onmousedown);
|
||||
testing.expectEqual('function', typeof div.onmouseup);
|
||||
testing.expectEqual('function', typeof div.onmousemove);
|
||||
testing.expectEqual('function', typeof div.onmouseover);
|
||||
testing.expectEqual('function', typeof div.onmouseout);
|
||||
testing.expectEqual('function', typeof div.ondblclick);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="pointer_event_listeners">
|
||||
{
|
||||
// Test pointer event listener getters/setters
|
||||
const div = document.createElement('div');
|
||||
|
||||
testing.expectEqual(null, div.onpointerdown);
|
||||
testing.expectEqual(null, div.onpointerup);
|
||||
testing.expectEqual(null, div.onpointermove);
|
||||
testing.expectEqual(null, div.onpointerover);
|
||||
testing.expectEqual(null, div.onpointerout);
|
||||
testing.expectEqual(null, div.onpointerenter);
|
||||
testing.expectEqual(null, div.onpointerleave);
|
||||
testing.expectEqual(null, div.onpointercancel);
|
||||
|
||||
div.onpointerdown = () => {};
|
||||
div.onpointerup = () => {};
|
||||
div.onpointermove = () => {};
|
||||
div.onpointerover = () => {};
|
||||
div.onpointerout = () => {};
|
||||
div.onpointerenter = () => {};
|
||||
div.onpointerleave = () => {};
|
||||
div.onpointercancel = () => {};
|
||||
|
||||
testing.expectEqual('function', typeof div.onpointerdown);
|
||||
testing.expectEqual('function', typeof div.onpointerup);
|
||||
testing.expectEqual('function', typeof div.onpointermove);
|
||||
testing.expectEqual('function', typeof div.onpointerover);
|
||||
testing.expectEqual('function', typeof div.onpointerout);
|
||||
testing.expectEqual('function', typeof div.onpointerenter);
|
||||
testing.expectEqual('function', typeof div.onpointerleave);
|
||||
testing.expectEqual('function', typeof div.onpointercancel);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="form_event_listeners">
|
||||
{
|
||||
// Test form event listener getters/setters
|
||||
const form = document.createElement('form');
|
||||
|
||||
testing.expectEqual(null, form.onsubmit);
|
||||
testing.expectEqual(null, form.onreset);
|
||||
testing.expectEqual(null, form.onchange);
|
||||
testing.expectEqual(null, form.oninput);
|
||||
testing.expectEqual(null, form.oninvalid);
|
||||
|
||||
form.onsubmit = () => {};
|
||||
form.onreset = () => {};
|
||||
form.onchange = () => {};
|
||||
form.oninput = () => {};
|
||||
form.oninvalid = () => {};
|
||||
|
||||
testing.expectEqual('function', typeof form.onsubmit);
|
||||
testing.expectEqual('function', typeof form.onreset);
|
||||
testing.expectEqual('function', typeof form.onchange);
|
||||
testing.expectEqual('function', typeof form.oninput);
|
||||
testing.expectEqual('function', typeof form.oninvalid);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="drag_event_listeners">
|
||||
{
|
||||
// Test drag event listener getters/setters
|
||||
const div = document.createElement('div');
|
||||
|
||||
testing.expectEqual(null, div.ondrag);
|
||||
testing.expectEqual(null, div.ondragstart);
|
||||
testing.expectEqual(null, div.ondragend);
|
||||
testing.expectEqual(null, div.ondragenter);
|
||||
testing.expectEqual(null, div.ondragleave);
|
||||
testing.expectEqual(null, div.ondragover);
|
||||
testing.expectEqual(null, div.ondrop);
|
||||
|
||||
div.ondrag = () => {};
|
||||
div.ondragstart = () => {};
|
||||
div.ondragend = () => {};
|
||||
div.ondragenter = () => {};
|
||||
div.ondragleave = () => {};
|
||||
div.ondragover = () => {};
|
||||
div.ondrop = () => {};
|
||||
|
||||
testing.expectEqual('function', typeof div.ondrag);
|
||||
testing.expectEqual('function', typeof div.ondragstart);
|
||||
testing.expectEqual('function', typeof div.ondragend);
|
||||
testing.expectEqual('function', typeof div.ondragenter);
|
||||
testing.expectEqual('function', typeof div.ondragleave);
|
||||
testing.expectEqual('function', typeof div.ondragover);
|
||||
testing.expectEqual('function', typeof div.ondrop);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="clipboard_event_listeners">
|
||||
{
|
||||
// Test clipboard event listener getters/setters
|
||||
const div = document.createElement('div');
|
||||
|
||||
testing.expectEqual(null, div.oncopy);
|
||||
testing.expectEqual(null, div.oncut);
|
||||
testing.expectEqual(null, div.onpaste);
|
||||
|
||||
div.oncopy = () => {};
|
||||
div.oncut = () => {};
|
||||
div.onpaste = () => {};
|
||||
|
||||
testing.expectEqual('function', typeof div.oncopy);
|
||||
testing.expectEqual('function', typeof div.oncut);
|
||||
testing.expectEqual('function', typeof div.onpaste);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="scroll_event_listeners">
|
||||
{
|
||||
// Test scroll event listener getters/setters
|
||||
const div = document.createElement('div');
|
||||
|
||||
testing.expectEqual(null, div.onscroll);
|
||||
testing.expectEqual(null, div.onscrollend);
|
||||
testing.expectEqual(null, div.onresize);
|
||||
|
||||
div.onscroll = () => {};
|
||||
div.onscrollend = () => {};
|
||||
div.onresize = () => {};
|
||||
|
||||
testing.expectEqual('function', typeof div.onscroll);
|
||||
testing.expectEqual('function', typeof div.onscrollend);
|
||||
testing.expectEqual('function', typeof div.onresize);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="animation_event_listeners">
|
||||
{
|
||||
// Test animation event listener getters/setters
|
||||
const div = document.createElement('div');
|
||||
|
||||
testing.expectEqual(null, div.onanimationstart);
|
||||
testing.expectEqual(null, div.onanimationend);
|
||||
testing.expectEqual(null, div.onanimationiteration);
|
||||
testing.expectEqual(null, div.onanimationcancel);
|
||||
|
||||
div.onanimationstart = () => {};
|
||||
div.onanimationend = () => {};
|
||||
div.onanimationiteration = () => {};
|
||||
div.onanimationcancel = () => {};
|
||||
|
||||
testing.expectEqual('function', typeof div.onanimationstart);
|
||||
testing.expectEqual('function', typeof div.onanimationend);
|
||||
testing.expectEqual('function', typeof div.onanimationiteration);
|
||||
testing.expectEqual('function', typeof div.onanimationcancel);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="transition_event_listeners">
|
||||
{
|
||||
// Test transition event listener getters/setters
|
||||
const div = document.createElement('div');
|
||||
|
||||
testing.expectEqual(null, div.ontransitionstart);
|
||||
testing.expectEqual(null, div.ontransitionend);
|
||||
testing.expectEqual(null, div.ontransitionrun);
|
||||
testing.expectEqual(null, div.ontransitioncancel);
|
||||
|
||||
div.ontransitionstart = () => {};
|
||||
div.ontransitionend = () => {};
|
||||
div.ontransitionrun = () => {};
|
||||
div.ontransitioncancel = () => {};
|
||||
|
||||
testing.expectEqual('function', typeof div.ontransitionstart);
|
||||
testing.expectEqual('function', typeof div.ontransitionend);
|
||||
testing.expectEqual('function', typeof div.ontransitionrun);
|
||||
testing.expectEqual('function', typeof div.ontransitioncancel);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="misc_event_listeners">
|
||||
{
|
||||
// Test miscellaneous event listener getters/setters
|
||||
const div = document.createElement('div');
|
||||
|
||||
testing.expectEqual(null, div.onwheel);
|
||||
testing.expectEqual(null, div.ontoggle);
|
||||
testing.expectEqual(null, div.oncontextmenu);
|
||||
testing.expectEqual(null, div.onselect);
|
||||
testing.expectEqual(null, div.onabort);
|
||||
testing.expectEqual(null, div.oncancel);
|
||||
testing.expectEqual(null, div.onclose);
|
||||
|
||||
div.onwheel = () => {};
|
||||
div.ontoggle = () => {};
|
||||
div.oncontextmenu = () => {};
|
||||
div.onselect = () => {};
|
||||
div.onabort = () => {};
|
||||
div.oncancel = () => {};
|
||||
div.onclose = () => {};
|
||||
|
||||
testing.expectEqual('function', typeof div.onwheel);
|
||||
testing.expectEqual('function', typeof div.ontoggle);
|
||||
testing.expectEqual('function', typeof div.oncontextmenu);
|
||||
testing.expectEqual('function', typeof div.onselect);
|
||||
testing.expectEqual('function', typeof div.onabort);
|
||||
testing.expectEqual('function', typeof div.oncancel);
|
||||
testing.expectEqual('function', typeof div.onclose);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="media_event_listeners">
|
||||
{
|
||||
// Test media event listener getters/setters
|
||||
const div = document.createElement('div');
|
||||
|
||||
testing.expectEqual(null, div.onplay);
|
||||
testing.expectEqual(null, div.onpause);
|
||||
testing.expectEqual(null, div.onplaying);
|
||||
testing.expectEqual(null, div.onended);
|
||||
testing.expectEqual(null, div.onvolumechange);
|
||||
testing.expectEqual(null, div.onwaiting);
|
||||
testing.expectEqual(null, div.onseeking);
|
||||
testing.expectEqual(null, div.onseeked);
|
||||
testing.expectEqual(null, div.ontimeupdate);
|
||||
testing.expectEqual(null, div.onloadstart);
|
||||
testing.expectEqual(null, div.onprogress);
|
||||
testing.expectEqual(null, div.onstalled);
|
||||
testing.expectEqual(null, div.onsuspend);
|
||||
testing.expectEqual(null, div.oncanplay);
|
||||
testing.expectEqual(null, div.oncanplaythrough);
|
||||
testing.expectEqual(null, div.ondurationchange);
|
||||
testing.expectEqual(null, div.onemptied);
|
||||
testing.expectEqual(null, div.onloadeddata);
|
||||
testing.expectEqual(null, div.onloadedmetadata);
|
||||
testing.expectEqual(null, div.onratechange);
|
||||
|
||||
div.onplay = () => {};
|
||||
div.onpause = () => {};
|
||||
div.onplaying = () => {};
|
||||
div.onended = () => {};
|
||||
div.onvolumechange = () => {};
|
||||
div.onwaiting = () => {};
|
||||
div.onseeking = () => {};
|
||||
div.onseeked = () => {};
|
||||
div.ontimeupdate = () => {};
|
||||
div.onloadstart = () => {};
|
||||
div.onprogress = () => {};
|
||||
div.onstalled = () => {};
|
||||
div.onsuspend = () => {};
|
||||
div.oncanplay = () => {};
|
||||
div.oncanplaythrough = () => {};
|
||||
div.ondurationchange = () => {};
|
||||
div.onemptied = () => {};
|
||||
div.onloadeddata = () => {};
|
||||
div.onloadedmetadata = () => {};
|
||||
div.onratechange = () => {};
|
||||
|
||||
testing.expectEqual('function', typeof div.onplay);
|
||||
testing.expectEqual('function', typeof div.onpause);
|
||||
testing.expectEqual('function', typeof div.onplaying);
|
||||
testing.expectEqual('function', typeof div.onended);
|
||||
testing.expectEqual('function', typeof div.onvolumechange);
|
||||
testing.expectEqual('function', typeof div.onwaiting);
|
||||
testing.expectEqual('function', typeof div.onseeking);
|
||||
testing.expectEqual('function', typeof div.onseeked);
|
||||
testing.expectEqual('function', typeof div.ontimeupdate);
|
||||
testing.expectEqual('function', typeof div.onloadstart);
|
||||
testing.expectEqual('function', typeof div.onprogress);
|
||||
testing.expectEqual('function', typeof div.onstalled);
|
||||
testing.expectEqual('function', typeof div.onsuspend);
|
||||
testing.expectEqual('function', typeof div.oncanplay);
|
||||
testing.expectEqual('function', typeof div.oncanplaythrough);
|
||||
testing.expectEqual('function', typeof div.ondurationchange);
|
||||
testing.expectEqual('function', typeof div.onemptied);
|
||||
testing.expectEqual('function', typeof div.onloadeddata);
|
||||
testing.expectEqual('function', typeof div.onloadedmetadata);
|
||||
testing.expectEqual('function', typeof div.onratechange);
|
||||
}
|
||||
</script>
|
||||
@@ -48,6 +48,9 @@ pub fn parseFromString(
|
||||
@"image/svg+xml",
|
||||
}, mime_type) orelse return error.NotSupported;
|
||||
|
||||
const arena = try page.getArena(.{ .debug = "DOMParser.parseFromString" });
|
||||
defer page.releaseArena(arena);
|
||||
|
||||
return switch (target_mime) {
|
||||
.@"text/html" => {
|
||||
// Create a new HTMLDocument
|
||||
@@ -61,7 +64,7 @@ pub fn parseFromString(
|
||||
}
|
||||
|
||||
// Parse HTML into the document
|
||||
var parser = Parser.init(page.arena, doc.asNode(), page);
|
||||
var parser = Parser.init(arena, doc.asNode(), page);
|
||||
parser.parse(normalized);
|
||||
|
||||
if (parser.err) |pe| {
|
||||
@@ -78,7 +81,7 @@ pub fn parseFromString(
|
||||
|
||||
// Parse XML into XMLDocument.
|
||||
const doc_node = doc.asNode();
|
||||
var parser = Parser.init(page.arena, doc_node, page);
|
||||
var parser = Parser.init(arena, doc_node, page);
|
||||
parser.parseXML(html);
|
||||
|
||||
if (parser.err) |pe| {
|
||||
|
||||
@@ -648,7 +648,10 @@ pub fn write(self: *Document, text: []const []const u8, page: *Page) !void {
|
||||
page._parse_mode = .document_write;
|
||||
defer page._parse_mode = previous_parse_mode;
|
||||
|
||||
var parser = Parser.init(page.call_arena, fragment_node, page);
|
||||
const arena = try page.getArena(.{ .debug = "Document.write" });
|
||||
defer page.releaseArena(arena);
|
||||
|
||||
var parser = Parser.init(arena, fragment_node, page);
|
||||
parser.parseFragment(html);
|
||||
|
||||
// Extract children from wrapper HTML element (html5ever wraps fragments)
|
||||
@@ -661,7 +664,7 @@ pub fn write(self: *Document, text: []const []const u8, page: *Page) !void {
|
||||
|
||||
var it = if (first.is(Element.Html.Html) == null) fragment_node.childrenIterator() else first.childrenIterator();
|
||||
while (it.next()) |child| {
|
||||
try children_to_insert.append(page.call_arena, child);
|
||||
try children_to_insert.append(arena, child);
|
||||
}
|
||||
|
||||
if (children_to_insert.items.len == 0) {
|
||||
|
||||
@@ -49,6 +49,129 @@ pub const RelListLookup = std.AutoHashMapUnmanaged(*Element, *collections.DOMTok
|
||||
pub const ShadowRootLookup = std.AutoHashMapUnmanaged(*Element, *ShadowRoot);
|
||||
pub const AssignedSlotLookup = std.AutoHashMapUnmanaged(*Element, *Html.Slot);
|
||||
|
||||
/// Better to discriminate it since not directly a pointer int.
|
||||
///
|
||||
/// See `calcAttrListenerKey` to obtain one.
|
||||
const AttrListenerKey = u64;
|
||||
/// Use `getAttrListenerKey` to create a key.
|
||||
pub const AttrListenerLookup = std.AutoHashMapUnmanaged(AttrListenerKey, js.Function.Global);
|
||||
|
||||
/// Enum of known event listeners; increasing the size of it (u7)
|
||||
/// can cause `AttrListenerKey` to behave incorrectly.
|
||||
pub const KnownListener = enum(u7) {
|
||||
abort,
|
||||
animationcancel,
|
||||
animationend,
|
||||
animationiteration,
|
||||
animationstart,
|
||||
auxclick,
|
||||
beforeinput,
|
||||
beforematch,
|
||||
beforetoggle,
|
||||
blur,
|
||||
cancel,
|
||||
canplay,
|
||||
canplaythrough,
|
||||
change,
|
||||
click,
|
||||
close,
|
||||
command,
|
||||
contentvisibilityautostatechange,
|
||||
contextlost,
|
||||
contextmenu,
|
||||
contextrestored,
|
||||
copy,
|
||||
cuechange,
|
||||
cut,
|
||||
dblclick,
|
||||
drag,
|
||||
dragend,
|
||||
dragenter,
|
||||
dragexit,
|
||||
dragleave,
|
||||
dragover,
|
||||
dragstart,
|
||||
drop,
|
||||
durationchange,
|
||||
emptied,
|
||||
ended,
|
||||
@"error",
|
||||
focus,
|
||||
formdata,
|
||||
fullscreenchange,
|
||||
fullscreenerror,
|
||||
gotpointercapture,
|
||||
input,
|
||||
invalid,
|
||||
keydown,
|
||||
keypress,
|
||||
keyup,
|
||||
load,
|
||||
loadeddata,
|
||||
loadedmetadata,
|
||||
loadstart,
|
||||
lostpointercapture,
|
||||
mousedown,
|
||||
mousemove,
|
||||
mouseout,
|
||||
mouseover,
|
||||
mouseup,
|
||||
paste,
|
||||
pause,
|
||||
play,
|
||||
playing,
|
||||
pointercancel,
|
||||
pointerdown,
|
||||
pointerenter,
|
||||
pointerleave,
|
||||
pointermove,
|
||||
pointerout,
|
||||
pointerover,
|
||||
pointerrawupdate,
|
||||
pointerup,
|
||||
progress,
|
||||
ratechange,
|
||||
reset,
|
||||
resize,
|
||||
scroll,
|
||||
scrollend,
|
||||
securitypolicyviolation,
|
||||
seeked,
|
||||
seeking,
|
||||
select,
|
||||
selectionchange,
|
||||
selectstart,
|
||||
slotchange,
|
||||
stalled,
|
||||
submit,
|
||||
@"suspend",
|
||||
timeupdate,
|
||||
toggle,
|
||||
transitioncancel,
|
||||
transitionend,
|
||||
transitionrun,
|
||||
transitionstart,
|
||||
volumechange,
|
||||
waiting,
|
||||
wheel,
|
||||
};
|
||||
|
||||
/// Calculates a lookup key (`AttrListenerKey`) to use with `AttrListenerLookup` for an element.
|
||||
/// NEVER use generated key to retrieve a pointer back. Portability is not guaranteed.
|
||||
pub fn calcAttrListenerKey(self: *Element, event_type: KnownListener) AttrListenerKey {
|
||||
// We can use `Element` for the key too; `EventTarget` is strict about
|
||||
// its size and alignment, though.
|
||||
const target = self.asEventTarget();
|
||||
// Check if we have 3 bits available from alignment of 8.
|
||||
lp.assert(@alignOf(@TypeOf(target)) == 8, "createLookupKey: incorrect alignment", .{
|
||||
.event_target_alignment = @alignOf(@TypeOf(target)),
|
||||
});
|
||||
|
||||
const ptr = @intFromPtr(target) >> 3;
|
||||
lp.assert(ptr < (1 << 57), "createLookupKey: pointer overflow", .{ .ptr = ptr });
|
||||
return ptr | (@as(u64, @intFromEnum(event_type)) << 57);
|
||||
}
|
||||
|
||||
pub const Namespace = enum(u8) {
|
||||
html,
|
||||
svg,
|
||||
|
||||
@@ -281,8 +281,11 @@ pub fn insertAdjacentHTML(
|
||||
});
|
||||
const doc_node = doc.asNode();
|
||||
|
||||
const arena = try page.getArena(.{ .debug = "HTML.insertAdjacentHTML" });
|
||||
defer page.releaseArena(arena);
|
||||
|
||||
const Parser = @import("../../parser/Parser.zig");
|
||||
var parser = Parser.init(page.call_arena, doc_node, page);
|
||||
var parser = Parser.init(arena, doc_node, page);
|
||||
parser.parse(html);
|
||||
|
||||
// Check if there's parsing error.
|
||||
@@ -330,6 +333,766 @@ pub fn click(self: *HtmlElement, page: *Page) !void {
|
||||
try page._event_manager.dispatch(self.asEventTarget(), event.asEvent());
|
||||
}
|
||||
|
||||
pub fn setOnAbort(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .abort, callback);
|
||||
}
|
||||
|
||||
pub fn getOnAbort(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .abort);
|
||||
}
|
||||
|
||||
pub fn setOnAnimationCancel(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .animationcancel, callback);
|
||||
}
|
||||
|
||||
pub fn getOnAnimationCancel(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .animationcancel);
|
||||
}
|
||||
|
||||
pub fn setOnAnimationEnd(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .animationend, callback);
|
||||
}
|
||||
|
||||
pub fn getOnAnimationEnd(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .animationend);
|
||||
}
|
||||
|
||||
pub fn setOnAnimationIteration(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .animationiteration, callback);
|
||||
}
|
||||
|
||||
pub fn getOnAnimationIteration(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .animationiteration);
|
||||
}
|
||||
|
||||
pub fn setOnAnimationStart(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .animationstart, callback);
|
||||
}
|
||||
|
||||
pub fn getOnAnimationStart(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .animationstart);
|
||||
}
|
||||
|
||||
pub fn setOnAuxClick(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .auxclick, callback);
|
||||
}
|
||||
|
||||
pub fn getOnAuxClick(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .auxclick);
|
||||
}
|
||||
|
||||
pub fn setOnBeforeInput(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .beforeinput, callback);
|
||||
}
|
||||
|
||||
pub fn getOnBeforeInput(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .beforeinput);
|
||||
}
|
||||
|
||||
pub fn setOnBeforeMatch(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .beforematch, callback);
|
||||
}
|
||||
|
||||
pub fn getOnBeforeMatch(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .beforematch);
|
||||
}
|
||||
|
||||
pub fn setOnBeforeToggle(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .beforetoggle, callback);
|
||||
}
|
||||
|
||||
pub fn getOnBeforeToggle(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .beforetoggle);
|
||||
}
|
||||
|
||||
pub fn setOnBlur(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .blur, callback);
|
||||
}
|
||||
|
||||
pub fn getOnBlur(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .blur);
|
||||
}
|
||||
|
||||
pub fn setOnCancel(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .cancel, callback);
|
||||
}
|
||||
|
||||
pub fn getOnCancel(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .cancel);
|
||||
}
|
||||
|
||||
pub fn setOnCanPlay(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .canplay, callback);
|
||||
}
|
||||
|
||||
pub fn getOnCanPlay(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .canplay);
|
||||
}
|
||||
|
||||
pub fn setOnCanPlayThrough(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .canplaythrough, callback);
|
||||
}
|
||||
|
||||
pub fn getOnCanPlayThrough(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .canplaythrough);
|
||||
}
|
||||
|
||||
pub fn setOnChange(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .change, callback);
|
||||
}
|
||||
|
||||
pub fn getOnChange(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .change);
|
||||
}
|
||||
|
||||
pub fn setOnClick(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .click, callback);
|
||||
}
|
||||
|
||||
pub fn getOnClick(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .click);
|
||||
}
|
||||
|
||||
pub fn setOnClose(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .close, callback);
|
||||
}
|
||||
|
||||
pub fn getOnClose(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .close);
|
||||
}
|
||||
|
||||
pub fn setOnCommand(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .command, callback);
|
||||
}
|
||||
|
||||
pub fn getOnCommand(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .command);
|
||||
}
|
||||
|
||||
pub fn setOnContentVisibilityAutoStateChange(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .contentvisibilityautostatechange, callback);
|
||||
}
|
||||
|
||||
pub fn getOnContentVisibilityAutoStateChange(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .contentvisibilityautostatechange);
|
||||
}
|
||||
|
||||
pub fn setOnContextLost(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .contextlost, callback);
|
||||
}
|
||||
|
||||
pub fn getOnContextLost(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .contextlost);
|
||||
}
|
||||
|
||||
pub fn setOnContextMenu(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .contextmenu, callback);
|
||||
}
|
||||
|
||||
pub fn getOnContextMenu(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .contextmenu);
|
||||
}
|
||||
|
||||
pub fn setOnContextRestored(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .contextrestored, callback);
|
||||
}
|
||||
|
||||
pub fn getOnContextRestored(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .contextrestored);
|
||||
}
|
||||
|
||||
pub fn setOnCopy(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .copy, callback);
|
||||
}
|
||||
|
||||
pub fn getOnCopy(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .copy);
|
||||
}
|
||||
|
||||
pub fn setOnCueChange(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .cuechange, callback);
|
||||
}
|
||||
|
||||
pub fn getOnCueChange(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .cuechange);
|
||||
}
|
||||
|
||||
pub fn setOnCut(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .cut, callback);
|
||||
}
|
||||
|
||||
pub fn getOnCut(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .cut);
|
||||
}
|
||||
|
||||
pub fn setOnDblClick(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .dblclick, callback);
|
||||
}
|
||||
|
||||
pub fn getOnDblClick(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .dblclick);
|
||||
}
|
||||
|
||||
pub fn setOnDrag(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .drag, callback);
|
||||
}
|
||||
|
||||
pub fn getOnDrag(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .drag);
|
||||
}
|
||||
|
||||
pub fn setOnDragEnd(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .dragend, callback);
|
||||
}
|
||||
|
||||
pub fn getOnDragEnd(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .dragend);
|
||||
}
|
||||
|
||||
pub fn setOnDragEnter(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .dragenter, callback);
|
||||
}
|
||||
|
||||
pub fn getOnDragEnter(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .dragenter);
|
||||
}
|
||||
|
||||
pub fn setOnDragExit(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .dragexit, callback);
|
||||
}
|
||||
|
||||
pub fn getOnDragExit(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .dragexit);
|
||||
}
|
||||
|
||||
pub fn setOnDragLeave(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .dragleave, callback);
|
||||
}
|
||||
|
||||
pub fn getOnDragLeave(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .dragleave);
|
||||
}
|
||||
|
||||
pub fn setOnDragOver(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .dragover, callback);
|
||||
}
|
||||
|
||||
pub fn getOnDragOver(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .dragover);
|
||||
}
|
||||
|
||||
pub fn setOnDragStart(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .dragstart, callback);
|
||||
}
|
||||
|
||||
pub fn getOnDragStart(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .dragstart);
|
||||
}
|
||||
|
||||
pub fn setOnDrop(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .drop, callback);
|
||||
}
|
||||
|
||||
pub fn getOnDrop(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .drop);
|
||||
}
|
||||
|
||||
pub fn setOnDurationChange(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .durationchange, callback);
|
||||
}
|
||||
|
||||
pub fn getOnDurationChange(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .durationchange);
|
||||
}
|
||||
|
||||
pub fn setOnEmptied(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .emptied, callback);
|
||||
}
|
||||
|
||||
pub fn getOnEmptied(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .emptied);
|
||||
}
|
||||
|
||||
pub fn setOnEnded(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .ended, callback);
|
||||
}
|
||||
|
||||
pub fn getOnEnded(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .ended);
|
||||
}
|
||||
|
||||
pub fn setOnError(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .@"error", callback);
|
||||
}
|
||||
|
||||
pub fn getOnError(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .@"error");
|
||||
}
|
||||
|
||||
pub fn setOnFocus(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .focus, callback);
|
||||
}
|
||||
|
||||
pub fn getOnFocus(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .focus);
|
||||
}
|
||||
|
||||
pub fn setOnFormData(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .formdata, callback);
|
||||
}
|
||||
|
||||
pub fn getOnFormData(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .formdata);
|
||||
}
|
||||
|
||||
pub fn setOnFullscreenChange(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .fullscreenchange, callback);
|
||||
}
|
||||
|
||||
pub fn getOnFullscreenChange(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .fullscreenchange);
|
||||
}
|
||||
|
||||
pub fn setOnFullscreenError(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .fullscreenerror, callback);
|
||||
}
|
||||
|
||||
pub fn getOnFullscreenError(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .fullscreenerror);
|
||||
}
|
||||
|
||||
pub fn setOnGotPointerCapture(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .gotpointercapture, callback);
|
||||
}
|
||||
|
||||
pub fn getOnGotPointerCapture(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .gotpointercapture);
|
||||
}
|
||||
|
||||
pub fn setOnInput(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .input, callback);
|
||||
}
|
||||
|
||||
pub fn getOnInput(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .input);
|
||||
}
|
||||
|
||||
pub fn setOnInvalid(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .invalid, callback);
|
||||
}
|
||||
|
||||
pub fn getOnInvalid(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .invalid);
|
||||
}
|
||||
|
||||
pub fn setOnKeyDown(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .keydown, callback);
|
||||
}
|
||||
|
||||
pub fn getOnKeyDown(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .keydown);
|
||||
}
|
||||
|
||||
pub fn setOnKeyPress(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .keypress, callback);
|
||||
}
|
||||
|
||||
pub fn getOnKeyPress(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .keypress);
|
||||
}
|
||||
|
||||
pub fn setOnKeyUp(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .keyup, callback);
|
||||
}
|
||||
|
||||
pub fn getOnKeyUp(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .keyup);
|
||||
}
|
||||
|
||||
pub fn setOnLoad(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .load, callback);
|
||||
}
|
||||
|
||||
pub fn getOnLoad(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .load);
|
||||
}
|
||||
|
||||
pub fn setOnLoadedData(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .loadeddata, callback);
|
||||
}
|
||||
|
||||
pub fn getOnLoadedData(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .loadeddata);
|
||||
}
|
||||
|
||||
pub fn setOnLoadedMetadata(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .loadedmetadata, callback);
|
||||
}
|
||||
|
||||
pub fn getOnLoadedMetadata(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .loadedmetadata);
|
||||
}
|
||||
|
||||
pub fn setOnLoadStart(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .loadstart, callback);
|
||||
}
|
||||
|
||||
pub fn getOnLoadStart(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .loadstart);
|
||||
}
|
||||
|
||||
pub fn setOnLostPointerCapture(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .lostpointercapture, callback);
|
||||
}
|
||||
|
||||
pub fn getOnLostPointerCapture(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .lostpointercapture);
|
||||
}
|
||||
|
||||
pub fn setOnMouseDown(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .mousedown, callback);
|
||||
}
|
||||
|
||||
pub fn getOnMouseDown(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .mousedown);
|
||||
}
|
||||
|
||||
pub fn setOnMouseMove(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .mousemove, callback);
|
||||
}
|
||||
|
||||
pub fn getOnMouseMove(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .mousemove);
|
||||
}
|
||||
|
||||
pub fn setOnMouseOut(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .mouseout, callback);
|
||||
}
|
||||
|
||||
pub fn getOnMouseOut(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .mouseout);
|
||||
}
|
||||
|
||||
pub fn setOnMouseOver(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .mouseover, callback);
|
||||
}
|
||||
|
||||
pub fn getOnMouseOver(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .mouseover);
|
||||
}
|
||||
|
||||
pub fn setOnMouseUp(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .mouseup, callback);
|
||||
}
|
||||
|
||||
pub fn getOnMouseUp(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .mouseup);
|
||||
}
|
||||
|
||||
pub fn setOnPaste(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .paste, callback);
|
||||
}
|
||||
|
||||
pub fn getOnPaste(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .paste);
|
||||
}
|
||||
|
||||
pub fn setOnPause(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .pause, callback);
|
||||
}
|
||||
|
||||
pub fn getOnPause(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .pause);
|
||||
}
|
||||
|
||||
pub fn setOnPlay(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .play, callback);
|
||||
}
|
||||
|
||||
pub fn getOnPlay(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .play);
|
||||
}
|
||||
|
||||
pub fn setOnPlaying(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .playing, callback);
|
||||
}
|
||||
|
||||
pub fn getOnPlaying(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .playing);
|
||||
}
|
||||
|
||||
pub fn setOnPointerCancel(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .pointercancel, callback);
|
||||
}
|
||||
|
||||
pub fn getOnPointerCancel(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .pointercancel);
|
||||
}
|
||||
|
||||
pub fn setOnPointerDown(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .pointerdown, callback);
|
||||
}
|
||||
|
||||
pub fn getOnPointerDown(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .pointerdown);
|
||||
}
|
||||
|
||||
pub fn setOnPointerEnter(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .pointerenter, callback);
|
||||
}
|
||||
|
||||
pub fn getOnPointerEnter(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .pointerenter);
|
||||
}
|
||||
|
||||
pub fn setOnPointerLeave(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .pointerleave, callback);
|
||||
}
|
||||
|
||||
pub fn getOnPointerLeave(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .pointerleave);
|
||||
}
|
||||
|
||||
pub fn setOnPointerMove(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .pointermove, callback);
|
||||
}
|
||||
|
||||
pub fn getOnPointerMove(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .pointermove);
|
||||
}
|
||||
|
||||
pub fn setOnPointerOut(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .pointerout, callback);
|
||||
}
|
||||
|
||||
pub fn getOnPointerOut(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .pointerout);
|
||||
}
|
||||
|
||||
pub fn setOnPointerOver(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .pointerover, callback);
|
||||
}
|
||||
|
||||
pub fn getOnPointerOver(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .pointerover);
|
||||
}
|
||||
|
||||
pub fn setOnPointerRawUpdate(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .pointerrawupdate, callback);
|
||||
}
|
||||
|
||||
pub fn getOnPointerRawUpdate(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .pointerrawupdate);
|
||||
}
|
||||
|
||||
pub fn setOnPointerUp(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .pointerup, callback);
|
||||
}
|
||||
|
||||
pub fn getOnPointerUp(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .pointerup);
|
||||
}
|
||||
|
||||
pub fn setOnProgress(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .progress, callback);
|
||||
}
|
||||
|
||||
pub fn getOnProgress(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .progress);
|
||||
}
|
||||
|
||||
pub fn setOnRateChange(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .ratechange, callback);
|
||||
}
|
||||
|
||||
pub fn getOnRateChange(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .ratechange);
|
||||
}
|
||||
|
||||
pub fn setOnReset(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .reset, callback);
|
||||
}
|
||||
|
||||
pub fn getOnReset(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .reset);
|
||||
}
|
||||
|
||||
pub fn setOnResize(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .resize, callback);
|
||||
}
|
||||
|
||||
pub fn getOnResize(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .resize);
|
||||
}
|
||||
|
||||
pub fn setOnScroll(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .scroll, callback);
|
||||
}
|
||||
|
||||
pub fn getOnScroll(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .scroll);
|
||||
}
|
||||
|
||||
pub fn setOnScrollEnd(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .scrollend, callback);
|
||||
}
|
||||
|
||||
pub fn getOnScrollEnd(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .scrollend);
|
||||
}
|
||||
|
||||
pub fn setOnSecurityPolicyViolation(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .securitypolicyviolation, callback);
|
||||
}
|
||||
|
||||
pub fn getOnSecurityPolicyViolation(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .securitypolicyviolation);
|
||||
}
|
||||
|
||||
pub fn setOnSeeked(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .seeked, callback);
|
||||
}
|
||||
|
||||
pub fn getOnSeeked(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .seeked);
|
||||
}
|
||||
|
||||
pub fn setOnSeeking(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .seeking, callback);
|
||||
}
|
||||
|
||||
pub fn getOnSeeking(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .seeking);
|
||||
}
|
||||
|
||||
pub fn setOnSelect(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .select, callback);
|
||||
}
|
||||
|
||||
pub fn getOnSelect(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .select);
|
||||
}
|
||||
|
||||
pub fn setOnSelectionChange(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .selectionchange, callback);
|
||||
}
|
||||
|
||||
pub fn getOnSelectionChange(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .selectionchange);
|
||||
}
|
||||
|
||||
pub fn setOnSelectStart(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .selectstart, callback);
|
||||
}
|
||||
|
||||
pub fn getOnSelectStart(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .selectstart);
|
||||
}
|
||||
|
||||
pub fn setOnSlotChange(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .slotchange, callback);
|
||||
}
|
||||
|
||||
pub fn getOnSlotChange(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .slotchange);
|
||||
}
|
||||
|
||||
pub fn setOnStalled(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .stalled, callback);
|
||||
}
|
||||
|
||||
pub fn getOnStalled(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .stalled);
|
||||
}
|
||||
|
||||
pub fn setOnSubmit(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .submit, callback);
|
||||
}
|
||||
|
||||
pub fn getOnSubmit(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .submit);
|
||||
}
|
||||
|
||||
pub fn setOnSuspend(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .@"suspend", callback);
|
||||
}
|
||||
|
||||
pub fn getOnSuspend(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .@"suspend");
|
||||
}
|
||||
|
||||
pub fn setOnTimeUpdate(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .timeupdate, callback);
|
||||
}
|
||||
|
||||
pub fn getOnTimeUpdate(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .timeupdate);
|
||||
}
|
||||
|
||||
pub fn setOnToggle(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .toggle, callback);
|
||||
}
|
||||
|
||||
pub fn getOnToggle(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .toggle);
|
||||
}
|
||||
|
||||
pub fn setOnTransitionCancel(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .transitioncancel, callback);
|
||||
}
|
||||
|
||||
pub fn getOnTransitionCancel(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .transitioncancel);
|
||||
}
|
||||
|
||||
pub fn setOnTransitionEnd(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .transitionend, callback);
|
||||
}
|
||||
|
||||
pub fn getOnTransitionEnd(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .transitionend);
|
||||
}
|
||||
|
||||
pub fn setOnTransitionRun(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .transitionrun, callback);
|
||||
}
|
||||
|
||||
pub fn getOnTransitionRun(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .transitionrun);
|
||||
}
|
||||
|
||||
pub fn setOnTransitionStart(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .transitionstart, callback);
|
||||
}
|
||||
|
||||
pub fn getOnTransitionStart(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .transitionstart);
|
||||
}
|
||||
|
||||
pub fn setOnVolumeChange(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .volumechange, callback);
|
||||
}
|
||||
|
||||
pub fn getOnVolumeChange(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .volumechange);
|
||||
}
|
||||
|
||||
pub fn setOnWaiting(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .waiting, callback);
|
||||
}
|
||||
|
||||
pub fn getOnWaiting(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .waiting);
|
||||
}
|
||||
|
||||
pub fn setOnWheel(self: *HtmlElement, callback: js.Function.Global, page: *Page) !void {
|
||||
return page.setAttrListener(self.asElement(), .wheel, callback);
|
||||
}
|
||||
|
||||
pub fn getOnWheel(self: *HtmlElement, page: *Page) ?js.Function.Global {
|
||||
return page.getAttrListener(self.asElement(), .wheel);
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
pub const bridge = js.Bridge(HtmlElement);
|
||||
|
||||
@@ -349,6 +1112,102 @@ pub const JsApi = struct {
|
||||
}
|
||||
pub const insertAdjacentHTML = bridge.function(HtmlElement.insertAdjacentHTML, .{ .dom_exception = true });
|
||||
pub const click = bridge.function(HtmlElement.click, .{});
|
||||
|
||||
pub const onabort = bridge.accessor(HtmlElement.getOnAbort, HtmlElement.setOnAbort, .{});
|
||||
pub const onanimationcancel = bridge.accessor(HtmlElement.getOnAnimationCancel, HtmlElement.setOnAnimationCancel, .{});
|
||||
pub const onanimationend = bridge.accessor(HtmlElement.getOnAnimationEnd, HtmlElement.setOnAnimationEnd, .{});
|
||||
pub const onanimationiteration = bridge.accessor(HtmlElement.getOnAnimationIteration, HtmlElement.setOnAnimationIteration, .{});
|
||||
pub const onanimationstart = bridge.accessor(HtmlElement.getOnAnimationStart, HtmlElement.setOnAnimationStart, .{});
|
||||
pub const onauxclick = bridge.accessor(HtmlElement.getOnAuxClick, HtmlElement.setOnAuxClick, .{});
|
||||
pub const onbeforeinput = bridge.accessor(HtmlElement.getOnBeforeInput, HtmlElement.setOnBeforeInput, .{});
|
||||
pub const onbeforematch = bridge.accessor(HtmlElement.getOnBeforeMatch, HtmlElement.setOnBeforeMatch, .{});
|
||||
pub const onbeforetoggle = bridge.accessor(HtmlElement.getOnBeforeToggle, HtmlElement.setOnBeforeToggle, .{});
|
||||
pub const onblur = bridge.accessor(HtmlElement.getOnBlur, HtmlElement.setOnBlur, .{});
|
||||
pub const oncancel = bridge.accessor(HtmlElement.getOnCancel, HtmlElement.setOnCancel, .{});
|
||||
pub const oncanplay = bridge.accessor(HtmlElement.getOnCanPlay, HtmlElement.setOnCanPlay, .{});
|
||||
pub const oncanplaythrough = bridge.accessor(HtmlElement.getOnCanPlayThrough, HtmlElement.setOnCanPlayThrough, .{});
|
||||
pub const onchange = bridge.accessor(HtmlElement.getOnChange, HtmlElement.setOnChange, .{});
|
||||
pub const onclick = bridge.accessor(HtmlElement.getOnClick, HtmlElement.setOnClick, .{});
|
||||
pub const onclose = bridge.accessor(HtmlElement.getOnClose, HtmlElement.setOnClose, .{});
|
||||
pub const oncommand = bridge.accessor(HtmlElement.getOnCommand, HtmlElement.setOnCommand, .{});
|
||||
pub const oncontentvisibilityautostatechange = bridge.accessor(HtmlElement.getOnContentVisibilityAutoStateChange, HtmlElement.setOnContentVisibilityAutoStateChange, .{});
|
||||
pub const oncontextlost = bridge.accessor(HtmlElement.getOnContextLost, HtmlElement.setOnContextLost, .{});
|
||||
pub const oncontextmenu = bridge.accessor(HtmlElement.getOnContextMenu, HtmlElement.setOnContextMenu, .{});
|
||||
pub const oncontextrestored = bridge.accessor(HtmlElement.getOnContextRestored, HtmlElement.setOnContextRestored, .{});
|
||||
pub const oncopy = bridge.accessor(HtmlElement.getOnCopy, HtmlElement.setOnCopy, .{});
|
||||
pub const oncuechange = bridge.accessor(HtmlElement.getOnCueChange, HtmlElement.setOnCueChange, .{});
|
||||
pub const oncut = bridge.accessor(HtmlElement.getOnCut, HtmlElement.setOnCut, .{});
|
||||
pub const ondblclick = bridge.accessor(HtmlElement.getOnDblClick, HtmlElement.setOnDblClick, .{});
|
||||
pub const ondrag = bridge.accessor(HtmlElement.getOnDrag, HtmlElement.setOnDrag, .{});
|
||||
pub const ondragend = bridge.accessor(HtmlElement.getOnDragEnd, HtmlElement.setOnDragEnd, .{});
|
||||
pub const ondragenter = bridge.accessor(HtmlElement.getOnDragEnter, HtmlElement.setOnDragEnter, .{});
|
||||
pub const ondragexit = bridge.accessor(HtmlElement.getOnDragExit, HtmlElement.setOnDragExit, .{});
|
||||
pub const ondragleave = bridge.accessor(HtmlElement.getOnDragLeave, HtmlElement.setOnDragLeave, .{});
|
||||
pub const ondragover = bridge.accessor(HtmlElement.getOnDragOver, HtmlElement.setOnDragOver, .{});
|
||||
pub const ondragstart = bridge.accessor(HtmlElement.getOnDragStart, HtmlElement.setOnDragStart, .{});
|
||||
pub const ondrop = bridge.accessor(HtmlElement.getOnDrop, HtmlElement.setOnDrop, .{});
|
||||
pub const ondurationchange = bridge.accessor(HtmlElement.getOnDurationChange, HtmlElement.setOnDurationChange, .{});
|
||||
pub const onemptied = bridge.accessor(HtmlElement.getOnEmptied, HtmlElement.setOnEmptied, .{});
|
||||
pub const onended = bridge.accessor(HtmlElement.getOnEnded, HtmlElement.setOnEnded, .{});
|
||||
pub const onerror = bridge.accessor(HtmlElement.getOnError, HtmlElement.setOnError, .{});
|
||||
pub const onfocus = bridge.accessor(HtmlElement.getOnFocus, HtmlElement.setOnFocus, .{});
|
||||
pub const onformdata = bridge.accessor(HtmlElement.getOnFormData, HtmlElement.setOnFormData, .{});
|
||||
pub const onfullscreenchange = bridge.accessor(HtmlElement.getOnFullscreenChange, HtmlElement.setOnFullscreenChange, .{});
|
||||
pub const onfullscreenerror = bridge.accessor(HtmlElement.getOnFullscreenError, HtmlElement.setOnFullscreenError, .{});
|
||||
pub const ongotpointercapture = bridge.accessor(HtmlElement.getOnGotPointerCapture, HtmlElement.setOnGotPointerCapture, .{});
|
||||
pub const oninput = bridge.accessor(HtmlElement.getOnInput, HtmlElement.setOnInput, .{});
|
||||
pub const oninvalid = bridge.accessor(HtmlElement.getOnInvalid, HtmlElement.setOnInvalid, .{});
|
||||
pub const onkeydown = bridge.accessor(HtmlElement.getOnKeyDown, HtmlElement.setOnKeyDown, .{});
|
||||
pub const onkeypress = bridge.accessor(HtmlElement.getOnKeyPress, HtmlElement.setOnKeyPress, .{});
|
||||
pub const onkeyup = bridge.accessor(HtmlElement.getOnKeyUp, HtmlElement.setOnKeyUp, .{});
|
||||
pub const onload = bridge.accessor(HtmlElement.getOnLoad, HtmlElement.setOnLoad, .{});
|
||||
pub const onloadeddata = bridge.accessor(HtmlElement.getOnLoadedData, HtmlElement.setOnLoadedData, .{});
|
||||
pub const onloadedmetadata = bridge.accessor(HtmlElement.getOnLoadedMetadata, HtmlElement.setOnLoadedMetadata, .{});
|
||||
pub const onloadstart = bridge.accessor(HtmlElement.getOnLoadStart, HtmlElement.setOnLoadStart, .{});
|
||||
pub const onlostpointercapture = bridge.accessor(HtmlElement.getOnLostPointerCapture, HtmlElement.setOnLostPointerCapture, .{});
|
||||
pub const onmousedown = bridge.accessor(HtmlElement.getOnMouseDown, HtmlElement.setOnMouseDown, .{});
|
||||
pub const onmousemove = bridge.accessor(HtmlElement.getOnMouseMove, HtmlElement.setOnMouseMove, .{});
|
||||
pub const onmouseout = bridge.accessor(HtmlElement.getOnMouseOut, HtmlElement.setOnMouseOut, .{});
|
||||
pub const onmouseover = bridge.accessor(HtmlElement.getOnMouseOver, HtmlElement.setOnMouseOver, .{});
|
||||
pub const onmouseup = bridge.accessor(HtmlElement.getOnMouseUp, HtmlElement.setOnMouseUp, .{});
|
||||
pub const onpaste = bridge.accessor(HtmlElement.getOnPaste, HtmlElement.setOnPaste, .{});
|
||||
pub const onpause = bridge.accessor(HtmlElement.getOnPause, HtmlElement.setOnPause, .{});
|
||||
pub const onplay = bridge.accessor(HtmlElement.getOnPlay, HtmlElement.setOnPlay, .{});
|
||||
pub const onplaying = bridge.accessor(HtmlElement.getOnPlaying, HtmlElement.setOnPlaying, .{});
|
||||
pub const onpointercancel = bridge.accessor(HtmlElement.getOnPointerCancel, HtmlElement.setOnPointerCancel, .{});
|
||||
pub const onpointerdown = bridge.accessor(HtmlElement.getOnPointerDown, HtmlElement.setOnPointerDown, .{});
|
||||
pub const onpointerenter = bridge.accessor(HtmlElement.getOnPointerEnter, HtmlElement.setOnPointerEnter, .{});
|
||||
pub const onpointerleave = bridge.accessor(HtmlElement.getOnPointerLeave, HtmlElement.setOnPointerLeave, .{});
|
||||
pub const onpointermove = bridge.accessor(HtmlElement.getOnPointerMove, HtmlElement.setOnPointerMove, .{});
|
||||
pub const onpointerout = bridge.accessor(HtmlElement.getOnPointerOut, HtmlElement.setOnPointerOut, .{});
|
||||
pub const onpointerover = bridge.accessor(HtmlElement.getOnPointerOver, HtmlElement.setOnPointerOver, .{});
|
||||
pub const onpointerrawupdate = bridge.accessor(HtmlElement.getOnPointerRawUpdate, HtmlElement.setOnPointerRawUpdate, .{});
|
||||
pub const onpointerup = bridge.accessor(HtmlElement.getOnPointerUp, HtmlElement.setOnPointerUp, .{});
|
||||
pub const onprogress = bridge.accessor(HtmlElement.getOnProgress, HtmlElement.setOnProgress, .{});
|
||||
pub const onratechange = bridge.accessor(HtmlElement.getOnRateChange, HtmlElement.setOnRateChange, .{});
|
||||
pub const onreset = bridge.accessor(HtmlElement.getOnReset, HtmlElement.setOnReset, .{});
|
||||
pub const onresize = bridge.accessor(HtmlElement.getOnResize, HtmlElement.setOnResize, .{});
|
||||
pub const onscroll = bridge.accessor(HtmlElement.getOnScroll, HtmlElement.setOnScroll, .{});
|
||||
pub const onscrollend = bridge.accessor(HtmlElement.getOnScrollEnd, HtmlElement.setOnScrollEnd, .{});
|
||||
pub const onsecuritypolicyviolation = bridge.accessor(HtmlElement.getOnSecurityPolicyViolation, HtmlElement.setOnSecurityPolicyViolation, .{});
|
||||
pub const onseeked = bridge.accessor(HtmlElement.getOnSeeked, HtmlElement.setOnSeeked, .{});
|
||||
pub const onseeking = bridge.accessor(HtmlElement.getOnSeeking, HtmlElement.setOnSeeking, .{});
|
||||
pub const onselect = bridge.accessor(HtmlElement.getOnSelect, HtmlElement.setOnSelect, .{});
|
||||
pub const onselectionchange = bridge.accessor(HtmlElement.getOnSelectionChange, HtmlElement.setOnSelectionChange, .{});
|
||||
pub const onselectstart = bridge.accessor(HtmlElement.getOnSelectStart, HtmlElement.setOnSelectStart, .{});
|
||||
pub const onslotchange = bridge.accessor(HtmlElement.getOnSlotChange, HtmlElement.setOnSlotChange, .{});
|
||||
pub const onstalled = bridge.accessor(HtmlElement.getOnStalled, HtmlElement.setOnStalled, .{});
|
||||
pub const onsubmit = bridge.accessor(HtmlElement.getOnSubmit, HtmlElement.setOnSubmit, .{});
|
||||
pub const onsuspend = bridge.accessor(HtmlElement.getOnSuspend, HtmlElement.setOnSuspend, .{});
|
||||
pub const ontimeupdate = bridge.accessor(HtmlElement.getOnTimeUpdate, HtmlElement.setOnTimeUpdate, .{});
|
||||
pub const ontoggle = bridge.accessor(HtmlElement.getOnToggle, HtmlElement.setOnToggle, .{});
|
||||
pub const ontransitioncancel = bridge.accessor(HtmlElement.getOnTransitionCancel, HtmlElement.setOnTransitionCancel, .{});
|
||||
pub const ontransitionend = bridge.accessor(HtmlElement.getOnTransitionEnd, HtmlElement.setOnTransitionEnd, .{});
|
||||
pub const ontransitionrun = bridge.accessor(HtmlElement.getOnTransitionRun, HtmlElement.setOnTransitionRun, .{});
|
||||
pub const ontransitionstart = bridge.accessor(HtmlElement.getOnTransitionStart, HtmlElement.setOnTransitionStart, .{});
|
||||
pub const onvolumechange = bridge.accessor(HtmlElement.getOnVolumeChange, HtmlElement.setOnVolumeChange, .{});
|
||||
pub const onwaiting = bridge.accessor(HtmlElement.getOnWaiting, HtmlElement.setOnWaiting, .{});
|
||||
pub const onwheel = bridge.accessor(HtmlElement.getOnWheel, HtmlElement.setOnWheel, .{});
|
||||
};
|
||||
|
||||
pub const Build = struct {
|
||||
@@ -379,3 +1238,8 @@ pub const Build = struct {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const testing = @import("../../../testing.zig");
|
||||
test "WebApi: HTML.event_listeners" {
|
||||
try testing.htmlRunner("element/html/event_listeners.html", .{});
|
||||
}
|
||||
|
||||
@@ -25,8 +25,9 @@ const Allocator = std.mem.Allocator;
|
||||
const TextDecoder = @This();
|
||||
|
||||
_fatal: bool,
|
||||
_ignore_bom: bool,
|
||||
_page: *Page,
|
||||
_arena: Allocator,
|
||||
_ignore_bom: bool,
|
||||
_stream: std.ArrayListUnmanaged(u8),
|
||||
|
||||
const Label = enum {
|
||||
@@ -45,13 +46,23 @@ pub fn init(label_: ?[]const u8, opts_: ?InitOpts, page: *Page) !*TextDecoder {
|
||||
_ = std.meta.stringToEnum(Label, label) orelse return error.RangeError;
|
||||
}
|
||||
|
||||
const arena = try page.getArena(.{ .debug = "TextDecoder" });
|
||||
errdefer page.releaseArena(arena);
|
||||
|
||||
const opts = opts_ orelse InitOpts{};
|
||||
return page._factory.create(TextDecoder{
|
||||
._arena = page.arena,
|
||||
const self = try arena.create(TextDecoder);
|
||||
self.* = .{
|
||||
._page = page,
|
||||
._arena = arena,
|
||||
._stream = .empty,
|
||||
._fatal = opts.fatal,
|
||||
._ignore_bom = opts.ignoreBOM,
|
||||
});
|
||||
};
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *TextDecoder, _: bool) void {
|
||||
self._page.releaseArena(self._arena);
|
||||
}
|
||||
|
||||
pub fn getEncoding(_: *const TextDecoder) []const u8 {
|
||||
@@ -103,6 +114,8 @@ pub const JsApi = struct {
|
||||
pub const name = "TextDecoder";
|
||||
pub const prototype_chain = bridge.prototypeChain();
|
||||
pub var class_id: bridge.ClassId = undefined;
|
||||
pub const weak = true;
|
||||
pub const finalizer = bridge.finalizer(TextDecoder.deinit);
|
||||
};
|
||||
|
||||
pub const constructor = bridge.constructor(TextDecoder.init, .{});
|
||||
|
||||
@@ -424,6 +424,11 @@ pub fn BrowserContext(comptime CDP_T: type) type {
|
||||
// in progress before deinit.
|
||||
self.cdp.browser.env.runMicrotasks();
|
||||
|
||||
// resetContextGroup detach the inspector from all contexts.
|
||||
// It append async tasks, so we make sure we run the message loop
|
||||
// before deinit it.
|
||||
self.inspector.resetContextGroup();
|
||||
self.session.browser.runMessageLoop();
|
||||
self.inspector.deinit();
|
||||
|
||||
// abort all intercepted requests before closing the sesion/page
|
||||
|
||||
Reference in New Issue
Block a user