Skip to content

Commit 7ac945b

Browse files
committed
browser: refacto script
1 parent 188d7a8 commit 7ac945b

File tree

1 file changed

+83
-71
lines changed

1 file changed

+83
-71
lines changed

src/browser/browser.zig

Lines changed: 83 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)