mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-28 22:53:28 +00:00
Merge pull request #778 from lightpanda-io/terminate_execution
Terminate execution on internal navigation
This commit is contained in:
@@ -388,11 +388,15 @@ pub const Page = struct {
|
||||
// > immediately before the browser continues to parse the
|
||||
// > page.
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#notes
|
||||
self.evalScript(&script);
|
||||
if (self.evalScript(&script) == false) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (defer_scripts.items) |*script| {
|
||||
self.evalScript(script);
|
||||
if (self.evalScript(script) == false) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// dispatch DOMContentLoaded before the transition to "complete",
|
||||
// at the point where all subresources apart from async script elements
|
||||
@@ -402,7 +406,9 @@ pub const Page = struct {
|
||||
|
||||
// eval async scripts.
|
||||
for (async_scripts.items) |*script| {
|
||||
self.evalScript(script);
|
||||
if (self.evalScript(script) == false) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try HTMLDocument.documentIsComplete(html_doc, self);
|
||||
@@ -419,10 +425,13 @@ pub const Page = struct {
|
||||
);
|
||||
}
|
||||
|
||||
fn evalScript(self: *Page, script: *const Script) void {
|
||||
self.tryEvalScript(script) catch |err| {
|
||||
log.err(.js, "eval script error", .{ .err = err, .src = script.src });
|
||||
fn evalScript(self: *Page, script: *const Script) bool {
|
||||
self.tryEvalScript(script) catch |err| switch (err) {
|
||||
error.JsErr => {}, // already been logged with detail
|
||||
error.Terminated => return false,
|
||||
else => log.err(.js, "eval script error", .{ .err = err, .src = script.src }),
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
// evalScript evaluates the src in priority.
|
||||
@@ -436,29 +445,26 @@ pub const Page = struct {
|
||||
log.err(.browser, "clear document script", .{ .err = err });
|
||||
};
|
||||
|
||||
const src = script.src orelse {
|
||||
var script_source: ?[]const u8 = null;
|
||||
if (script.src) |src| {
|
||||
self.current_script = script;
|
||||
defer self.current_script = null;
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-classic-script
|
||||
script_source = (try self.fetchData(src, null)) orelse {
|
||||
// TODO If el's result is null, then fire an event named error at
|
||||
// el, and return
|
||||
return;
|
||||
};
|
||||
} else {
|
||||
// source is inline
|
||||
// TODO handle charset attribute
|
||||
if (try parser.nodeTextContent(parser.elementToNode(script.element))) |text| {
|
||||
try script.eval(self, text);
|
||||
}
|
||||
return;
|
||||
};
|
||||
script_source = try parser.nodeTextContent(parser.elementToNode(script.element));
|
||||
}
|
||||
|
||||
self.current_script = script;
|
||||
defer self.current_script = null;
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-classic-script
|
||||
const body = (try self.fetchData(src, null)) orelse {
|
||||
// TODO If el's result is null, then fire an event named error at
|
||||
// el, and return
|
||||
return;
|
||||
};
|
||||
|
||||
script.eval(self, body) catch |err| switch (err) {
|
||||
error.JsErr => {}, // nothing to do here.
|
||||
else => return err,
|
||||
};
|
||||
if (script_source) |ss| {
|
||||
try script.eval(self, ss);
|
||||
}
|
||||
|
||||
// TODO If el's from an external file is true, then fire an event
|
||||
// named load at el.
|
||||
@@ -701,13 +707,18 @@ pub const Page = struct {
|
||||
.reason = opts.reason,
|
||||
});
|
||||
self.delayed_navigation = true;
|
||||
const arena = self.session.transfer_arena;
|
||||
|
||||
const session = self.session;
|
||||
const arena = session.transfer_arena;
|
||||
const navi = try arena.create(DelayedNavigation);
|
||||
navi.* = .{
|
||||
.opts = opts,
|
||||
.session = self.session,
|
||||
.session = session,
|
||||
.url = try self.url.resolve(arena, url),
|
||||
};
|
||||
|
||||
// In v8, this throws an exception which JS code cannot catch.
|
||||
session.executor.terminateExecution();
|
||||
_ = try self.loop.timeout(0, &navi.navigate_node);
|
||||
}
|
||||
|
||||
@@ -822,6 +833,11 @@ const DelayedNavigation = struct {
|
||||
const initial = self.initial;
|
||||
|
||||
if (initial) {
|
||||
// Prior to schedule this task, we terminated excution to stop
|
||||
// the running script. If we don't resume it before doing a shutdown
|
||||
// we'll get an error.
|
||||
session.executor.resumeExecution();
|
||||
|
||||
session.removePage();
|
||||
_ = session.createPage() catch |err| {
|
||||
log.err(.browser, "delayed navigation page error", .{
|
||||
@@ -985,9 +1001,17 @@ const Script = struct {
|
||||
}
|
||||
},
|
||||
} catch {
|
||||
if (try try_catch.err(page.arena)) |msg| {
|
||||
log.warn(.user_script, "eval script", .{ .src = src, .err = msg });
|
||||
if (page.delayed_navigation) {
|
||||
return error.Terminated;
|
||||
}
|
||||
|
||||
if (try try_catch.err(page.arena)) |msg| {
|
||||
log.warn(.user_script, "eval script", .{
|
||||
.src = src,
|
||||
.err = msg,
|
||||
});
|
||||
}
|
||||
|
||||
try self.executeCallback("onerror", page);
|
||||
return error.JsErr;
|
||||
};
|
||||
@@ -1060,10 +1084,15 @@ fn timestamp() u32 {
|
||||
// immediately.
|
||||
pub export fn scriptAddedCallback(ctx: ?*anyopaque, element: ?*parser.Element) callconv(.C) void {
|
||||
const self: *Page = @alignCast(@ptrCast(ctx.?));
|
||||
if (self.delayed_navigation) {
|
||||
// if we're planning on navigating to another page, don't run this script
|
||||
return;
|
||||
}
|
||||
|
||||
var script = Script.init(element.?, self) catch |err| {
|
||||
log.warn(.browser, "script added init error", .{ .err = err });
|
||||
return;
|
||||
} orelse return;
|
||||
|
||||
self.evalScript(&script);
|
||||
_ = self.evalScript(&script);
|
||||
}
|
||||
|
||||
@@ -472,6 +472,14 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
|
||||
self.scope = null;
|
||||
_ = self.scope_arena.reset(.{ .retain_with_limit = SCOPE_ARENA_RETAIN });
|
||||
}
|
||||
|
||||
pub fn terminateExecution(self: *const ExecutionWorld) void {
|
||||
self.env.isolate.terminateExecution();
|
||||
}
|
||||
|
||||
pub fn resumeExecution(self: *const ExecutionWorld) void {
|
||||
self.env.isolate.cancelTerminateExecution();
|
||||
}
|
||||
};
|
||||
|
||||
const PersistentObject = v8.Persistent(v8.Object);
|
||||
@@ -1562,7 +1570,15 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
|
||||
}
|
||||
|
||||
pub fn send(self: *const Inspector, msg: []const u8) void {
|
||||
self.session.dispatchProtocolMessage(self.isolate, msg);
|
||||
// Can't assume there's an existing Scope (with its HandleScope)
|
||||
// available when doing this. Pages (and thus Scopes) come and
|
||||
// go, but CDP can keep sending messages.
|
||||
const isolate = self.isolate;
|
||||
var temp_scope: v8.HandleScope = undefined;
|
||||
v8.HandleScope.init(&temp_scope, isolate);
|
||||
defer temp_scope.deinit();
|
||||
|
||||
self.session.dispatchProtocolMessage(isolate, msg);
|
||||
}
|
||||
|
||||
// From CDP docs
|
||||
|
||||
Reference in New Issue
Block a user