@@ -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
@@ -392,7 +392,6 @@ pub const Page = struct {
392392 };
393393 }
394394
395-
396395 // evalScript evaluates the src in priority.
397396 // if no src is present, we evaluate the text source.
398397 // https://html.spec.whatwg.org/multipage/scripting.html#script-processing-model
@@ -401,7 +400,7 @@ pub const Page = struct {
401400 try parser .documentHTMLSetCurrentScript (html_doc , @ptrCast (script .element ));
402401
403402 defer parser .documentHTMLSetCurrentScript (html_doc , null ) catch | err | {
404- log .err (.page , "clear document script" , .{.err = err });
403+ log .err (.page , "clear document script" , .{ .err = err });
405404 };
406405
407406 const src = script .src orelse {
@@ -569,7 +568,7 @@ pub const Page = struct {
569568 }
570569
571570 pub fn getOrCreateNodeWrapper (self : * Page , comptime T : type , node : * parser.Node ) ! * T {
572- if (try self .getNodeWrapper (T , node )) | wrap | {
571+ if (self .getNodeWrapper (T , node )) | wrap | {
573572 return wrap ;
574573 }
575574
@@ -580,7 +579,7 @@ pub const Page = struct {
580579 return wrap ;
581580 }
582581
583- pub fn getNodeWrapper (_ : * Page , comptime T : type , node : * parser.Node ) ! ? * T {
582+ pub fn getNodeWrapper (_ : * const Page , comptime T : type , node : * parser.Node ) ? * T {
584583 if (parser .nodeGetEmbedderData (node )) | wrap | {
585584 return @alignCast (@ptrCast (wrap ));
586585 }
@@ -609,8 +608,9 @@ const Script = struct {
609608 is_defer : bool ,
610609 src : ? []const u8 ,
611610 element : * parser.Element ,
612- // The javascript to load after we successfully load the script
613- onload : ? []const u8 ,
611+ // The javascript to load after we successfully load the script
612+ onload : ? Callback ,
613+ onerror : ? Callback ,
614614
615615 // The javascript to load if we have an error executing the script
616616 // For now, we ignore this, since we still have a lot of errors that we
@@ -622,7 +622,12 @@ const Script = struct {
622622 javascript ,
623623 };
624624
625- 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 {
626631 if (try parser .elementGetAttribute (e , "nomodule" ) != null ) {
627632 // these scripts should only be loaded if we don't support modules
628633 // but since we do support modules, we can just skip them.
@@ -633,11 +638,42 @@ const Script = struct {
633638 return null ;
634639 };
635640
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+
636671 return .{
637672 .kind = kind ,
638673 .element = e ,
674+ .onload = onload ,
675+ .onerror = onerror ,
639676 .src = try parser .elementGetAttribute (e , "src" ),
640- .onload = try parser .elementGetAttribute (e , "onload" ),
641677 .is_async = try parser .elementGetAttribute (e , "async" ) != null ,
642678 .is_defer = try parser .elementGetAttribute (e , "defer" ) != null ,
643679 };
@@ -654,9 +690,9 @@ const Script = struct {
654690 return .javascript ;
655691 }
656692
657- if (std .mem . eql ( u8 , script_type , "application/javascript" )) return .javascript ;
658- if (std .mem . eql ( u8 , script_type , "text/javascript" )) return .javascript ;
659- 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 ;
660696
661697 return null ;
662698 }
@@ -667,7 +703,7 @@ const Script = struct {
667703 defer try_catch .deinit ();
668704
669705 const src = self .src orelse "inline" ;
670- const res = switch (self .kind ) {
706+ _ = switch (self .kind ) {
671707 .javascript = > page .scope .exec (body , src ),
672708 .module = > blk : {
673709 switch (try page .scope .module (body , src )) {
@@ -682,17 +718,46 @@ const Script = struct {
682718 if (try try_catch .err (page .arena )) | msg | {
683719 log .warn (.page , "eval script" , .{ .src = src , .err = msg });
684720 }
721+ try self .executeCallback ("onerror" , page );
685722 return error .JsErr ;
686723 };
687- _ = res ;
724+ try self .executeCallback ("onload" , page );
725+ }
688726
689- if (self .onload ) | onload | {
690- _ = page .scope .exec (onload , "script_on_load" ) catch {
691- if (try try_catch .err (page .arena )) | msg | {
692- log .warn (.page , "eval onload" , .{ .src = src , .err = msg });
693- }
694- return error .JsErr ;
695- };
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+ },
696761 }
697762 }
698763};
@@ -720,11 +785,11 @@ fn timestamp() u32 {
720785// after the document is loaded, it's ok to execute any async and defer scripts
721786// immediately.
722787pub export fn scriptAddedCallback (ctx : ? * anyopaque , element : ? * parser.Element ) callconv (.C ) void {
723- var script = Script .init (element .? ) catch | err | {
724- log .warn (.page , "script added init error" , .{.err = err });
788+ const self : * Page = @alignCast (@ptrCast (ctx .? ));
789+ var script = Script .init (element .? , self ) catch | err | {
790+ log .warn (.page , "script added init error" , .{ .err = err });
725791 return ;
726792 } orelse return ;
727793
728- const self : * Page = @alignCast (@ptrCast (ctx .? ));
729794 self .evalScript (& script );
730795}
0 commit comments