mirror of
				https://github.com/lightpanda-io/browser.git
				synced 2025-10-30 07:31:47 +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
	 Pierre Tachoire
					Pierre Tachoire