mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-28 14:43:28 +00:00
Load dynamically added <script> tags
Add a callback to libdom which triggers whenever a script tag is added. Page registers the callback AFTER the HTML is parsed, but before any JS is processed and loads the script tags.
This commit is contained in:
@@ -2133,6 +2133,10 @@ pub inline fn documentCreateAttributeNS(doc: *Document, ns: []const u8, qname: [
|
|||||||
return attr.?;
|
return attr.?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn documentSetScriptAddedCallback(doc: *Document, ctx: *anyopaque, callback: c.dom_script_added_callback,) void {
|
||||||
|
c._dom_document_set_script_added_callback(doc, ctx, callback);
|
||||||
|
}
|
||||||
|
|
||||||
// DocumentHTML
|
// DocumentHTML
|
||||||
pub const DocumentHTML = c.dom_html_document;
|
pub const DocumentHTML = c.dom_html_document;
|
||||||
|
|
||||||
|
|||||||
@@ -277,6 +277,10 @@ pub const Page = struct {
|
|||||||
const html_doc = self.window.document;
|
const html_doc = self.window.document;
|
||||||
const doc = parser.documentHTMLToDocument(html_doc);
|
const doc = parser.documentHTMLToDocument(html_doc);
|
||||||
|
|
||||||
|
// we want to be notified of any dynamically added script tags
|
||||||
|
// so that we can load the script
|
||||||
|
parser.documentSetScriptAddedCallback(doc, self, scriptAddedCallback);
|
||||||
|
|
||||||
const document_element = (try parser.documentGetDocumentElement(doc)) orelse return error.DocumentElementError;
|
const document_element = (try parser.documentGetDocumentElement(doc)) orelse return error.DocumentElementError;
|
||||||
_ = try parser.eventTargetAddEventListener(
|
_ = try parser.eventTargetAddEventListener(
|
||||||
parser.toEventTarget(parser.Element, document_element),
|
parser.toEventTarget(parser.Element, document_element),
|
||||||
@@ -317,8 +321,12 @@ pub const Page = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const e = parser.nodeToElement(next.?);
|
const e = parser.nodeToElement(next.?);
|
||||||
|
const tag = try parser.elementHTMLGetTagType(@as(*parser.ElementHTML, @ptrCast(e)));
|
||||||
|
if (tag != .script) {
|
||||||
|
// ignore non-js script.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// ignore non-js script.
|
|
||||||
const script = try Script.init(e) orelse continue;
|
const script = try Script.init(e) orelse continue;
|
||||||
|
|
||||||
// TODO use fetchpriority
|
// TODO use fetchpriority
|
||||||
@@ -348,19 +356,11 @@ 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
|
||||||
try parser.documentHTMLSetCurrentScript(html_doc, @ptrCast(e));
|
self.evalScript(&script);
|
||||||
self.evalScript(&script) catch |err| {
|
|
||||||
log.err(.page, "eval script error", .{ .err = err });
|
|
||||||
};
|
|
||||||
try parser.documentHTMLSetCurrentScript(html_doc, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (defer_scripts.items) |script| {
|
for (defer_scripts.items) |*script| {
|
||||||
try parser.documentHTMLSetCurrentScript(html_doc, @ptrCast(script.element));
|
self.evalScript(script);
|
||||||
self.evalScript(&script) catch |err| {
|
|
||||||
log.err(.page, "eval script error", .{ .err = err });
|
|
||||||
};
|
|
||||||
try parser.documentHTMLSetCurrentScript(html_doc, null);
|
|
||||||
}
|
}
|
||||||
// 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
|
||||||
@@ -369,12 +369,8 @@ pub const Page = struct {
|
|||||||
try HTMLDocument.documentIsLoaded(html_doc, self);
|
try HTMLDocument.documentIsLoaded(html_doc, self);
|
||||||
|
|
||||||
// eval async scripts.
|
// eval async scripts.
|
||||||
for (async_scripts.items) |script| {
|
for (async_scripts.items) |*script| {
|
||||||
try parser.documentHTMLSetCurrentScript(html_doc, @ptrCast(script.element));
|
self.evalScript(script);
|
||||||
self.evalScript(&script) catch |err| {
|
|
||||||
log.err(.page, "eval script error", .{ .err = err });
|
|
||||||
};
|
|
||||||
try parser.documentHTMLSetCurrentScript(html_doc, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try HTMLDocument.documentIsComplete(html_doc, self);
|
try HTMLDocument.documentIsComplete(html_doc, self);
|
||||||
@@ -390,10 +386,24 @@ pub const Page = struct {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn evalScript(self: *Page, script: *const Script) void {
|
||||||
|
self.tryEvalScript(script) catch |err| {
|
||||||
|
log.err(.page, "eval script error", .{ .err = err });
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// evalScript evaluates the src in priority.
|
// evalScript evaluates the src in priority.
|
||||||
// if no src is present, we evaluate the text source.
|
// if no src is present, we evaluate the text source.
|
||||||
// https://html.spec.whatwg.org/multipage/scripting.html#script-processing-model
|
// https://html.spec.whatwg.org/multipage/scripting.html#script-processing-model
|
||||||
fn evalScript(self: *Page, script: *const Script) !void {
|
fn tryEvalScript(self: *Page, script: *const Script) !void {
|
||||||
|
const html_doc = self.window.document;
|
||||||
|
try parser.documentHTMLSetCurrentScript(html_doc, @ptrCast(script.element));
|
||||||
|
|
||||||
|
defer parser.documentHTMLSetCurrentScript(html_doc, null) catch |err| {
|
||||||
|
log.err(.page, "clear document script", .{.err = err});
|
||||||
|
};
|
||||||
|
|
||||||
const src = script.src orelse {
|
const src = script.src orelse {
|
||||||
// source is inline
|
// source is inline
|
||||||
// TODO handle charset attribute
|
// TODO handle charset attribute
|
||||||
@@ -613,12 +623,6 @@ const Script = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
fn init(e: *parser.Element) !?Script {
|
fn init(e: *parser.Element) !?Script {
|
||||||
// ignore non-script tags
|
|
||||||
const tag = try parser.elementHTMLGetTagType(@as(*parser.ElementHTML, @ptrCast(e)));
|
|
||||||
if (tag != .script) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (try parser.elementGetAttribute(e, "nomodule") != null) {
|
if (try parser.elementGetAttribute(e, "nomodule") != null) {
|
||||||
// these scripts should only be loaded if we don't support modules
|
// these scripts should only be loaded if we don't support modules
|
||||||
// but since we do support modules, we can just skip them.
|
// but since we do support modules, we can just skip them.
|
||||||
@@ -707,3 +711,20 @@ fn timestamp() u32 {
|
|||||||
const ts = std.posix.clock_gettime(std.posix.CLOCK.MONOTONIC) catch unreachable;
|
const ts = std.posix.clock_gettime(std.posix.CLOCK.MONOTONIC) catch unreachable;
|
||||||
return @intCast(ts.sec);
|
return @intCast(ts.sec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A callback from libdom whenever a script tag is added to the DOM.
|
||||||
|
// element is guaranteed to be a script element.
|
||||||
|
// The script tag might not have a src. It might be any attribute, like
|
||||||
|
// `nomodule`, `defer` and `async`. `Script.init` will return null on `nomodule`
|
||||||
|
// so that's handled. And because we're only executing the inline <script> tags
|
||||||
|
// after the document is loaded, it's ok to execute any async and defer scripts
|
||||||
|
// immediately.
|
||||||
|
pub export fn scriptAddedCallback(ctx: ?*anyopaque, element: ?*parser.Element) callconv(.C) void {
|
||||||
|
var script = Script.init(element.?) catch |err| {
|
||||||
|
log.warn(.page, "script added init error", .{.err = err});
|
||||||
|
return;
|
||||||
|
} orelse return;
|
||||||
|
|
||||||
|
const self: *Page = @alignCast(@ptrCast(ctx.?));
|
||||||
|
self.evalScript(&script);
|
||||||
|
}
|
||||||
|
|||||||
2
vendor/netsurf/libdom
vendored
2
vendor/netsurf/libdom
vendored
Submodule vendor/netsurf/libdom updated: b0f4a43147...2576e31b3e
Reference in New Issue
Block a user