mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-29 15:13:28 +00:00
storage: first implementation of webstorage API
This commit is contained in:
@@ -6,6 +6,7 @@ const DOM = @import("dom/dom.zig");
|
|||||||
const HTML = @import("html/html.zig");
|
const HTML = @import("html/html.zig");
|
||||||
const Events = @import("events/event.zig");
|
const Events = @import("events/event.zig");
|
||||||
const XHR = @import("xhr/xhr.zig");
|
const XHR = @import("xhr/xhr.zig");
|
||||||
|
const Storage = @import("storage/storage.zig");
|
||||||
|
|
||||||
pub const HTMLDocument = @import("html/document.zig").HTMLDocument;
|
pub const HTMLDocument = @import("html/document.zig").HTMLDocument;
|
||||||
|
|
||||||
@@ -16,4 +17,5 @@ pub const Interfaces = generate.Tuple(.{
|
|||||||
Events.Interfaces,
|
Events.Interfaces,
|
||||||
HTML.Interfaces,
|
HTML.Interfaces,
|
||||||
XHR.Interfaces,
|
XHR.Interfaces,
|
||||||
|
Storage.Interfaces,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ const apiweb = @import("../apiweb.zig");
|
|||||||
const Window = @import("../html/window.zig").Window;
|
const Window = @import("../html/window.zig").Window;
|
||||||
const Walker = @import("../dom/walker.zig").WalkerDepthFirst;
|
const Walker = @import("../dom/walker.zig").WalkerDepthFirst;
|
||||||
|
|
||||||
|
const storage = @import("../storage/storage.zig");
|
||||||
|
|
||||||
const FetchResult = std.http.Client.FetchResult;
|
const FetchResult = std.http.Client.FetchResult;
|
||||||
|
|
||||||
const log = std.log.scoped(.browser);
|
const log = std.log.scoped(.browser);
|
||||||
@@ -69,6 +71,8 @@ pub const Session = struct {
|
|||||||
env: Env = undefined,
|
env: Env = undefined,
|
||||||
loop: Loop,
|
loop: Loop,
|
||||||
window: Window,
|
window: Window,
|
||||||
|
// TODO move the shed to the browser?
|
||||||
|
storageShed: storage.Shed,
|
||||||
|
|
||||||
jstypes: [Types.len]usize = undefined,
|
jstypes: [Types.len]usize = undefined,
|
||||||
|
|
||||||
@@ -81,6 +85,7 @@ pub const Session = struct {
|
|||||||
.window = Window.create(null),
|
.window = Window.create(null),
|
||||||
.loader = Loader.init(alloc),
|
.loader = Loader.init(alloc),
|
||||||
.loop = try Loop.init(alloc),
|
.loop = try Loop.init(alloc),
|
||||||
|
.storageShed = storage.Shed.init(alloc),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.env = try Env.init(self.arena.allocator(), &self.loop);
|
self.env = try Env.init(self.arena.allocator(), &self.loop);
|
||||||
@@ -95,6 +100,7 @@ pub const Session = struct {
|
|||||||
|
|
||||||
self.loader.deinit();
|
self.loader.deinit();
|
||||||
self.loop.deinit();
|
self.loop.deinit();
|
||||||
|
self.storageShed.deinit();
|
||||||
self.alloc.destroy(self);
|
self.alloc.destroy(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,6 +122,7 @@ pub const Page = struct {
|
|||||||
// handle url
|
// handle url
|
||||||
rawuri: ?[]const u8 = null,
|
rawuri: ?[]const u8 = null,
|
||||||
uri: std.Uri = undefined,
|
uri: std.Uri = undefined,
|
||||||
|
origin: ?[]const u8 = null,
|
||||||
|
|
||||||
raw_data: ?[]const u8 = null,
|
raw_data: ?[]const u8 = null,
|
||||||
|
|
||||||
@@ -169,6 +176,15 @@ pub const Page = struct {
|
|||||||
self.rawuri = try alloc.dupe(u8, uri);
|
self.rawuri = try alloc.dupe(u8, uri);
|
||||||
self.uri = std.Uri.parse(self.rawuri.?) catch try std.Uri.parseWithoutScheme(self.rawuri.?);
|
self.uri = std.Uri.parse(self.rawuri.?) catch try std.Uri.parseWithoutScheme(self.rawuri.?);
|
||||||
|
|
||||||
|
// prepare origin value.
|
||||||
|
var buf = std.ArrayList(u8).init(alloc);
|
||||||
|
defer buf.deinit();
|
||||||
|
try self.uri.writeToStream(.{
|
||||||
|
.scheme = true,
|
||||||
|
.authority = true,
|
||||||
|
}, buf.writer());
|
||||||
|
self.origin = try buf.toOwnedSlice();
|
||||||
|
|
||||||
// TODO handle fragment in url.
|
// TODO handle fragment in url.
|
||||||
|
|
||||||
// load the data
|
// load the data
|
||||||
@@ -237,6 +253,9 @@ pub const Page = struct {
|
|||||||
// TODO set the referrer to the document.
|
// TODO set the referrer to the document.
|
||||||
|
|
||||||
self.session.window.replaceDocument(html_doc);
|
self.session.window.replaceDocument(html_doc);
|
||||||
|
self.session.window.setStorageShelf(
|
||||||
|
try self.session.storageShed.getOrPut(self.origin orelse "null"),
|
||||||
|
);
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/#read-html
|
// https://html.spec.whatwg.org/#read-html
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ const parser = @import("../netsurf.zig");
|
|||||||
|
|
||||||
const EventTarget = @import("../dom/event_target.zig").EventTarget;
|
const EventTarget = @import("../dom/event_target.zig").EventTarget;
|
||||||
|
|
||||||
|
const storage = @import("../storage/storage.zig");
|
||||||
|
|
||||||
// https://dom.spec.whatwg.org/#interface-window-extensions
|
// https://dom.spec.whatwg.org/#interface-window-extensions
|
||||||
// https://html.spec.whatwg.org/multipage/nav-history-apis.html#window
|
// https://html.spec.whatwg.org/multipage/nav-history-apis.html#window
|
||||||
pub const Window = struct {
|
pub const Window = struct {
|
||||||
@@ -17,6 +19,8 @@ pub const Window = struct {
|
|||||||
document: ?*parser.DocumentHTML = null,
|
document: ?*parser.DocumentHTML = null,
|
||||||
target: []const u8,
|
target: []const u8,
|
||||||
|
|
||||||
|
storageShelf: ?*storage.Shelf = null,
|
||||||
|
|
||||||
pub fn create(target: ?[]const u8) Window {
|
pub fn create(target: ?[]const u8) Window {
|
||||||
return Window{
|
return Window{
|
||||||
.target = target orelse "",
|
.target = target orelse "",
|
||||||
@@ -27,6 +31,10 @@ pub const Window = struct {
|
|||||||
self.document = doc;
|
self.document = doc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn setStorageShelf(self: *Window, shelf: *storage.Shelf) void {
|
||||||
|
self.storageShelf = shelf;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_window(self: *Window) *Window {
|
pub fn get_window(self: *Window) *Window {
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
@@ -46,4 +54,14 @@ pub const Window = struct {
|
|||||||
pub fn get_name(self: *Window) []const u8 {
|
pub fn get_name(self: *Window) []const u8 {
|
||||||
return self.target;
|
return self.target;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_localStorage(self: *Window) !*storage.Bottle {
|
||||||
|
if (self.storageShelf == null) return parser.DOMError.NotSupported;
|
||||||
|
return &self.storageShelf.?.bucket.local;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_sessionStorage(self: *Window) !*storage.Bottle {
|
||||||
|
if (self.storageShelf == null) return parser.DOMError.NotSupported;
|
||||||
|
return &self.storageShelf.?.bucket.session;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ const jsruntime = @import("jsruntime");
|
|||||||
const parser = @import("netsurf.zig");
|
const parser = @import("netsurf.zig");
|
||||||
const apiweb = @import("apiweb.zig");
|
const apiweb = @import("apiweb.zig");
|
||||||
const Window = @import("html/window.zig").Window;
|
const Window = @import("html/window.zig").Window;
|
||||||
|
const storage = @import("storage/storage.zig");
|
||||||
|
|
||||||
const html_test = @import("html_test.zig").html;
|
const html_test = @import("html_test.zig").html;
|
||||||
|
|
||||||
@@ -20,9 +21,13 @@ fn execJS(
|
|||||||
try js_env.start(alloc);
|
try js_env.start(alloc);
|
||||||
defer js_env.stop();
|
defer js_env.stop();
|
||||||
|
|
||||||
|
var storageShelf = storage.Shelf.init(alloc);
|
||||||
|
defer storageShelf.deinit();
|
||||||
|
|
||||||
// alias global as self and window
|
// alias global as self and window
|
||||||
var window = Window.create(null);
|
var window = Window.create(null);
|
||||||
window.replaceDocument(doc);
|
window.replaceDocument(doc);
|
||||||
|
window.setStorageShelf(&storageShelf);
|
||||||
try js_env.bindGlobal(window);
|
try js_env.bindGlobal(window);
|
||||||
|
|
||||||
// launch shellExec
|
// launch shellExec
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ const parser = @import("netsurf.zig");
|
|||||||
const apiweb = @import("apiweb.zig");
|
const apiweb = @import("apiweb.zig");
|
||||||
const Window = @import("html/window.zig").Window;
|
const Window = @import("html/window.zig").Window;
|
||||||
const xhr = @import("xhr/xhr.zig");
|
const xhr = @import("xhr/xhr.zig");
|
||||||
|
const storage = @import("storage/storage.zig");
|
||||||
|
|
||||||
const documentTestExecFn = @import("dom/document.zig").testExecFn;
|
const documentTestExecFn = @import("dom/document.zig").testExecFn;
|
||||||
const HTMLDocumentTestExecFn = @import("html/document.zig").testExecFn;
|
const HTMLDocumentTestExecFn = @import("html/document.zig").testExecFn;
|
||||||
@@ -28,6 +29,7 @@ const ProcessingInstructionTestExecFn = @import("dom/processing_instruction.zig"
|
|||||||
const EventTestExecFn = @import("events/event.zig").testExecFn;
|
const EventTestExecFn = @import("events/event.zig").testExecFn;
|
||||||
const XHRTestExecFn = xhr.testExecFn;
|
const XHRTestExecFn = xhr.testExecFn;
|
||||||
const ProgressEventTestExecFn = @import("xhr/progress_event.zig").testExecFn;
|
const ProgressEventTestExecFn = @import("xhr/progress_event.zig").testExecFn;
|
||||||
|
const StorageTestExecFn = storage.testExecFn;
|
||||||
|
|
||||||
pub const Types = jsruntime.reflect(apiweb.Interfaces);
|
pub const Types = jsruntime.reflect(apiweb.Interfaces);
|
||||||
|
|
||||||
@@ -45,6 +47,9 @@ fn testExecFn(
|
|||||||
try js_env.start(alloc);
|
try js_env.start(alloc);
|
||||||
defer js_env.stop();
|
defer js_env.stop();
|
||||||
|
|
||||||
|
var storageShelf = storage.Shelf.init(alloc);
|
||||||
|
defer storageShelf.deinit();
|
||||||
|
|
||||||
// document
|
// document
|
||||||
const file = try std.fs.cwd().openFile("test.html", .{});
|
const file = try std.fs.cwd().openFile("test.html", .{});
|
||||||
defer file.close();
|
defer file.close();
|
||||||
@@ -56,7 +61,10 @@ fn testExecFn(
|
|||||||
|
|
||||||
// alias global as self and window
|
// alias global as self and window
|
||||||
var window = Window.create(null);
|
var window = Window.create(null);
|
||||||
|
|
||||||
window.replaceDocument(doc);
|
window.replaceDocument(doc);
|
||||||
|
window.setStorageShelf(&storageShelf);
|
||||||
|
|
||||||
try js_env.bindGlobal(window);
|
try js_env.bindGlobal(window);
|
||||||
|
|
||||||
// run test
|
// run test
|
||||||
@@ -86,6 +94,7 @@ fn testsAllExecFn(
|
|||||||
XHRTestExecFn,
|
XHRTestExecFn,
|
||||||
ProgressEventTestExecFn,
|
ProgressEventTestExecFn,
|
||||||
ProcessingInstructionTestExecFn,
|
ProcessingInstructionTestExecFn,
|
||||||
|
StorageTestExecFn,
|
||||||
};
|
};
|
||||||
|
|
||||||
inline for (testFns) |testFn| {
|
inline for (testFns) |testFn| {
|
||||||
|
|||||||
214
src/storage/storage.zig
Normal file
214
src/storage/storage.zig
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const jsruntime = @import("jsruntime");
|
||||||
|
const Case = jsruntime.test_utils.Case;
|
||||||
|
const checkCases = jsruntime.test_utils.checkCases;
|
||||||
|
const generate = @import("../generate.zig");
|
||||||
|
|
||||||
|
const DOMError = @import("../netsurf.zig").DOMError;
|
||||||
|
|
||||||
|
const log = std.log.scoped(.storage);
|
||||||
|
|
||||||
|
pub const Interfaces = generate.Tuple(.{
|
||||||
|
Bottle,
|
||||||
|
});
|
||||||
|
|
||||||
|
// See https://storage.spec.whatwg.org/#model for storage hierarchy.
|
||||||
|
// A Shed contains map of Shelves. The key is the document's origin.
|
||||||
|
// A Shelf contains on default Bucket (it could contain many in the future).
|
||||||
|
// A Bucket contains a local and a session Bottle.
|
||||||
|
// A Bottle stores a map of strings and is exposed to the JS.
|
||||||
|
|
||||||
|
pub const Shed = struct {
|
||||||
|
const Map = std.StringHashMapUnmanaged(Shelf);
|
||||||
|
|
||||||
|
alloc: std.mem.Allocator,
|
||||||
|
map: Map,
|
||||||
|
|
||||||
|
pub fn init(alloc: std.mem.Allocator) Shed {
|
||||||
|
return .{
|
||||||
|
.alloc = alloc,
|
||||||
|
.map = .{},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *Shed) void {
|
||||||
|
// loop hover each KV and free the memory.
|
||||||
|
var it = self.map.iterator();
|
||||||
|
while (it.next()) |entry| {
|
||||||
|
entry.value_ptr.deinit();
|
||||||
|
self.alloc.free(entry.key_ptr.*);
|
||||||
|
}
|
||||||
|
self.map.deinit(self.alloc);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getOrPut(self: *Shed, origin: []const u8) !*Shelf {
|
||||||
|
const shelf = self.map.getPtr(origin);
|
||||||
|
if (shelf) |s| return s;
|
||||||
|
|
||||||
|
const oorigin = try self.alloc.dupe(u8, origin);
|
||||||
|
try self.map.put(self.alloc, oorigin, Shelf.init(self.alloc));
|
||||||
|
return self.map.getPtr(origin).?;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Shelf = struct {
|
||||||
|
bucket: Bucket,
|
||||||
|
|
||||||
|
pub fn init(alloc: std.mem.Allocator) Shelf {
|
||||||
|
return .{ .bucket = Bucket.init(alloc) };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *Shelf) void {
|
||||||
|
self.bucket.deinit();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Bucket = struct {
|
||||||
|
local: Bottle,
|
||||||
|
session: Bottle,
|
||||||
|
|
||||||
|
pub fn init(alloc: std.mem.Allocator) Bucket {
|
||||||
|
return .{
|
||||||
|
.local = Bottle.init(alloc),
|
||||||
|
.session = Bottle.init(alloc),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *Bucket) void {
|
||||||
|
self.local.deinit();
|
||||||
|
self.session.deinit();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/webstorage.html#the-storage-interface
|
||||||
|
pub const Bottle = struct {
|
||||||
|
pub const mem_guarantied = true;
|
||||||
|
const Map = std.StringHashMapUnmanaged([]const u8);
|
||||||
|
|
||||||
|
// allocator is stored. we don't use the JS env allocator b/c the storage
|
||||||
|
// data could exists longer than a js env lifetime.
|
||||||
|
alloc: std.mem.Allocator,
|
||||||
|
map: Map,
|
||||||
|
|
||||||
|
pub fn init(alloc: std.mem.Allocator) Bottle {
|
||||||
|
return .{
|
||||||
|
.alloc = alloc,
|
||||||
|
.map = .{},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// loop hover each KV and free the memory.
|
||||||
|
fn free(self: *Bottle) void {
|
||||||
|
var it = self.map.iterator();
|
||||||
|
while (it.next()) |entry| {
|
||||||
|
self.alloc.free(entry.key_ptr.*);
|
||||||
|
self.alloc.free(entry.value_ptr.*);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *Bottle) void {
|
||||||
|
self.free();
|
||||||
|
self.map.deinit(self.alloc);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_length(self: *Bottle) u32 {
|
||||||
|
return @intCast(self.map.count());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn _key(self: *Bottle, idx: u32) ?[]const u8 {
|
||||||
|
if (idx >= self.map.count()) return null;
|
||||||
|
|
||||||
|
var it = self.map.valueIterator();
|
||||||
|
var i: u32 = 0;
|
||||||
|
while (it.next()) |v| {
|
||||||
|
if (i == idx) return v.*;
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn _getItem(self: *Bottle, k: []const u8) ?[]const u8 {
|
||||||
|
return self.map.get(k);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn _setItem(self: *Bottle, k: []const u8, v: []const u8) !void {
|
||||||
|
const old = self.map.get(k);
|
||||||
|
if (old != null and std.mem.eql(u8, v, old.?)) return;
|
||||||
|
|
||||||
|
// owns k and v by copying them.
|
||||||
|
const kk = try self.alloc.dupe(u8, k);
|
||||||
|
errdefer self.alloc.free(kk);
|
||||||
|
const vv = try self.alloc.dupe(u8, v);
|
||||||
|
errdefer self.alloc.free(vv);
|
||||||
|
|
||||||
|
self.map.put(self.alloc, kk, vv) catch |e| {
|
||||||
|
log.debug("set item: {any}", .{e});
|
||||||
|
return DOMError.QuotaExceeded;
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO dispatch event
|
||||||
|
// > Broadcast this with key, oldValue, and value.
|
||||||
|
// https://html.spec.whatwg.org/multipage/webstorage.html#the-storageevent-interface
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn _removeItem(self: *Bottle, k: []const u8) !void {
|
||||||
|
const old = self.map.fetchRemove(k);
|
||||||
|
if (old == null) return;
|
||||||
|
|
||||||
|
// TODO dispatch event
|
||||||
|
// > Broadcast this with key, oldValue, and null.
|
||||||
|
// https://html.spec.whatwg.org/multipage/webstorage.html#the-storageevent-interface
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn _clear(self: *Bottle) void {
|
||||||
|
self.free();
|
||||||
|
self.map.clearRetainingCapacity();
|
||||||
|
|
||||||
|
// TODO dispatch event
|
||||||
|
// > Broadcast this with null, null, and null.
|
||||||
|
// https://html.spec.whatwg.org/multipage/webstorage.html#the-storageevent-interface
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Tests
|
||||||
|
// -----
|
||||||
|
|
||||||
|
pub fn testExecFn(
|
||||||
|
_: std.mem.Allocator,
|
||||||
|
js_env: *jsruntime.Env,
|
||||||
|
) anyerror!void {
|
||||||
|
var storage = [_]Case{
|
||||||
|
.{ .src = "localStorage.length", .ex = "0" },
|
||||||
|
|
||||||
|
.{ .src = "localStorage.setItem('foo', 'bar')", .ex = "undefined" },
|
||||||
|
.{ .src = "localStorage.length", .ex = "1" },
|
||||||
|
.{ .src = "localStorage.getItem('foo')", .ex = "bar" },
|
||||||
|
.{ .src = "localStorage.removeItem('foo')", .ex = "undefined" },
|
||||||
|
.{ .src = "localStorage.length", .ex = "0" },
|
||||||
|
|
||||||
|
// .{ .src = "localStorage['foo'] = 'bar'", .ex = "undefined" },
|
||||||
|
// .{ .src = "localStorage['foo']", .ex = "bar" },
|
||||||
|
// .{ .src = "localStorage.length", .ex = "1" },
|
||||||
|
|
||||||
|
.{ .src = "localStorage.clear()", .ex = "undefined" },
|
||||||
|
.{ .src = "localStorage.length", .ex = "0" },
|
||||||
|
};
|
||||||
|
try checkCases(js_env, &storage);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "storage bottle" {
|
||||||
|
var bottle = Bottle.init(std.testing.allocator);
|
||||||
|
defer bottle.deinit();
|
||||||
|
|
||||||
|
try std.testing.expect(0 == bottle.get_length());
|
||||||
|
try std.testing.expect(null == bottle._getItem("foo"));
|
||||||
|
|
||||||
|
try bottle._setItem("foo", "bar");
|
||||||
|
try std.testing.expect(std.mem.eql(u8, "bar", bottle._getItem("foo").?));
|
||||||
|
|
||||||
|
try bottle._removeItem("foo");
|
||||||
|
|
||||||
|
try std.testing.expect(0 == bottle.get_length());
|
||||||
|
try std.testing.expect(null == bottle._getItem("foo"));
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ const jsruntime = @import("jsruntime");
|
|||||||
const Loop = jsruntime.Loop;
|
const Loop = jsruntime.Loop;
|
||||||
const Env = jsruntime.Env;
|
const Env = jsruntime.Env;
|
||||||
const Window = @import("../html/window.zig").Window;
|
const Window = @import("../html/window.zig").Window;
|
||||||
|
const storage = @import("../storage/storage.zig");
|
||||||
|
|
||||||
const Types = @import("../main_wpt.zig").Types;
|
const Types = @import("../main_wpt.zig").Types;
|
||||||
|
|
||||||
@@ -34,6 +35,9 @@ pub fn run(arena: *std.heap.ArenaAllocator, comptime dir: []const u8, f: []const
|
|||||||
var js_env = try Env.init(alloc, &loop);
|
var js_env = try Env.init(alloc, &loop);
|
||||||
defer js_env.deinit();
|
defer js_env.deinit();
|
||||||
|
|
||||||
|
var storageShelf = storage.Shelf.init(alloc);
|
||||||
|
defer storageShelf.deinit();
|
||||||
|
|
||||||
// load user-defined types in JS env
|
// load user-defined types in JS env
|
||||||
var js_types: [Types.len]usize = undefined;
|
var js_types: [Types.len]usize = undefined;
|
||||||
try js_env.load(&js_types);
|
try js_env.load(&js_types);
|
||||||
@@ -54,6 +58,7 @@ pub fn run(arena: *std.heap.ArenaAllocator, comptime dir: []const u8, f: []const
|
|||||||
// setup global env vars.
|
// setup global env vars.
|
||||||
var window = Window.create(null);
|
var window = Window.create(null);
|
||||||
window.replaceDocument(html_doc);
|
window.replaceDocument(html_doc);
|
||||||
|
window.setStorageShelf(&storageShelf);
|
||||||
try js_env.bindGlobal(window);
|
try js_env.bindGlobal(window);
|
||||||
|
|
||||||
// thanks to the arena, we don't need to deinit res.
|
// thanks to the arena, we don't need to deinit res.
|
||||||
|
|||||||
Reference in New Issue
Block a user