Skip to content

Commit 6ad6d27

Browse files
committed
enable conditionnal loading for polyfill
1 parent 8a80f0b commit 6ad6d27

File tree

9 files changed

+94
-30
lines changed

9 files changed

+94
-30
lines changed

src/browser/page.zig

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ pub const Page = struct {
9595

9696
state_pool: *std.heap.MemoryPool(State),
9797

98+
polyfill_loader: polyfill.Loader = .{},
99+
98100
pub fn init(self: *Page, arena: Allocator, session: *Session) !void {
99101
const browser = session.browser;
100102
self.* = .{
@@ -117,10 +119,7 @@ pub const Page = struct {
117119
}),
118120
.main_context = undefined,
119121
};
120-
self.main_context = try session.executor.createJsContext(&self.window, self, self, true);
121-
122-
// load polyfills
123-
try polyfill.load(self.arena, self.main_context);
122+
self.main_context = try session.executor.createJsContext(&self.window, self, self, true, Env.GlobalMissingCallback.init(&self.polyfill_loader));
124123

125124
_ = try session.browser.app.loop.timeout(1 * std.time.ns_per_ms, &self.microtask_node);
126125
// message loop must run only non-test env
@@ -1017,7 +1016,7 @@ const Script = struct {
10171016

10181017
const src: []const u8 = blk: {
10191018
const s = self.src orelse break :blk page.url.raw;
1020-
break :blk try URL.stitch(page.arena, s, page.url.raw, .{.alloc = .if_needed});
1019+
break :blk try URL.stitch(page.arena, s, page.url.raw, .{ .alloc = .if_needed });
10211020
};
10221021

10231022
// if self.src is null, then this is an inline script, and it should

src/browser/polyfill/fetch.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ test "Browser.fetch" {
1616
var runner = try testing.jsRunner(testing.tracking_allocator, .{});
1717
defer runner.deinit();
1818

19-
try @import("polyfill.zig").load(testing.allocator, runner.page.main_context);
19+
try @import("polyfill.zig").Loader.load("fetch", source, runner.page.main_context);
2020

2121
try runner.testCases(&.{
2222
.{

src/browser/polyfill/polyfill.zig

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,25 +23,36 @@ const log = @import("../../log.zig");
2323
const Allocator = std.mem.Allocator;
2424
const Env = @import("../env.zig").Env;
2525

26-
const modules = [_]struct {
27-
name: []const u8,
28-
source: []const u8,
29-
}{
30-
.{ .name = "polyfill-fetch", .source = @import("fetch.zig").source },
31-
};
26+
pub const Loader = struct {
27+
done: struct {
28+
fetch: bool = false,
29+
} = .{},
3230

33-
pub fn load(allocator: Allocator, js_context: *Env.JsContext) !void {
34-
var try_catch: Env.TryCatch = undefined;
35-
try_catch.init(js_context);
36-
defer try_catch.deinit();
31+
pub fn load(name: []const u8, source: []const u8, js_context: *Env.JsContext) !void {
32+
var try_catch: Env.TryCatch = undefined;
33+
try_catch.init(js_context);
34+
defer try_catch.deinit();
3735

38-
for (modules) |m| {
39-
_ = js_context.exec(m.source, m.name) catch |err| {
40-
if (try try_catch.err(allocator)) |msg| {
41-
defer allocator.free(msg);
42-
log.fatal(.app, "polyfill error", .{ .name = m.name, .err = msg });
36+
_ = js_context.exec(source, name) catch |err| {
37+
if (try try_catch.err(js_context.call_arena)) |msg| {
38+
log.fatal(.app, "polyfill error", .{ .name = name, .err = msg });
4339
}
4440
return err;
4541
};
4642
}
47-
}
43+
44+
pub fn missing(self: *Loader, name: []const u8, js_context: *Env.JsContext) bool {
45+
if (!self.done.fetch and std.mem.eql(u8, name, "fetch")) {
46+
// load the polyfill once.
47+
self.done.fetch = true;
48+
49+
const _name = "fetch";
50+
const source = @import("fetch.zig").source;
51+
log.debug(.polyfill, "dynamic load", .{ .property = name });
52+
load(_name, source, js_context) catch |err| {
53+
log.fatal(.app, "polyfill load", .{ .name = name, .err = err });
54+
};
55+
}
56+
return false;
57+
}
58+
};

src/cdp/cdp.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -569,7 +569,7 @@ const IsolatedWorld = struct {
569569
// Currently we have only 1 page/frame and thus also only 1 state in the isolate world.
570570
pub fn createContext(self: *IsolatedWorld, page: *Page) !void {
571571
if (self.executor.js_context != null) return error.Only1IsolatedContextSupported;
572-
_ = try self.executor.createJsContext(&page.window, page, {}, false);
572+
_ = try self.executor.createJsContext(&page.window, page, {}, false, null);
573573
}
574574
};
575575

src/cdp/domains/page.zig

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -284,9 +284,6 @@ pub fn pageCreated(bc: anytype, page: *Page) !void {
284284
if (bc.isolated_world) |*isolated_world| {
285285
// We need to recreate the isolated world context
286286
try isolated_world.createContext(page);
287-
288-
const polyfill = @import("../../browser/polyfill/polyfill.zig");
289-
try polyfill.load(bc.arena, &isolated_world.executor.js_context.?);
290287
}
291288
}
292289

src/log.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ pub const Scope = enum {
3939
unknown_prop,
4040
web_api,
4141
xhr,
42+
polyfill,
4243
};
4344

4445
const Opts = struct {

src/main_wpt.zig

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,6 @@ fn run(
126126
});
127127
defer runner.deinit();
128128

129-
try polyfill.load(arena, runner.page.main_context);
130-
131129
// loop over the scripts.
132130
const doc = parser.documentHTMLToDocument(runner.page.window.document);
133131
const scripts = try parser.documentGetElementsByTagName(doc, "script");

src/runtime/js.zig

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,7 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
346346
// when the handle_scope is freed.
347347
// We also maintain our own "context_arena" which allows us to have
348348
// all page related memory easily managed.
349-
pub fn createJsContext(self: *ExecutionWorld, global: anytype, state: State, module_loader: anytype, enter: bool) !*JsContext {
349+
pub fn createJsContext(self: *ExecutionWorld, global: anytype, state: State, module_loader: anytype, enter: bool, global_callback: ?GlobalMissingCallback) !*JsContext {
350350
std.debug.assert(self.js_context == null);
351351

352352
const ModuleLoader = switch (@typeInfo(@TypeOf(module_loader))) {
@@ -375,6 +375,30 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
375375
const global_template = js_global.getInstanceTemplate();
376376
global_template.setInternalFieldCount(1);
377377

378+
// Configure the missing property callback on the global
379+
// object.
380+
if (global_callback != null) {
381+
const configuration = v8.NamedPropertyHandlerConfiguration{
382+
.getter = struct {
383+
fn callback(c_name: ?*const v8.C_Name, raw_info: ?*const v8.C_PropertyCallbackInfo) callconv(.c) u8 {
384+
const info = v8.PropertyCallbackInfo.initFromV8(raw_info);
385+
const _isolate = info.getIsolate();
386+
const v8_context = _isolate.getCurrentContext();
387+
388+
const js_context: *JsContext = @ptrFromInt(v8_context.getEmbedderData(1).castTo(v8.BigInt).getUint64());
389+
390+
const property = valueToString(js_context.call_arena, .{ .handle = c_name.? }, _isolate, v8_context) catch "???";
391+
if (js_context.global_callback.?.missing(property, js_context)) {
392+
return v8.Intercepted.Yes;
393+
}
394+
return v8.Intercepted.No;
395+
}
396+
}.callback,
397+
.flags = v8.PropertyHandlerFlags.NonMasking | v8.PropertyHandlerFlags.OnlyInterceptStrings,
398+
};
399+
global_template.setNamedProperty(configuration, null);
400+
}
401+
378402
// All the FunctionTemplates that we created and setup in Env.init
379403
// are now going to get associated with our global instance.
380404
inline for (Types, 0..) |s, i| {
@@ -456,6 +480,7 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
456480
.ptr = safe_module_loader,
457481
.func = ModuleLoader.fetchModuleSource,
458482
},
483+
.global_callback = global_callback,
459484
};
460485

461486
var js_context = &self.js_context.?;
@@ -608,6 +633,9 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
608633
// necessary to lookup/store the dependent module in the module_cache.
609634
module_identifier: std.AutoHashMapUnmanaged(u32, []const u8) = .empty,
610635

636+
// Global callback is called on missing property.
637+
global_callback: ?GlobalMissingCallback = null,
638+
611639
const ModuleLoader = struct {
612640
ptr: *anyopaque,
613641
func: *const fn (ptr: *anyopaque, specifier: []const u8) anyerror!?[]const u8,
@@ -2546,6 +2574,35 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
25462574
self.callScopeEndFn(self.ptr);
25472575
}
25482576
};
2577+
2578+
// Callback called on global's property mssing.
2579+
// Return true to intercept the exectution or false to let the call
2580+
// continue the chain.
2581+
pub const GlobalMissingCallback = struct {
2582+
ptr: *anyopaque,
2583+
missingFn: *const fn (ptr: *anyopaque, name: []const u8, ctx: *JsContext) bool,
2584+
2585+
pub fn init(ptr: anytype) GlobalMissingCallback {
2586+
const T = @TypeOf(ptr);
2587+
const ptr_info = @typeInfo(T);
2588+
2589+
const gen = struct {
2590+
pub fn missing(pointer: *anyopaque, name: []const u8, ctx: *JsContext) bool {
2591+
const self: T = @ptrCast(@alignCast(pointer));
2592+
return ptr_info.pointer.child.missing(self, name, ctx);
2593+
}
2594+
};
2595+
2596+
return .{
2597+
.ptr = ptr,
2598+
.missingFn = gen.missing,
2599+
};
2600+
}
2601+
2602+
pub fn missing(self: GlobalMissingCallback, name: []const u8, ctx: *JsContext) bool {
2603+
return self.missingFn(self.ptr, name, ctx);
2604+
}
2605+
};
25492606
};
25502607
}
25512608

@@ -3431,7 +3488,7 @@ fn valueToDetailString(arena: Allocator, value: v8.Value, isolate: v8.Isolate, v
34313488
if (debugValueToString(arena, value.castTo(v8.Object), isolate, v8_context)) |ds| {
34323489
return ds;
34333490
} else |err| {
3434-
log.err(.js, "debug serialize value", .{.err = err});
3491+
log.err(.js, "debug serialize value", .{ .err = err });
34353492
}
34363493
}
34373494
}

src/runtime/testing.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ pub fn Runner(comptime State: type, comptime Global: type, comptime types: anyty
5353
state,
5454
{},
5555
true,
56+
null,
5657
);
5758
return self;
5859
}

0 commit comments

Comments
 (0)