mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-03-22 04:34:44 +00:00
Merge pull request #1417 from lightpanda-io/observer_arenas
Leverage finalizers and ArenaPool in Intersction and Mutation Observer
This commit is contained in:
@@ -244,6 +244,18 @@ pub fn weakRef(self: *Context, obj: anytype) void {
|
|||||||
v8.v8__Global__SetWeakFinalizer(global, obj, bridge.Struct(@TypeOf(obj)).JsApi.Meta.finalizer.from_v8, v8.kParameter);
|
v8.v8__Global__SetWeakFinalizer(global, obj, bridge.Struct(@TypeOf(obj)).JsApi.Meta.finalizer.from_v8, v8.kParameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn safeWeakRef(self: *Context, obj: anytype) void {
|
||||||
|
const global = self.identity_map.getPtr(@intFromPtr(obj)) orelse {
|
||||||
|
if (comptime IS_DEBUG) {
|
||||||
|
// should not be possible
|
||||||
|
std.debug.assert(false);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
v8.v8__Global__ClearWeak(global);
|
||||||
|
v8.v8__Global__SetWeakFinalizer(global, obj, bridge.Struct(@TypeOf(obj)).JsApi.Meta.finalizer.from_v8, v8.kParameter);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn strongRef(self: *Context, obj: anytype) void {
|
pub fn strongRef(self: *Context, obj: anytype) void {
|
||||||
const global = self.identity_map.getPtr(@intFromPtr(obj)) orelse {
|
const global = self.identity_map.getPtr(@intFromPtr(obj)) orelse {
|
||||||
if (comptime IS_DEBUG) {
|
if (comptime IS_DEBUG) {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright (C) 2023-2025 Lightpanda (Selecy SAS)
|
// Copyright (C) 2023-2026 Lightpanda (Selecy SAS)
|
||||||
//
|
//
|
||||||
// Francis Bouvier <francis@lightpanda.io>
|
// Francis Bouvier <francis@lightpanda.io>
|
||||||
// Pierre Tachoire <pierre@lightpanda.io>
|
// Pierre Tachoire <pierre@lightpanda.io>
|
||||||
@@ -19,6 +19,10 @@ const std = @import("std");
|
|||||||
const js = @import("../js/js.zig");
|
const js = @import("../js/js.zig");
|
||||||
const log = @import("../../log.zig");
|
const log = @import("../../log.zig");
|
||||||
|
|
||||||
|
const IS_DEBUG = @import("builtin").mode == .Debug;
|
||||||
|
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
const Page = @import("../Page.zig");
|
const Page = @import("../Page.zig");
|
||||||
const Element = @import("Element.zig");
|
const Element = @import("Element.zig");
|
||||||
const DOMRect = @import("DOMRect.zig");
|
const DOMRect = @import("DOMRect.zig");
|
||||||
@@ -32,7 +36,9 @@ pub fn registerTypes() []const type {
|
|||||||
|
|
||||||
const IntersectionObserver = @This();
|
const IntersectionObserver = @This();
|
||||||
|
|
||||||
_callback: js.Function.Global,
|
_page: *Page,
|
||||||
|
_arena: Allocator,
|
||||||
|
_callback: js.Function.Temp,
|
||||||
_observing: std.ArrayList(*Element) = .{},
|
_observing: std.ArrayList(*Element) = .{},
|
||||||
_root: ?*Element = null,
|
_root: ?*Element = null,
|
||||||
_root_margin: []const u8 = "0px",
|
_root_margin: []const u8 = "0px",
|
||||||
@@ -59,25 +65,42 @@ pub const ObserverInit = struct {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn init(callback: js.Function.Global, options: ?ObserverInit, page: *Page) !*IntersectionObserver {
|
pub fn init(callback: js.Function.Temp, options: ?ObserverInit, page: *Page) !*IntersectionObserver {
|
||||||
|
const arena = try page.getArena(.{ .debug = "IntersectionObserver" });
|
||||||
|
errdefer page.releaseArena(arena);
|
||||||
|
|
||||||
const opts = options orelse ObserverInit{};
|
const opts = options orelse ObserverInit{};
|
||||||
const root_margin = if (opts.rootMargin) |rm| try page.arena.dupe(u8, rm) else "0px";
|
const root_margin = if (opts.rootMargin) |rm| try arena.dupe(u8, rm) else "0px";
|
||||||
|
|
||||||
const threshold = switch (opts.threshold) {
|
const threshold = switch (opts.threshold) {
|
||||||
.scalar => |s| blk: {
|
.scalar => |s| blk: {
|
||||||
const arr = try page.arena.alloc(f64, 1);
|
const arr = try arena.alloc(f64, 1);
|
||||||
arr[0] = s;
|
arr[0] = s;
|
||||||
break :blk arr;
|
break :blk arr;
|
||||||
},
|
},
|
||||||
.array => |arr| try page.arena.dupe(f64, arr),
|
.array => |arr| try arena.dupe(f64, arr),
|
||||||
};
|
};
|
||||||
|
|
||||||
return page._factory.create(IntersectionObserver{
|
const self = try arena.create(IntersectionObserver);
|
||||||
|
self.* = .{
|
||||||
|
._page = page,
|
||||||
|
._arena = arena,
|
||||||
._callback = callback,
|
._callback = callback,
|
||||||
._root = opts.root,
|
._root = opts.root,
|
||||||
._root_margin = root_margin,
|
._root_margin = root_margin,
|
||||||
._threshold = threshold,
|
._threshold = threshold,
|
||||||
});
|
};
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *IntersectionObserver, shutdown: bool) void {
|
||||||
|
const page = self._page;
|
||||||
|
page.js.release(self._callback);
|
||||||
|
if ((comptime IS_DEBUG) and !shutdown) {
|
||||||
|
std.debug.assert(self._observing.items.len == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
page.releaseArena(self._arena);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn observe(self: *IntersectionObserver, target: *Element, page: *Page) !void {
|
pub fn observe(self: *IntersectionObserver, target: *Element, page: *Page) !void {
|
||||||
@@ -90,10 +113,11 @@ pub fn observe(self: *IntersectionObserver, target: *Element, page: *Page) !void
|
|||||||
|
|
||||||
// Register with page if this is our first observation
|
// Register with page if this is our first observation
|
||||||
if (self._observing.items.len == 0) {
|
if (self._observing.items.len == 0) {
|
||||||
|
page.js.strongRef(self);
|
||||||
try page.registerIntersectionObserver(self);
|
try page.registerIntersectionObserver(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
try self._observing.append(page.arena, target);
|
try self._observing.append(self._arena, target);
|
||||||
|
|
||||||
// Don't initialize previous state yet - let checkIntersection do it
|
// Don't initialize previous state yet - let checkIntersection do it
|
||||||
// This ensures we get an entry on first observation
|
// This ensures we get an entry on first observation
|
||||||
@@ -105,7 +129,7 @@ pub fn observe(self: *IntersectionObserver, target: *Element, page: *Page) !void
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unobserve(self: *IntersectionObserver, target: *Element) void {
|
pub fn unobserve(self: *IntersectionObserver, target: *Element, page: *Page) void {
|
||||||
for (self._observing.items, 0..) |elem, i| {
|
for (self._observing.items, 0..) |elem, i| {
|
||||||
if (elem == target) {
|
if (elem == target) {
|
||||||
_ = self._observing.swapRemove(i);
|
_ = self._observing.swapRemove(i);
|
||||||
@@ -115,21 +139,31 @@ pub fn unobserve(self: *IntersectionObserver, target: *Element) void {
|
|||||||
var j: usize = 0;
|
var j: usize = 0;
|
||||||
while (j < self._pending_entries.items.len) {
|
while (j < self._pending_entries.items.len) {
|
||||||
if (self._pending_entries.items[j]._target == target) {
|
if (self._pending_entries.items[j]._target == target) {
|
||||||
_ = self._pending_entries.swapRemove(j);
|
const entry = self._pending_entries.swapRemove(j);
|
||||||
|
entry.deinit(false);
|
||||||
} else {
|
} else {
|
||||||
j += 1;
|
j += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (self._observing.items.len == 0) {
|
||||||
|
page.js.safeWeakRef(self);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn disconnect(self: *IntersectionObserver, page: *Page) void {
|
pub fn disconnect(self: *IntersectionObserver, page: *Page) void {
|
||||||
page.unregisterIntersectionObserver(self);
|
page.unregisterIntersectionObserver(self);
|
||||||
self._observing.clearRetainingCapacity();
|
self._observing.clearRetainingCapacity();
|
||||||
self._previous_states.clearRetainingCapacity();
|
self._previous_states.clearRetainingCapacity();
|
||||||
|
|
||||||
|
for (self._pending_entries.items) |entry| {
|
||||||
|
entry.deinit(false);
|
||||||
|
}
|
||||||
self._pending_entries.clearRetainingCapacity();
|
self._pending_entries.clearRetainingCapacity();
|
||||||
|
page.js.safeWeakRef(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn takeRecords(self: *IntersectionObserver, page: *Page) ![]*IntersectionObserverEntry {
|
pub fn takeRecords(self: *IntersectionObserver, page: *Page) ![]*IntersectionObserverEntry {
|
||||||
@@ -206,8 +240,11 @@ fn checkIntersection(self: *IntersectionObserver, target: *Element, page: *Page)
|
|||||||
(was_intersecting_opt != null and was_intersecting_opt.? != is_now_intersecting);
|
(was_intersecting_opt != null and was_intersecting_opt.? != is_now_intersecting);
|
||||||
|
|
||||||
if (should_report) {
|
if (should_report) {
|
||||||
const entry = try page.arena.create(IntersectionObserverEntry);
|
const arena = try page.getArena(.{ .debug = "IntersectionObserverEntry" });
|
||||||
|
const entry = try arena.create(IntersectionObserverEntry);
|
||||||
entry.* = .{
|
entry.* = .{
|
||||||
|
._page = page,
|
||||||
|
._arena = arena,
|
||||||
._target = target,
|
._target = target,
|
||||||
._time = 0.0, // TODO: Get actual timestamp
|
._time = 0.0, // TODO: Get actual timestamp
|
||||||
._bounding_client_rect = data.bounding_client_rect,
|
._bounding_client_rect = data.bounding_client_rect,
|
||||||
@@ -217,12 +254,12 @@ fn checkIntersection(self: *IntersectionObserver, target: *Element, page: *Page)
|
|||||||
._is_intersecting = is_now_intersecting,
|
._is_intersecting = is_now_intersecting,
|
||||||
};
|
};
|
||||||
|
|
||||||
try self._pending_entries.append(page.arena, entry);
|
try self._pending_entries.append(self._arena, entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always update the previous state, even if we didn't report
|
// Always update the previous state, even if we didn't report
|
||||||
// This ensures we can detect state changes on subsequent checks
|
// This ensures we can detect state changes on subsequent checks
|
||||||
try self._previous_states.put(page.arena, target, is_now_intersecting);
|
try self._previous_states.put(self._arena, target, is_now_intersecting);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn checkIntersections(self: *IntersectionObserver, page: *Page) !void {
|
pub fn checkIntersections(self: *IntersectionObserver, page: *Page) !void {
|
||||||
@@ -258,14 +295,20 @@ pub fn deliverEntries(self: *IntersectionObserver, page: *Page) !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub const IntersectionObserverEntry = struct {
|
pub const IntersectionObserverEntry = struct {
|
||||||
_target: *Element,
|
_page: *Page,
|
||||||
|
_arena: Allocator,
|
||||||
_time: f64,
|
_time: f64,
|
||||||
|
_target: *Element,
|
||||||
_bounding_client_rect: *DOMRect,
|
_bounding_client_rect: *DOMRect,
|
||||||
_intersection_rect: *DOMRect,
|
_intersection_rect: *DOMRect,
|
||||||
_root_bounds: *DOMRect,
|
_root_bounds: *DOMRect,
|
||||||
_intersection_ratio: f64,
|
_intersection_ratio: f64,
|
||||||
_is_intersecting: bool,
|
_is_intersecting: bool,
|
||||||
|
|
||||||
|
pub fn deinit(self: *const IntersectionObserverEntry, _: bool) void {
|
||||||
|
self._page.releaseArena(self._arena);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn getTarget(self: *const IntersectionObserverEntry) *Element {
|
pub fn getTarget(self: *const IntersectionObserverEntry) *Element {
|
||||||
return self._target;
|
return self._target;
|
||||||
}
|
}
|
||||||
@@ -301,6 +344,8 @@ pub const IntersectionObserverEntry = struct {
|
|||||||
pub const name = "IntersectionObserverEntry";
|
pub const name = "IntersectionObserverEntry";
|
||||||
pub const prototype_chain = bridge.prototypeChain();
|
pub const prototype_chain = bridge.prototypeChain();
|
||||||
pub var class_id: bridge.ClassId = undefined;
|
pub var class_id: bridge.ClassId = undefined;
|
||||||
|
pub const weak = true;
|
||||||
|
pub const finalizer = bridge.finalizer(IntersectionObserverEntry.deinit);
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const target = bridge.accessor(IntersectionObserverEntry.getTarget, null, .{});
|
pub const target = bridge.accessor(IntersectionObserverEntry.getTarget, null, .{});
|
||||||
@@ -320,6 +365,8 @@ pub const JsApi = struct {
|
|||||||
pub const name = "IntersectionObserver";
|
pub const name = "IntersectionObserver";
|
||||||
pub const prototype_chain = bridge.prototypeChain();
|
pub const prototype_chain = bridge.prototypeChain();
|
||||||
pub var class_id: bridge.ClassId = undefined;
|
pub var class_id: bridge.ClassId = undefined;
|
||||||
|
pub const weak = true;
|
||||||
|
pub const finalizer = bridge.finalizer(IntersectionObserver.deinit);
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const constructor = bridge.constructor(init, .{});
|
pub const constructor = bridge.constructor(init, .{});
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright (C) 2023-2025 Lightpanda (Selecy SAS)
|
// Copyright (C) 2023-2026 Lightpanda (Selecy SAS)
|
||||||
//
|
//
|
||||||
// Francis Bouvier <francis@lightpanda.io>
|
// Francis Bouvier <francis@lightpanda.io>
|
||||||
// Pierre Tachoire <pierre@lightpanda.io>
|
// Pierre Tachoire <pierre@lightpanda.io>
|
||||||
@@ -25,6 +25,10 @@ const Node = @import("Node.zig");
|
|||||||
const Element = @import("Element.zig");
|
const Element = @import("Element.zig");
|
||||||
const log = @import("../../log.zig");
|
const log = @import("../../log.zig");
|
||||||
|
|
||||||
|
const IS_DEBUG = @import("builtin").mode == .Debug;
|
||||||
|
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
pub fn registerTypes() []const type {
|
pub fn registerTypes() []const type {
|
||||||
return &.{
|
return &.{
|
||||||
MutationObserver,
|
MutationObserver,
|
||||||
@@ -34,9 +38,12 @@ pub fn registerTypes() []const type {
|
|||||||
|
|
||||||
const MutationObserver = @This();
|
const MutationObserver = @This();
|
||||||
|
|
||||||
_callback: js.Function.Global,
|
_page: *Page,
|
||||||
|
_arena: Allocator,
|
||||||
|
_callback: js.Function.Temp,
|
||||||
_observing: std.ArrayList(Observing) = .{},
|
_observing: std.ArrayList(Observing) = .{},
|
||||||
_pending_records: std.ArrayList(*MutationRecord) = .{},
|
_pending_records: std.ArrayList(*MutationRecord) = .{},
|
||||||
|
|
||||||
/// Intrusively linked to next element (see Page.zig).
|
/// Intrusively linked to next element (see Page.zig).
|
||||||
node: std.DoublyLinkedList.Node = .{},
|
node: std.DoublyLinkedList.Node = .{},
|
||||||
|
|
||||||
@@ -55,19 +62,38 @@ pub const ObserveOptions = struct {
|
|||||||
attributeFilter: ?[]const []const u8 = null,
|
attributeFilter: ?[]const []const u8 = null,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn init(callback: js.Function.Global, page: *Page) !*MutationObserver {
|
pub fn init(callback: js.Function.Temp, page: *Page) !*MutationObserver {
|
||||||
return page._factory.create(MutationObserver{
|
const arena = try page.getArena(.{ .debug = "MutationObserver" });
|
||||||
|
errdefer page.releaseArena(arena);
|
||||||
|
|
||||||
|
const self = try arena.create(MutationObserver);
|
||||||
|
self.* = .{
|
||||||
|
._page = page,
|
||||||
|
._arena = arena,
|
||||||
._callback = callback,
|
._callback = callback,
|
||||||
});
|
};
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *MutationObserver, shutdown: bool) void {
|
||||||
|
const page = self._page;
|
||||||
|
page.js.release(self._callback);
|
||||||
|
if ((comptime IS_DEBUG) and !shutdown) {
|
||||||
|
std.debug.assert(self._observing.items.len == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
page.releaseArena(self._arena);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn observe(self: *MutationObserver, target: *Node, options: ObserveOptions, page: *Page) !void {
|
pub fn observe(self: *MutationObserver, target: *Node, options: ObserveOptions, page: *Page) !void {
|
||||||
|
const arena = self._arena;
|
||||||
|
|
||||||
// Deep copy attributeFilter if present
|
// Deep copy attributeFilter if present
|
||||||
var copied_options = options;
|
var copied_options = options;
|
||||||
if (options.attributeFilter) |filter| {
|
if (options.attributeFilter) |filter| {
|
||||||
const filter_copy = try page.arena.alloc([]const u8, filter.len);
|
const filter_copy = try arena.alloc([]const u8, filter.len);
|
||||||
for (filter, 0..) |name, i| {
|
for (filter, 0..) |name, i| {
|
||||||
filter_copy[i] = try page.arena.dupe(u8, name);
|
filter_copy[i] = try arena.dupe(u8, name);
|
||||||
}
|
}
|
||||||
copied_options.attributeFilter = filter_copy;
|
copied_options.attributeFilter = filter_copy;
|
||||||
}
|
}
|
||||||
@@ -86,10 +112,11 @@ pub fn observe(self: *MutationObserver, target: *Node, options: ObserveOptions,
|
|||||||
|
|
||||||
// Register with page if this is our first observation
|
// Register with page if this is our first observation
|
||||||
if (self._observing.items.len == 0) {
|
if (self._observing.items.len == 0) {
|
||||||
|
page.js.strongRef(self);
|
||||||
try page.registerMutationObserver(self);
|
try page.registerMutationObserver(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
try self._observing.append(page.arena, .{
|
try self._observing.append(arena, .{
|
||||||
.target = target,
|
.target = target,
|
||||||
.options = copied_options,
|
.options = copied_options,
|
||||||
});
|
});
|
||||||
@@ -98,7 +125,11 @@ pub fn observe(self: *MutationObserver, target: *Node, options: ObserveOptions,
|
|||||||
pub fn disconnect(self: *MutationObserver, page: *Page) void {
|
pub fn disconnect(self: *MutationObserver, page: *Page) void {
|
||||||
page.unregisterMutationObserver(self);
|
page.unregisterMutationObserver(self);
|
||||||
self._observing.clearRetainingCapacity();
|
self._observing.clearRetainingCapacity();
|
||||||
|
for (self._pending_records.items) |record| {
|
||||||
|
record.deinit(false);
|
||||||
|
}
|
||||||
self._pending_records.clearRetainingCapacity();
|
self._pending_records.clearRetainingCapacity();
|
||||||
|
page.js.safeWeakRef(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn takeRecords(self: *MutationObserver, page: *Page) ![]*MutationRecord {
|
pub fn takeRecords(self: *MutationObserver, page: *Page) ![]*MutationRecord {
|
||||||
@@ -139,21 +170,25 @@ pub fn notifyAttributeChange(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const record = try page._factory.create(MutationRecord{
|
const arena = try self._page.getArena(.{ .debug = "MutationRecord" });
|
||||||
|
const record = try arena.create(MutationRecord);
|
||||||
|
record.* = .{
|
||||||
|
._page = page,
|
||||||
|
._arena = arena,
|
||||||
._type = .attributes,
|
._type = .attributes,
|
||||||
._target = target_node,
|
._target = target_node,
|
||||||
._attribute_name = try page.arena.dupe(u8, attribute_name.str()),
|
._attribute_name = try arena.dupe(u8, attribute_name.str()),
|
||||||
._old_value = if (obs.options.attributeOldValue and old_value != null)
|
._old_value = if (obs.options.attributeOldValue and old_value != null)
|
||||||
try page.arena.dupe(u8, old_value.?.str())
|
try arena.dupe(u8, old_value.?.str())
|
||||||
else
|
else
|
||||||
null,
|
null,
|
||||||
._added_nodes = &.{},
|
._added_nodes = &.{},
|
||||||
._removed_nodes = &.{},
|
._removed_nodes = &.{},
|
||||||
._previous_sibling = null,
|
._previous_sibling = null,
|
||||||
._next_sibling = null,
|
._next_sibling = null,
|
||||||
});
|
};
|
||||||
|
|
||||||
try self._pending_records.append(page.arena, record);
|
try self._pending_records.append(self._arena, record);
|
||||||
|
|
||||||
try page.scheduleMutationDelivery();
|
try page.scheduleMutationDelivery();
|
||||||
break;
|
break;
|
||||||
@@ -180,21 +215,25 @@ pub fn notifyCharacterDataChange(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const record = try page._factory.create(MutationRecord{
|
const arena = try self._page.getArena(.{ .debug = "MutationRecord" });
|
||||||
|
const record = try arena.create(MutationRecord);
|
||||||
|
record.* = .{
|
||||||
|
._page = page,
|
||||||
|
._arena = arena,
|
||||||
._type = .characterData,
|
._type = .characterData,
|
||||||
._target = target,
|
._target = target,
|
||||||
._attribute_name = null,
|
._attribute_name = null,
|
||||||
._old_value = if (obs.options.characterDataOldValue and old_value != null)
|
._old_value = if (obs.options.characterDataOldValue and old_value != null)
|
||||||
try page.arena.dupe(u8, old_value.?)
|
try arena.dupe(u8, old_value.?)
|
||||||
else
|
else
|
||||||
null,
|
null,
|
||||||
._added_nodes = &.{},
|
._added_nodes = &.{},
|
||||||
._removed_nodes = &.{},
|
._removed_nodes = &.{},
|
||||||
._previous_sibling = null,
|
._previous_sibling = null,
|
||||||
._next_sibling = null,
|
._next_sibling = null,
|
||||||
});
|
};
|
||||||
|
|
||||||
try self._pending_records.append(page.arena, record);
|
try self._pending_records.append(self._arena, record);
|
||||||
|
|
||||||
try page.scheduleMutationDelivery();
|
try page.scheduleMutationDelivery();
|
||||||
break;
|
break;
|
||||||
@@ -224,18 +263,22 @@ pub fn notifyChildListChange(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const record = try page._factory.create(MutationRecord{
|
const arena = try self._page.getArena(.{ .debug = "MutationRecord" });
|
||||||
|
const record = try arena.create(MutationRecord);
|
||||||
|
record.* = .{
|
||||||
|
._page = page,
|
||||||
|
._arena = arena,
|
||||||
._type = .childList,
|
._type = .childList,
|
||||||
._target = target,
|
._target = target,
|
||||||
._attribute_name = null,
|
._attribute_name = null,
|
||||||
._old_value = null,
|
._old_value = null,
|
||||||
._added_nodes = try page.arena.dupe(*Node, added_nodes),
|
._added_nodes = try arena.dupe(*Node, added_nodes),
|
||||||
._removed_nodes = try page.arena.dupe(*Node, removed_nodes),
|
._removed_nodes = try arena.dupe(*Node, removed_nodes),
|
||||||
._previous_sibling = previous_sibling,
|
._previous_sibling = previous_sibling,
|
||||||
._next_sibling = next_sibling,
|
._next_sibling = next_sibling,
|
||||||
});
|
};
|
||||||
|
|
||||||
try self._pending_records.append(page.arena, record);
|
try self._pending_records.append(self._arena, record);
|
||||||
|
|
||||||
try page.scheduleMutationDelivery();
|
try page.scheduleMutationDelivery();
|
||||||
break;
|
break;
|
||||||
@@ -263,7 +306,9 @@ pub fn deliverRecords(self: *MutationObserver, page: *Page) !void {
|
|||||||
|
|
||||||
pub const MutationRecord = struct {
|
pub const MutationRecord = struct {
|
||||||
_type: Type,
|
_type: Type,
|
||||||
|
_page: *Page,
|
||||||
_target: *Node,
|
_target: *Node,
|
||||||
|
_arena: Allocator,
|
||||||
_attribute_name: ?[]const u8,
|
_attribute_name: ?[]const u8,
|
||||||
_old_value: ?[]const u8,
|
_old_value: ?[]const u8,
|
||||||
_added_nodes: []const *Node,
|
_added_nodes: []const *Node,
|
||||||
@@ -277,6 +322,10 @@ pub const MutationRecord = struct {
|
|||||||
characterData,
|
characterData,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub fn deinit(self: *const MutationRecord, _: bool) void {
|
||||||
|
self._page.releaseArena(self._arena);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn getType(self: *const MutationRecord) []const u8 {
|
pub fn getType(self: *const MutationRecord) []const u8 {
|
||||||
return switch (self._type) {
|
return switch (self._type) {
|
||||||
.attributes => "attributes",
|
.attributes => "attributes",
|
||||||
@@ -327,6 +376,8 @@ pub const MutationRecord = struct {
|
|||||||
pub const name = "MutationRecord";
|
pub const name = "MutationRecord";
|
||||||
pub const prototype_chain = bridge.prototypeChain();
|
pub const prototype_chain = bridge.prototypeChain();
|
||||||
pub var class_id: bridge.ClassId = undefined;
|
pub var class_id: bridge.ClassId = undefined;
|
||||||
|
pub const weak = true;
|
||||||
|
pub const finalizer = bridge.finalizer(MutationRecord.deinit);
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const @"type" = bridge.accessor(MutationRecord.getType, null, .{});
|
pub const @"type" = bridge.accessor(MutationRecord.getType, null, .{});
|
||||||
@@ -348,6 +399,8 @@ pub const JsApi = struct {
|
|||||||
pub const name = "MutationObserver";
|
pub const name = "MutationObserver";
|
||||||
pub const prototype_chain = bridge.prototypeChain();
|
pub const prototype_chain = bridge.prototypeChain();
|
||||||
pub var class_id: bridge.ClassId = undefined;
|
pub var class_id: bridge.ClassId = undefined;
|
||||||
|
pub const weak = true;
|
||||||
|
pub const finalizer = bridge.finalizer(MutationObserver.deinit);
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const constructor = bridge.constructor(MutationObserver.init, .{});
|
pub const constructor = bridge.constructor(MutationObserver.init, .{});
|
||||||
|
|||||||
Reference in New Issue
Block a user