mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-29 23:23: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
|
// > immediately before the browser continues to parse the
|
||||||
// > page.
|
// > page.
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#notes
|
// 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| {
|
for (defer_scripts.items) |*script| {
|
||||||
self.evalScript(script);
|
if (self.evalScript(script) == false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// dispatch DOMContentLoaded before the transition to "complete",
|
// dispatch DOMContentLoaded before the transition to "complete",
|
||||||
// at the point where all subresources apart from async script elements
|
// at the point where all subresources apart from async script elements
|
||||||
@@ -402,7 +406,9 @@ pub const Page = struct {
|
|||||||
|
|
||||||
// eval async scripts.
|
// eval async scripts.
|
||||||
for (async_scripts.items) |*script| {
|
for (async_scripts.items) |*script| {
|
||||||
self.evalScript(script);
|
if (self.evalScript(script) == false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try HTMLDocument.documentIsComplete(html_doc, self);
|
try HTMLDocument.documentIsComplete(html_doc, self);
|
||||||
@@ -419,10 +425,13 @@ pub const Page = struct {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn evalScript(self: *Page, script: *const Script) void {
|
fn evalScript(self: *Page, script: *const Script) bool {
|
||||||
self.tryEvalScript(script) catch |err| {
|
self.tryEvalScript(script) catch |err| switch (err) {
|
||||||
log.err(.js, "eval script error", .{ .err = err, .src = script.src });
|
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.
|
// evalScript evaluates the src in priority.
|
||||||
@@ -436,29 +445,26 @@ pub const Page = struct {
|
|||||||
log.err(.browser, "clear document script", .{ .err = err });
|
log.err(.browser, "clear document script", .{ .err = err });
|
||||||
};
|
};
|
||||||
|
|
||||||
const src = script.src orelse {
|
var script_source: ?[]const u8 = null;
|
||||||
// source is inline
|
if (script.src) |src| {
|
||||||
// TODO handle charset attribute
|
|
||||||
if (try parser.nodeTextContent(parser.elementToNode(script.element))) |text| {
|
|
||||||
try script.eval(self, text);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
self.current_script = script;
|
self.current_script = script;
|
||||||
defer self.current_script = null;
|
defer self.current_script = null;
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-classic-script
|
// https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-classic-script
|
||||||
const body = (try self.fetchData(src, null)) orelse {
|
script_source = (try self.fetchData(src, null)) orelse {
|
||||||
// TODO If el's result is null, then fire an event named error at
|
// TODO If el's result is null, then fire an event named error at
|
||||||
// el, and return
|
// el, and return
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
} else {
|
||||||
|
// source is inline
|
||||||
|
// TODO handle charset attribute
|
||||||
|
script_source = try parser.nodeTextContent(parser.elementToNode(script.element));
|
||||||
|
}
|
||||||
|
|
||||||
script.eval(self, body) catch |err| switch (err) {
|
if (script_source) |ss| {
|
||||||
error.JsErr => {}, // nothing to do here.
|
try script.eval(self, ss);
|
||||||
else => return err,
|
}
|
||||||
};
|
|
||||||
|
|
||||||
// TODO If el's from an external file is true, then fire an event
|
// TODO If el's from an external file is true, then fire an event
|
||||||
// named load at el.
|
// named load at el.
|
||||||
@@ -701,13 +707,18 @@ pub const Page = struct {
|
|||||||
.reason = opts.reason,
|
.reason = opts.reason,
|
||||||
});
|
});
|
||||||
self.delayed_navigation = true;
|
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);
|
const navi = try arena.create(DelayedNavigation);
|
||||||
navi.* = .{
|
navi.* = .{
|
||||||
.opts = opts,
|
.opts = opts,
|
||||||
.session = self.session,
|
.session = session,
|
||||||
.url = try self.url.resolve(arena, url),
|
.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);
|
_ = try self.loop.timeout(0, &navi.navigate_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -822,6 +833,11 @@ const DelayedNavigation = struct {
|
|||||||
const initial = self.initial;
|
const initial = self.initial;
|
||||||
|
|
||||||
if (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.removePage();
|
||||||
_ = session.createPage() catch |err| {
|
_ = session.createPage() catch |err| {
|
||||||
log.err(.browser, "delayed navigation page error", .{
|
log.err(.browser, "delayed navigation page error", .{
|
||||||
@@ -985,9 +1001,17 @@ const Script = struct {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
} catch {
|
} catch {
|
||||||
if (try try_catch.err(page.arena)) |msg| {
|
if (page.delayed_navigation) {
|
||||||
log.warn(.user_script, "eval script", .{ .src = src, .err = msg });
|
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);
|
try self.executeCallback("onerror", page);
|
||||||
return error.JsErr;
|
return error.JsErr;
|
||||||
};
|
};
|
||||||
@@ -1060,10 +1084,15 @@ fn timestamp() u32 {
|
|||||||
// immediately.
|
// immediately.
|
||||||
pub export fn scriptAddedCallback(ctx: ?*anyopaque, element: ?*parser.Element) callconv(.C) void {
|
pub export fn scriptAddedCallback(ctx: ?*anyopaque, element: ?*parser.Element) callconv(.C) void {
|
||||||
const self: *Page = @alignCast(@ptrCast(ctx.?));
|
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| {
|
var script = Script.init(element.?, self) catch |err| {
|
||||||
log.warn(.browser, "script added init error", .{ .err = err });
|
log.warn(.browser, "script added init error", .{ .err = err });
|
||||||
return;
|
return;
|
||||||
} orelse 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 = null;
|
||||||
_ = self.scope_arena.reset(.{ .retain_with_limit = SCOPE_ARENA_RETAIN });
|
_ = 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);
|
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 {
|
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
|
// From CDP docs
|
||||||
|
|||||||
Reference in New Issue
Block a user