@@ -327,7 +327,7 @@ pub const Page = struct {
327327 continue ;
328328 }
329329
330- const script = try Script .init (e ) orelse continue ;
330+ const script = try Script .init (e , null ) orelse continue ;
331331
332332 // TODO use fetchpriority
333333 // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#fetchpriority
@@ -568,7 +568,7 @@ pub const Page = struct {
568568 }
569569
570570 pub fn getOrCreateNodeWrapper (self : * Page , comptime T : type , node : * parser.Node ) ! * T {
571- if (try self .getNodeWrapper (T , node )) | wrap | {
571+ if (self .getNodeWrapper (T , node )) | wrap | {
572572 return wrap ;
573573 }
574574
@@ -579,7 +579,7 @@ pub const Page = struct {
579579 return wrap ;
580580 }
581581
582- pub fn getNodeWrapper (_ : * Page , comptime T : type , node : * parser.Node ) ! ? * T {
582+ pub fn getNodeWrapper (_ : * const Page , comptime T : type , node : * parser.Node ) ? * T {
583583 if (parser .nodeGetEmbedderData (node )) | wrap | {
584584 return @alignCast (@ptrCast (wrap ));
585585 }
@@ -608,8 +608,9 @@ const Script = struct {
608608 is_defer : bool ,
609609 src : ? []const u8 ,
610610 element : * parser.Element ,
611- // The javascript to load after we successfully load the script
612- onload : ? []const u8 ,
611+ // The javascript to load after we successfully load the script
612+ onload : ? Callback ,
613+ onerror : ? Callback ,
613614
614615 // The javascript to load if we have an error executing the script
615616 // For now, we ignore this, since we still have a lot of errors that we
@@ -621,7 +622,12 @@ const Script = struct {
621622 javascript ,
622623 };
623624
624- fn init (e : * parser.Element ) ! ? Script {
625+ const Callback = union (enum ) {
626+ string : []const u8 ,
627+ function : Env.Function ,
628+ };
629+
630+ fn init (e : * parser.Element , page_ : ? * const Page ) ! ? Script {
625631 if (try parser .elementGetAttribute (e , "nomodule" ) != null ) {
626632 // these scripts should only be loaded if we don't support modules
627633 // but since we do support modules, we can just skip them.
@@ -632,11 +638,42 @@ const Script = struct {
632638 return null ;
633639 };
634640
641+ var onload : ? Callback = null ;
642+ var onerror : ? Callback = null ;
643+
644+ if (page_ ) | page | {
645+ // If we're given the page, then it means the script is dynamic
646+ // and we need to load the onload and onerror function (if there are
647+ // any) from our WebAPI.
648+ // This page == null is an optimization which isn't technically
649+ // correct, as a static script could have a dynamic onload/onerror
650+ // attached to it. But this seems quite unlikely and it does help
651+ // optimize loading scripts, of which there can be hundreds for a
652+ // page.
653+ const HTMLScriptElement = @import ("html/elements.zig" ).HTMLScriptElement ;
654+ if (page .getNodeWrapper (HTMLScriptElement , @ptrCast (e ))) | se | {
655+ if (se .onload ) | function | {
656+ onload = .{ .function = function };
657+ }
658+ if (se .onerror ) | function | {
659+ onerror = .{ .function = function };
660+ }
661+ }
662+ } else {
663+ if (try parser .elementGetAttribute (e , "onload" )) | string | {
664+ onload = .{ .string = string };
665+ }
666+ if (try parser .elementGetAttribute (e , "onerror" )) | string | {
667+ onerror = .{ .string = string };
668+ }
669+ }
670+
635671 return .{
636672 .kind = kind ,
637673 .element = e ,
674+ .onload = onload ,
675+ .onerror = onerror ,
638676 .src = try parser .elementGetAttribute (e , "src" ),
639- .onload = try parser .elementGetAttribute (e , "onload" ),
640677 .is_async = try parser .elementGetAttribute (e , "async" ) != null ,
641678 .is_defer = try parser .elementGetAttribute (e , "defer" ) != null ,
642679 };
@@ -653,9 +690,9 @@ const Script = struct {
653690 return .javascript ;
654691 }
655692
656- if (std .mem . eql ( u8 , script_type , "application/javascript" )) return .javascript ;
657- if (std .mem . eql ( u8 , script_type , "text/javascript" )) return .javascript ;
658- if (std .mem . eql ( u8 , script_type , "module" )) return .module ;
693+ if (std .ascii . eqlIgnoreCase ( script_type , "application/javascript" )) return .javascript ;
694+ if (std .ascii . eqlIgnoreCase ( script_type , "text/javascript" )) return .javascript ;
695+ if (std .ascii . eqlIgnoreCase ( script_type , "module" )) return .module ;
659696
660697 return null ;
661698 }
@@ -666,7 +703,7 @@ const Script = struct {
666703 defer try_catch .deinit ();
667704
668705 const src = self .src orelse "inline" ;
669- const res = switch (self .kind ) {
706+ _ = switch (self .kind ) {
670707 .javascript = > page .scope .exec (body , src ),
671708 .module = > blk : {
672709 switch (try page .scope .module (body , src )) {
@@ -681,17 +718,46 @@ const Script = struct {
681718 if (try try_catch .err (page .arena )) | msg | {
682719 log .warn (.page , "eval script" , .{ .src = src , .err = msg });
683720 }
721+ try self .executeCallback ("onerror" , page );
684722 return error .JsErr ;
685723 };
686- _ = res ;
724+ try self .executeCallback ("onload" , page );
725+ }
687726
688- if (self .onload ) | onload | {
689- _ = page .scope .exec (onload , "script_on_load" ) catch {
690- if (try try_catch .err (page .arena )) | msg | {
691- log .warn (.page , "eval onload" , .{ .src = src , .err = msg });
692- }
693- return error .JsErr ;
694- };
727+ fn executeCallback (self : * const Script , comptime typ : []const u8 , page : * Page ) ! void {
728+ const callback = @field (self , typ ) orelse return ;
729+ switch (callback ) {
730+ .string = > | str | {
731+ var try_catch : Env.TryCatch = undefined ;
732+ try_catch .init (page .scope );
733+ defer try_catch .deinit ();
734+ _ = page .scope .exec (str , typ ) catch {
735+ if (try try_catch .err (page .arena )) | msg | {
736+ log .warn (.page , "script callback" , .{
737+ .src = self .src ,
738+ .err = msg ,
739+ .type = typ ,
740+ .@"inline" = true ,
741+ });
742+ }
743+ };
744+ },
745+ .function = > | f | {
746+ const Event = @import ("events/event.zig" ).Event ;
747+ const loadevt = try parser .eventCreate ();
748+ defer parser .eventDestroy (loadevt );
749+
750+ var result : Env.Function.Result = undefined ;
751+ f .tryCall (void , .{try Event .toInterface (loadevt )}, & result ) catch {
752+ log .warn (.page , "script callback" , .{
753+ .src = self .src ,
754+ .type = typ ,
755+ .err = result .exception ,
756+ .stack = result .stack ,
757+ .@"inline" = false ,
758+ });
759+ };
760+ },
695761 }
696762 }
697763};
@@ -719,11 +785,11 @@ fn timestamp() u32 {
719785// after the document is loaded, it's ok to execute any async and defer scripts
720786// immediately.
721787pub export fn scriptAddedCallback (ctx : ? * anyopaque , element : ? * parser.Element ) callconv (.C ) void {
722- var script = Script .init (element .? ) catch | err | {
788+ const self : * Page = @alignCast (@ptrCast (ctx .? ));
789+ var script = Script .init (element .? , self ) catch | err | {
723790 log .warn (.page , "script added init error" , .{ .err = err });
724791 return ;
725792 } orelse return ;
726793
727- const self : * Page = @alignCast (@ptrCast (ctx .? ));
728794 self .evalScript (& script );
729795}
0 commit comments