mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-29 15:13:28 +00:00
Merge pull request #975 from lightpanda-io/dynamic_script
Some checks failed
e2e-test / zig build release (push) Has been cancelled
e2e-test / demo-scripts (push) Has been cancelled
e2e-test / cdp-and-hyperfine-bench (push) Has been cancelled
e2e-test / perf-fmt (push) Has been cancelled
zig-test / zig build dev (push) Has been cancelled
zig-test / browser fetch (push) Has been cancelled
zig-test / zig test (push) Has been cancelled
zig-test / perf-fmt (push) Has been cancelled
Some checks failed
e2e-test / zig build release (push) Has been cancelled
e2e-test / demo-scripts (push) Has been cancelled
e2e-test / cdp-and-hyperfine-bench (push) Has been cancelled
e2e-test / perf-fmt (push) Has been cancelled
zig-test / zig build dev (push) Has been cancelled
zig-test / browser fetch (push) Has been cancelled
zig-test / zig test (push) Has been cancelled
zig-test / perf-fmt (push) Has been cancelled
Support dynamic scripts which are added to the DOM before src is set
This commit is contained in:
@@ -143,32 +143,7 @@ pub fn addFromElement(self: *ScriptManager, element: *parser.Element) !void {
|
||||
return;
|
||||
};
|
||||
|
||||
var onload: ?Script.Callback = null;
|
||||
var onerror: ?Script.Callback = null;
|
||||
|
||||
const page = self.page;
|
||||
if (page.getNodeState(@ptrCast(element))) |se| {
|
||||
// if the script has a node state, then it was dynamically added and thus
|
||||
// the onload/onerror were saved in the state (if there are any)
|
||||
if (se.onload) |function| {
|
||||
onload = .{ .function = function };
|
||||
}
|
||||
if (se.onerror) |function| {
|
||||
onerror = .{ .function = function };
|
||||
}
|
||||
} else {
|
||||
// if the script has no node state, then it could still be dynamically
|
||||
// added (could have been dynamically added, but no attributes were set
|
||||
// which required a node state to be created) or it could be a inline
|
||||
// <script>.
|
||||
if (try parser.elementGetAttribute(element, "onload")) |string| {
|
||||
onload = .{ .string = string };
|
||||
}
|
||||
if (try parser.elementGetAttribute(element, "onerror")) |string| {
|
||||
onerror = .{ .string = string };
|
||||
}
|
||||
}
|
||||
|
||||
var source: Script.Source = undefined;
|
||||
var remote_url: ?[:0]const u8 = null;
|
||||
if (try parser.elementGetAttribute(element, "src")) |src| {
|
||||
@@ -184,8 +159,6 @@ pub fn addFromElement(self: *ScriptManager, element: *parser.Element) !void {
|
||||
|
||||
var script = Script{
|
||||
.kind = kind,
|
||||
.onload = onload,
|
||||
.onerror = onerror,
|
||||
.element = element,
|
||||
.source = source,
|
||||
.url = remote_url orelse page.url.raw,
|
||||
@@ -558,8 +531,6 @@ const Script = struct {
|
||||
is_async: bool,
|
||||
is_defer: bool,
|
||||
source: Source,
|
||||
onload: ?Callback,
|
||||
onerror: ?Callback,
|
||||
element: *parser.Element,
|
||||
|
||||
const Kind = enum {
|
||||
@@ -644,7 +615,7 @@ const Script = struct {
|
||||
}
|
||||
|
||||
fn executeCallback(self: *const Script, comptime typ: []const u8, page: *Page) void {
|
||||
const callback = @field(self, typ) orelse return;
|
||||
const callback = self.getCallback(typ, page) orelse return;
|
||||
|
||||
switch (callback) {
|
||||
.string => |str| {
|
||||
@@ -687,6 +658,23 @@ const Script = struct {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn getCallback(self: *const Script, comptime typ: []const u8, page: *Page) ?Callback {
|
||||
const element = self.element;
|
||||
// first we check if there was an el.onload set directly on the
|
||||
// element in JavaScript (if so, it'd be stored in the node state)
|
||||
if (page.getNodeState(@ptrCast(element))) |se| {
|
||||
if (@field(se, typ)) |function| {
|
||||
return .{ .function = function };
|
||||
}
|
||||
}
|
||||
// if we have no node state, or if the node state has no onload/onerror
|
||||
// then check for the onload/onerror attribute
|
||||
if (parser.elementGetAttribute(element, typ) catch null) |string| {
|
||||
return .{ .string = string };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const BufferPool = struct {
|
||||
|
||||
@@ -262,7 +262,6 @@ pub const EventHandler = struct {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const callback = (try listener.callback(target)) orelse return null;
|
||||
|
||||
if (signal) |s| {
|
||||
|
||||
@@ -862,12 +862,23 @@ pub const HTMLScriptElement = struct {
|
||||
) orelse "";
|
||||
}
|
||||
|
||||
pub fn set_src(self: *parser.Script, v: []const u8) !void {
|
||||
return try parser.elementSetAttribute(
|
||||
pub fn set_src(self: *parser.Script, v: []const u8, page: *Page) !void {
|
||||
try parser.elementSetAttribute(
|
||||
parser.scriptToElt(self),
|
||||
"src",
|
||||
v,
|
||||
);
|
||||
|
||||
if (try Node.get_isConnected(@alignCast(@ptrCast(self)))) {
|
||||
// There are sites which do set the src AFTER appending the script
|
||||
// tag to the document:
|
||||
// const s = document.createElement('script');
|
||||
// document.getElementsByTagName('body')[0].appendChild(s);
|
||||
// s.src = '...';
|
||||
// This should load the script.
|
||||
// addFromElement protects against double execution.
|
||||
try page.script_manager.addFromElement(@alignCast(@ptrCast(self)));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_type(self: *parser.Script) !?[]const u8 {
|
||||
@@ -878,7 +889,7 @@ pub const HTMLScriptElement = struct {
|
||||
}
|
||||
|
||||
pub fn set_type(self: *parser.Script, v: []const u8) !void {
|
||||
return try parser.elementSetAttribute(
|
||||
try parser.elementSetAttribute(
|
||||
parser.scriptToElt(self),
|
||||
"type",
|
||||
v,
|
||||
@@ -893,7 +904,7 @@ pub const HTMLScriptElement = struct {
|
||||
}
|
||||
|
||||
pub fn set_text(self: *parser.Script, v: []const u8) !void {
|
||||
return try parser.elementSetAttribute(
|
||||
try parser.elementSetAttribute(
|
||||
parser.scriptToElt(self),
|
||||
"text",
|
||||
v,
|
||||
@@ -908,7 +919,7 @@ pub const HTMLScriptElement = struct {
|
||||
}
|
||||
|
||||
pub fn set_integrity(self: *parser.Script, v: []const u8) !void {
|
||||
return try parser.elementSetAttribute(
|
||||
try parser.elementSetAttribute(
|
||||
parser.scriptToElt(self),
|
||||
"integrity",
|
||||
v,
|
||||
|
||||
@@ -1110,13 +1110,22 @@ fn timestamp() u32 {
|
||||
// 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 {
|
||||
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;
|
||||
}
|
||||
|
||||
// It's posisble for a script to be dynamically added without a src.
|
||||
// const s = document.createElement('script');
|
||||
// document.getElementsByTagName('body')[0].appendChild(s);
|
||||
// The src can be set after. We handle that in HTMLScriptElement.set_src,
|
||||
// but it's important we don't pass such elements to the script_manager
|
||||
// here, else the script_manager will flag it as already-processed.
|
||||
_ = parser.elementGetAttribute(element.?, "src") catch return orelse return;
|
||||
|
||||
self.script_manager.addFromElement(element.?) catch |err| {
|
||||
log.warn(.browser, "dynamic script", .{ .err = err });
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user