Skip to content
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
48 changes: 18 additions & 30 deletions src/browser/ScriptManager.zig
Original file line number Diff line number Diff line change
Expand Up @@ -147,32 +147,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| {
Expand All @@ -188,8 +163,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,
Expand Down Expand Up @@ -562,8 +535,6 @@ const Script = struct {
is_async: bool,
is_defer: bool,
source: Source,
onload: ?Callback,
onerror: ?Callback,
element: *parser.Element,

const Kind = enum {
Expand Down Expand Up @@ -648,7 +619,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| {
Expand Down Expand Up @@ -691,6 +662,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 {
Expand Down
1 change: 0 additions & 1 deletion src/browser/events/event.zig
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,6 @@ pub const EventHandler = struct {
},
}
}

const callback = (try listener.callback(target)) orelse return null;

if (signal) |s| {
Expand Down
21 changes: 16 additions & 5 deletions src/browser/html/elements.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand Down
11 changes: 10 additions & 1 deletion src/browser/page.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1103,13 +1103,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 });
};
Expand Down