@@ -388,7 +388,7 @@ pub const Page = struct {
388388 // sasync stores scripts which can be run asynchronously.
389389 // for now they are just run after the non-async one in order to
390390 // dispatch DOMContentLoaded the sooner as possible.
391- var sasync = std .ArrayList (* parser . Element ).init (alloc );
391+ var sasync = std .ArrayList (Script ).init (alloc );
392392 defer sasync .deinit ();
393393
394394 const root = parser .documentToNode (doc );
@@ -403,21 +403,11 @@ pub const Page = struct {
403403 }
404404
405405 const e = parser .nodeToElement (next .? );
406- const tag = try parser .elementHTMLGetTagType (@as (* parser .ElementHTML , @ptrCast (e )));
407-
408- // ignore non-script tags
409- if (tag != .script ) continue ;
410406
411407 // ignore non-js script.
412- // > type
413- // > Attribute is not set (default), an empty string, or a JavaScript MIME
414- // > type indicates that the script is a "classic script", containing
415- // > JavaScript code.
416- // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attribute_is_not_set_default_an_empty_string_or_a_javascript_mime_type
417- const stype = try parser .elementGetAttribute (e , "type" );
418- if (! isJS (stype )) {
419- continue ;
420- }
408+ const script = try Script .init (e ) orelse continue ;
409+ if (script .kind == .unknown ) continue ;
410+ if (script .kind == .module ) continue ;
421411
422412 // Ignore the defer attribute b/c we analyze all script
423413 // after the document has been parsed.
@@ -431,8 +421,8 @@ pub const Page = struct {
431421 // > then the classic script will be fetched in parallel to
432422 // > parsing and evaluated as soon as it is available.
433423 // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#async
434- if (try parser . elementGetAttribute ( e , "async" ) != null ) {
435- try sasync .append (e );
424+ if (script . isasync ) {
425+ try sasync .append (script );
436426 continue ;
437427 }
438428
@@ -455,7 +445,7 @@ pub const Page = struct {
455445 // > page.
456446 // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#notes
457447 try parser .documentHTMLSetCurrentScript (html_doc , @ptrCast (e ));
458- self .evalScript (e ) catch | err | log .warn ("evaljs: {any}" , .{err });
448+ self .evalScript (script ) catch | err | log .warn ("evaljs: {any}" , .{err });
459449 try parser .documentHTMLSetCurrentScript (html_doc , null );
460450 }
461451
@@ -472,9 +462,9 @@ pub const Page = struct {
472462 _ = try parser .eventTargetDispatchEvent (parser .toEventTarget (parser .DocumentHTML , html_doc ), evt );
473463
474464 // eval async scripts.
475- for (sasync .items ) | e | {
476- try parser .documentHTMLSetCurrentScript (html_doc , @ptrCast (e ));
477- self .evalScript (e ) catch | err | log .warn ("evaljs: {any}" , .{err });
465+ for (sasync .items ) | s | {
466+ try parser .documentHTMLSetCurrentScript (html_doc , @ptrCast (s . element ));
467+ self .evalScript (s ) catch | err | log .warn ("evaljs: {any}" , .{err });
478468 try parser .documentHTMLSetCurrentScript (html_doc , null );
479469 }
480470
@@ -496,15 +486,15 @@ pub const Page = struct {
496486 // evalScript evaluates the src in priority.
497487 // if no src is present, we evaluate the text source.
498488 // https://html.spec.whatwg.org/multipage/scripting.html#script-processing-model
499- fn evalScript (self : * Page , e : * parser.Element ) ! void {
489+ fn evalScript (self : * Page , s : Script ) ! void {
500490 const alloc = self .arena .allocator ();
501491
502492 // https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-classic-script
503- const opt_src = try parser .elementGetAttribute (e , "src" );
493+ const opt_src = try parser .elementGetAttribute (s . element , "src" );
504494 if (opt_src ) | src | {
505495 log .debug ("starting GET {s}" , .{src });
506496
507- self .fetchScript (src ) catch | err | {
497+ self .fetchScript (s ) catch | err | {
508498 switch (err ) {
509499 FetchError .BadStatusCode = > return err ,
510500
@@ -523,26 +513,10 @@ pub const Page = struct {
523513 return ;
524514 }
525515
526- var try_catch : jsruntime.TryCatch = undefined ;
527- try_catch .init (self .session .env );
528- defer try_catch .deinit ();
529-
530- const opt_text = try parser .nodeTextContent (parser .elementToNode (e ));
516+ // TODO handle charset attribute
517+ const opt_text = try parser .nodeTextContent (parser .elementToNode (s .element ));
531518 if (opt_text ) | text | {
532- // TODO handle charset attribute
533- const res = self .session .env .exec (text , "" ) catch {
534- if (try try_catch .err (alloc , self .session .env )) | msg | {
535- defer alloc .free (msg );
536- log .info ("eval inline {s}: {s}" , .{ text , msg });
537- }
538- return ;
539- };
540-
541- if (builtin .mode == .Debug ) {
542- const msg = try res .toString (alloc , self .session .env );
543- defer alloc .free (msg );
544- log .debug ("eval inline {s}" , .{msg });
545- }
519+ try s .eval (alloc , self .session .env , text );
546520 return ;
547521 }
548522
@@ -559,14 +533,14 @@ pub const Page = struct {
559533
560534 // fetchScript senf a GET request to the src and execute the script
561535 // received.
562- fn fetchScript (self : * Page , src : [] const u8 ) ! void {
536+ fn fetchScript (self : * Page , s : Script ) ! void {
563537 const alloc = self .arena .allocator ();
564538
565- log .debug ("starting fetch script {s}" , .{src });
539+ log .debug ("starting fetch script {s}" , .{s . src });
566540
567541 var buffer : [1024 ]u8 = undefined ;
568542 var b : []u8 = buffer [0.. ];
569- const u = try std .Uri .resolve_inplace (self .uri , src , & b );
543+ const u = try std .Uri .resolve_inplace (self .uri , s . src , & b );
570544
571545 var fetchres = try self .session .loader .get (alloc , u );
572546 defer fetchres .deinit ();
@@ -584,35 +558,73 @@ pub const Page = struct {
584558 // check no body
585559 if (body .len == 0 ) return FetchError .NoBody ;
586560
587- var try_catch : jsruntime.TryCatch = undefined ;
588- try_catch .init (self .session .env );
589- defer try_catch .deinit ();
561+ try s .eval (alloc , self .session .env , body );
562+ }
590563
591- const res = self .session .env .exec (body , src ) catch {
592- if (try try_catch .err (alloc , self .session .env )) | msg | {
593- defer alloc .free (msg );
594- log .info ("eval remote {s}: {s}" , .{ src , msg });
595- }
596- return FetchError .JsErr ;
564+ const Script = struct {
565+ element : * parser.Element ,
566+ kind : Kind ,
567+ isasync : bool ,
568+
569+ src : []const u8 ,
570+
571+ const Kind = enum {
572+ unknown ,
573+ javascript ,
574+ module ,
597575 };
598576
599- if (builtin .mode == .Debug ) {
600- const msg = try res .toString (alloc , self .session .env );
601- defer alloc .free (msg );
602- log .debug ("eval remote {s}: {s}" , .{ src , msg });
577+ fn init (e : * parser.Element ) ! ? Script {
578+ // ignore non-script tags
579+ const tag = try parser .elementHTMLGetTagType (@as (* parser .ElementHTML , @ptrCast (e )));
580+ if (tag != .script ) return null ;
581+
582+ return .{
583+ .element = e ,
584+ .kind = kind (try parser .elementGetAttribute (e , "type" )),
585+ .isasync = try parser .elementGetAttribute (e , "async" ) != null ,
586+
587+ .src = try parser .elementGetAttribute (e , "src" ) orelse "inline" ,
588+ };
603589 }
604- }
605590
606- // > type
607- // > Attribute is not set (default), an empty string, or a JavaScript MIME
608- // > type indicates that the script is a "classic script", containing
609- // > JavaScript code.
610- // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attribute_is_not_set_default_an_empty_string_or_a_javascript_mime_type
611- fn isJS (stype : ? []const u8 ) bool {
612- if (stype == null or stype .? .len == 0 ) return true ;
613- if (std .mem .eql (u8 , stype .? , "application/javascript" )) return true ;
614- if (! std .mem .eql (u8 , stype .? , "module" )) return true ;
615-
616- return false ;
617- }
591+ // > type
592+ // > Attribute is not set (default), an empty string, or a JavaScript MIME
593+ // > type indicates that the script is a "classic script", containing
594+ // > JavaScript code.
595+ // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attribute_is_not_set_default_an_empty_string_or_a_javascript_mime_type
596+ fn kind (stype : ? []const u8 ) Kind {
597+ if (stype == null or stype .? .len == 0 ) return .javascript ;
598+ if (std .mem .eql (u8 , stype .? , "application/javascript" )) return .javascript ;
599+ if (! std .mem .eql (u8 , stype .? , "module" )) return .module ;
600+
601+ return .unknown ;
602+ }
603+
604+ fn eval (self : Script , alloc : std.mem.Allocator , env : Env , body : []const u8 ) ! void {
605+ switch (self .kind ) {
606+ .unknown = > return error .UnknownScript ,
607+ .javascript = > {},
608+ .module = > {},
609+ }
610+
611+ var try_catch : jsruntime.TryCatch = undefined ;
612+ try_catch .init (env );
613+ defer try_catch .deinit ();
614+
615+ const res = env .exec (body , self .src ) catch {
616+ if (try try_catch .err (alloc , env )) | msg | {
617+ defer alloc .free (msg );
618+ log .info ("eval script {s}: {s}" , .{ self .src , msg });
619+ }
620+ return FetchError .JsErr ;
621+ };
622+
623+ if (builtin .mode == .Debug ) {
624+ const msg = try res .toString (alloc , env );
625+ defer alloc .free (msg );
626+ log .debug ("eval script {s}: {s}" , .{ self .src , msg });
627+ }
628+ }
629+ };
618630};
0 commit comments