Skip to content

Prevent double-execution of script tags. #908

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/browser/netsurf.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2885,3 +2885,15 @@ pub fn buttonGetType(button: *Button) ![]const u8 {
const s = s_ orelse return "button";
return strToData(s);
}

pub fn scriptGetProcessed(script: *Script) !bool {
var processed: bool = false;
const err = c.dom_html_script_element_get_processed(script, &processed);
try DOMErr(err);
return processed;
}

pub fn scriptSetProcessed(script: *Script, processed: bool) !void {
const err = c.dom_html_script_element_set_processed(script, processed);
try DOMErr(err);
}
21 changes: 21 additions & 0 deletions src/browser/page.zig
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,12 @@ pub const Page = struct {
// if no src is present, we evaluate the text source.
// https://html.spec.whatwg.org/multipage/scripting.html#script-processing-model
fn tryEvalScript(self: *Page, script: *const Script) !void {
if (try script.alreadyProcessed()) {
return;
}

try script.markAsProcessed();

const html_doc = self.window.document;
try parser.documentHTMLSetCurrentScript(html_doc, @ptrCast(script.element));

Expand Down Expand Up @@ -998,6 +1004,21 @@ const Script = struct {
return null;
}

// If a script tag gets dynamically created and added to the dom:
// document.getElementsByTagName('head')[0].appendChild(script)
// that script tag will immediately get executed by our scriptAddedCallback.
// However, if the location where the script tag is inserted happens to be
// below where processHTMLDoc curently is, then we'll re-run that same script
// again in processHTMLDoc. This flag is used to let us know if a specific
// <script> has already been processed.
fn alreadyProcessed(self: *const Script) !bool {
return parser.scriptGetProcessed(@ptrCast(self.element));
}

fn markAsProcessed(self: *const Script) !void {
return parser.scriptSetProcessed(@ptrCast(self.element), true);
}

fn eval(self: *const Script, page: *Page, body: []const u8) !void {
var try_catch: Env.TryCatch = undefined;
try_catch.init(page.main_context);
Expand Down