mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-12-15 15:58:57 +00:00
explicitly run microtasks
This commit is contained in:
@@ -107,12 +107,19 @@ pub fn dispatch(self: *EventManager, target: *EventTarget, event: *Event) !void
|
||||
if (comptime IS_DEBUG) {
|
||||
log.debug(.event, "eventManager.dispatch", .{ .type = event._type_string.str(), .bubbles = event._bubbles });
|
||||
}
|
||||
|
||||
event._target = target;
|
||||
var was_handled = false;
|
||||
|
||||
defer if (was_handled) {
|
||||
self.page.js.runMicrotasks();
|
||||
};
|
||||
|
||||
switch (target._type) {
|
||||
.node => |node| try self.dispatchNode(node, event),
|
||||
.node => |node| try self.dispatchNode(node, event, &was_handled),
|
||||
.xhr, .window, .abort_signal, .media_query_list => {
|
||||
const list = self.lookup.getPtr(@intFromPtr(target)) orelse return;
|
||||
try self.dispatchAll(list, target, event);
|
||||
try self.dispatchAll(list, target, event, &was_handled);
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -135,19 +142,26 @@ pub fn dispatchWithFunction(self: *EventManager, target: *EventTarget, event: *E
|
||||
event._target = target;
|
||||
}
|
||||
|
||||
var was_dispatched = false;
|
||||
defer if (was_dispatched) {
|
||||
self.page.js.runMicrotasks();
|
||||
};
|
||||
|
||||
if (function_) |func| {
|
||||
event._current_target = target;
|
||||
func.call(void, .{event}) catch |err| {
|
||||
if (func.call(void, .{event})) {
|
||||
was_dispatched = true;
|
||||
} else |err| {
|
||||
// a non-JS error
|
||||
log.warn(.event, opts.context, .{ .err = err });
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const list = self.lookup.getPtr(@intFromPtr(target)) orelse return;
|
||||
try self.dispatchAll(list, target, event);
|
||||
try self.dispatchAll(list, target, event, &was_dispatched);
|
||||
}
|
||||
|
||||
fn dispatchNode(self: *EventManager, target: *Node, event: *Event) !void {
|
||||
fn dispatchNode(self: *EventManager, target: *Node, event: *Event, was_handled: *bool) !void {
|
||||
var path_len: usize = 0;
|
||||
var path_buffer: [128]*EventTarget = undefined;
|
||||
|
||||
@@ -175,7 +189,7 @@ fn dispatchNode(self: *EventManager, target: *Node, event: *Event) !void {
|
||||
i -= 1;
|
||||
const current_target = path[i];
|
||||
if (self.lookup.getPtr(@intFromPtr(current_target))) |list| {
|
||||
try self.dispatchPhase(list, current_target, event, true);
|
||||
try self.dispatchPhase(list, current_target, event, was_handled, true);
|
||||
if (event._stop_propagation) {
|
||||
event._event_phase = .none;
|
||||
return;
|
||||
@@ -187,7 +201,7 @@ fn dispatchNode(self: *EventManager, target: *Node, event: *Event) !void {
|
||||
event._event_phase = .at_target;
|
||||
const target_et = target.asEventTarget();
|
||||
if (self.lookup.getPtr(@intFromPtr(target_et))) |list| {
|
||||
try self.dispatchPhase(list, target_et, event, null);
|
||||
try self.dispatchPhase(list, target_et, event, was_handled, null);
|
||||
if (event._stop_propagation) {
|
||||
event._event_phase = .none;
|
||||
return;
|
||||
@@ -200,7 +214,7 @@ fn dispatchNode(self: *EventManager, target: *Node, event: *Event) !void {
|
||||
event._event_phase = .bubbling_phase;
|
||||
for (path[1..]) |current_target| {
|
||||
if (self.lookup.getPtr(@intFromPtr(current_target))) |list| {
|
||||
try self.dispatchPhase(list, current_target, event, false);
|
||||
try self.dispatchPhase(list, current_target, event, was_handled, false);
|
||||
if (event._stop_propagation) {
|
||||
break;
|
||||
}
|
||||
@@ -211,7 +225,7 @@ fn dispatchNode(self: *EventManager, target: *Node, event: *Event) !void {
|
||||
event._event_phase = .none;
|
||||
}
|
||||
|
||||
fn dispatchPhase(self: *EventManager, list: *std.DoublyLinkedList, current_target: *EventTarget, event: *Event, comptime capture_only: ?bool) !void {
|
||||
fn dispatchPhase(self: *EventManager, list: *std.DoublyLinkedList, current_target: *EventTarget, event: *Event, was_handled: *bool, comptime capture_only: ?bool) !void {
|
||||
const page = self.page;
|
||||
const typ = event._type_string;
|
||||
|
||||
@@ -240,6 +254,7 @@ fn dispatchPhase(self: *EventManager, list: *std.DoublyLinkedList, current_targe
|
||||
}
|
||||
}
|
||||
|
||||
was_handled.* = true;
|
||||
event._current_target = current_target;
|
||||
|
||||
switch (listener.function) {
|
||||
@@ -261,8 +276,8 @@ fn dispatchPhase(self: *EventManager, list: *std.DoublyLinkedList, current_targe
|
||||
}
|
||||
|
||||
// Non-Node dispatching (XHR, Window without propagation)
|
||||
fn dispatchAll(self: *EventManager, list: *std.DoublyLinkedList, current_target: *EventTarget, event: *Event) !void {
|
||||
return self.dispatchPhase(list, current_target, event, null);
|
||||
fn dispatchAll(self: *EventManager, list: *std.DoublyLinkedList, current_target: *EventTarget, event: *Event, was_handled: *bool) !void {
|
||||
return self.dispatchPhase(list, current_target, event, was_handled, null);
|
||||
}
|
||||
|
||||
fn removeListener(self: *EventManager, list: *std.DoublyLinkedList, listener: *Listener) void {
|
||||
|
||||
@@ -242,21 +242,13 @@ fn registerBackgroundTasks(self: *Page) !void {
|
||||
|
||||
const Browser = @import("Browser.zig");
|
||||
|
||||
try self.scheduler.add(self._session.browser, struct {
|
||||
fn runMicrotasks(ctx: *anyopaque) !?u32 {
|
||||
const b: *Browser = @ptrCast(@alignCast(ctx));
|
||||
b.runMicrotasks();
|
||||
return 5;
|
||||
}
|
||||
}.runMicrotasks, 5, .{ .name = "page.microtasks" });
|
||||
|
||||
try self.scheduler.add(self._session.browser, struct {
|
||||
fn runMessageLoop(ctx: *anyopaque) !?u32 {
|
||||
const b: *Browser = @ptrCast(@alignCast(ctx));
|
||||
b.runMessageLoop();
|
||||
return 100;
|
||||
return 250;
|
||||
}
|
||||
}.runMessageLoop, 5, .{ .name = "page.messageLoop" });
|
||||
}.runMessageLoop, 250, .{ .name = "page.messageLoop" });
|
||||
}
|
||||
|
||||
pub fn navigate(self: *Page, request_url: [:0]const u8, opts: NavigateOpts) !void {
|
||||
@@ -705,10 +697,10 @@ fn _wait(self: *Page, wait_ms: u32) !Session.WaitResult {
|
||||
}
|
||||
|
||||
pub fn tick(self: *Page) void {
|
||||
self._session.browser.runMicrotasks();
|
||||
_ = self.scheduler.run() catch |err| {
|
||||
log.err(.page, "tick", .{ .err = err });
|
||||
};
|
||||
self.js.runMicrotasks();
|
||||
}
|
||||
|
||||
pub fn scriptAddedCallback(self: *Page, script: *HtmlScript) !void {
|
||||
|
||||
@@ -1162,6 +1162,10 @@ pub fn resolvePromise(self: *Context, value: anytype) !js.Promise {
|
||||
return resolver.getPromise();
|
||||
}
|
||||
|
||||
pub fn runMicrotasks(self: *Context) void {
|
||||
self.isolate.performMicrotasksCheckpoint();
|
||||
}
|
||||
|
||||
// creates a PersistentPromiseResolver, taking in a lifetime parameter.
|
||||
// If the lifetime is page, the page will clean up the PersistentPromiseResolver.
|
||||
// If the lifetime is self, you will be expected to deinitalize the PersistentPromiseResolver.
|
||||
@@ -1444,6 +1448,7 @@ fn dynamicModuleSourceCallback(ctx: *anyopaque, fetch_result_: anyerror!ScriptMa
|
||||
}
|
||||
|
||||
fn resolveDynamicModule(self: *Context, state: *DynamicModuleResolveState, module_entry: ModuleEntry) void {
|
||||
defer self.runMicrotasks();
|
||||
const ctx = self.v8_context;
|
||||
const isolate = self.isolate;
|
||||
const external = v8.External.init(self.isolate, @ptrCast(state));
|
||||
@@ -1479,6 +1484,7 @@ fn resolveDynamicModule(self: *Context, state: *DynamicModuleResolveState, modul
|
||||
return;
|
||||
}
|
||||
|
||||
defer caller.context.runMicrotasks();
|
||||
const namespace = s.module.?.getModuleNamespace();
|
||||
_ = s.resolver.castToPromiseResolver().resolve(caller.context.v8_context, namespace);
|
||||
}
|
||||
@@ -1494,6 +1500,7 @@ fn resolveDynamicModule(self: *Context, state: *DynamicModuleResolveState, modul
|
||||
if (s.context_id != caller.context.id) {
|
||||
return;
|
||||
}
|
||||
defer caller.context.runMicrotasks();
|
||||
_ = s.resolver.castToPromiseResolver().reject(caller.context.v8_context, info.getData());
|
||||
}
|
||||
}.callback, external);
|
||||
|
||||
@@ -107,6 +107,7 @@ pub const PersistentPromiseResolver = struct {
|
||||
pub fn resolve(self: PersistentPromiseResolver, value: anytype) !void {
|
||||
const context = self.context;
|
||||
const js_value = try context.zigValueToJs(value, .{});
|
||||
defer context.runMicrotasks();
|
||||
|
||||
// resolver.resolve will return null if the promise isn't pending
|
||||
const ok = self.resolver.castToPromiseResolver().resolve(context.v8_context, js_value) orelse return;
|
||||
@@ -118,6 +119,7 @@ pub const PersistentPromiseResolver = struct {
|
||||
pub fn reject(self: PersistentPromiseResolver, value: anytype) !void {
|
||||
const context = self.context;
|
||||
const js_value = try context.zigValueToJs(value, .{});
|
||||
defer context.runMicrotasks();
|
||||
|
||||
// resolver.reject will return null if the promise isn't pending
|
||||
const ok = self.resolver.castToPromiseResolver().reject(context.v8_context, js_value) orelse return;
|
||||
|
||||
@@ -321,14 +321,15 @@ const ScheduleCallback = struct {
|
||||
|
||||
fn run(ctx: *anyopaque) !?u32 {
|
||||
const self: *ScheduleCallback = @ptrCast(@alignCast(ctx));
|
||||
const page = self.page;
|
||||
if (self.removed) {
|
||||
_ = self.page.window._timers.remove(self.timer_id);
|
||||
_ = page.window._timers.remove(self.timer_id);
|
||||
self.deinit();
|
||||
return null;
|
||||
}
|
||||
|
||||
if (self.animation_frame) {
|
||||
self.cb.call(void, .{self.page.window._performance.now()}) catch |err| {
|
||||
self.cb.call(void, .{page.window._performance.now()}) catch |err| {
|
||||
// a non-JS error
|
||||
log.warn(.js, "window.RAF", .{ .name = self.name, .err = err });
|
||||
};
|
||||
@@ -342,9 +343,10 @@ const ScheduleCallback = struct {
|
||||
if (self.repeat_ms) |ms| {
|
||||
return ms;
|
||||
}
|
||||
defer self.deinit();
|
||||
|
||||
_ = self.page.window._timers.remove(self.timer_id);
|
||||
self.deinit();
|
||||
_ = page.window._timers.remove(self.timer_id);
|
||||
page.js.runMicrotasks();
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user