Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ lightpanda.id
/v8/
/build/
/src/html5ever/target/
src/snapshot.bin
8 changes: 0 additions & 8 deletions LICENSING.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,6 @@ List](https://spdx.org/licenses/).

The default license for this project is [AGPL-3.0-only](LICENSE).

## MIT

The following files are licensed under MIT:

```
src/polyfill/fetch.js
```

The following directories and their subdirectories are licensed under their
original upstream licenses:

Expand Down
26 changes: 26 additions & 0 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,12 @@ pub fn build(b: *Build) !void {

const git_commit = b.option([]const u8, "git_commit", "Current git commit");
const prebuilt_v8_path = b.option([]const u8, "prebuilt_v8_path", "Path to prebuilt libc_v8.a");
const snapshot_path = b.option([]const u8, "snapshot_path", "Path to v8 snapshot");

var opts = b.addOptions();
opts.addOption([]const u8, "version", manifest.version);
opts.addOption([]const u8, "git_commit", git_commit orelse "dev");
opts.addOption(?[]const u8, "snapshot_path", snapshot_path);

// Build step to install html5ever dependency.
const html5ever_argv = blk: {
Expand Down Expand Up @@ -112,6 +114,30 @@ pub fn build(b: *Build) !void {
run_step.dependOn(&run_cmd.step);
}

{
// snapshot creator
const exe = b.addExecutable(.{
.name = "lightpanda-snapshot-creator",
.use_llvm = true,
.root_module = b.createModule(.{
.root_source_file = b.path("src/main_snapshot_creator.zig"),
.target = target,
.optimize = optimize,
.imports = &.{
.{ .name = "lightpanda", .module = lightpanda_module },
},
}),
});
b.installArtifact(exe);

const run_cmd = b.addRunArtifact(exe);
if (b.args) |args| {
run_cmd.addArgs(args);
}
const run_step = b.step("snapshot_creator", "Generate a v8 snapshot");
run_step.dependOn(&run_cmd.step);
}

{
// test
const tests = b.addTest(.{
Expand Down
6 changes: 3 additions & 3 deletions build.zig.zon
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
.minimum_zig_version = "0.15.2",
.dependencies = .{
.v8 = .{
.url = "https://github.com/lightpanda-io/zig-v8-fork/archive/e047d2a4d5af5783763f0f6a652fab8982a08603.tar.gz",
.hash = "v8-0.0.0-xddH65gMBACRBQMM7EwmVgfi94FJyyX-0jpe5KhXYhfv",
.url = "https://github.com/lightpanda-io/zig-v8-fork/archive/0d64a3d5b36ac94067df3e13fddbf715caa6f391.tar.gz",
.hash = "v8-0.0.0-xddH65sfBAC8o3q41YxhOms5uY2fvMzBrsgN8IeCXZgE",
},
//.v8 = .{ .path = "../zig-v8-fork" }
//.v8 = .{ .path = "../zig-v8-fork" },
.@"boringssl-zig" = .{
.url = "git+https://github.com/Syndica/boringssl-zig.git#c53df00d06b02b755ad88bbf4d1202ed9687b096",
.hash = "boringssl-0.1.0-VtJeWehMAAA4RNnwRnzEvKcS9rjsR1QVRw1uJrwXxmVK",
Expand Down
6 changes: 6 additions & 0 deletions src/App.zig
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const Allocator = std.mem.Allocator;

const log = @import("log.zig");
const Http = @import("http/Http.zig");
const Snapshot = @import("browser/js/Snapshot.zig");
const Platform = @import("browser/js/Platform.zig");

const Notification = @import("Notification.zig");
Expand All @@ -34,6 +35,7 @@ const App = @This();
http: Http,
config: Config,
platform: Platform,
snapshot: Snapshot,
telemetry: Telemetry,
allocator: Allocator,
app_dir_path: ?[]const u8,
Expand Down Expand Up @@ -83,6 +85,9 @@ pub fn init(allocator: Allocator, config: Config) !*App {
app.platform = try Platform.init();
errdefer app.platform.deinit();

app.snapshot = try Snapshot.load(allocator);
errdefer app.snapshot.deinit(allocator);

app.app_dir_path = getAndMakeAppDir(allocator);

app.telemetry = try Telemetry.init(app, config.run_mode);
Expand All @@ -101,6 +106,7 @@ pub fn deinit(self: *App) void {
self.telemetry.deinit();
self.notification.deinit();
self.http.deinit();
self.snapshot.deinit(allocator);
self.platform.deinit();

allocator.destroy(self);
Expand Down
4 changes: 2 additions & 2 deletions src/browser/Browser.zig
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const Session = @import("Session.zig");
// A browser contains only one session.
const Browser = @This();

env: *js.Env,
env: js.Env,
app: *App,
session: ?Session,
allocator: Allocator,
Expand All @@ -48,7 +48,7 @@ notification: *Notification,
pub fn init(app: *App) !Browser {
const allocator = app.allocator;

const env = try js.Env.init(allocator, &app.platform, .{});
var env = try js.Env.init(allocator, &app.platform, &app.snapshot);
errdefer env.deinit();

const notification = try Notification.init(allocator, app.notification);
Expand Down
7 changes: 2 additions & 5 deletions src/browser/Page.zig
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@ const Scheduler = @import("Scheduler.zig");
const EventManager = @import("EventManager.zig");
const ScriptManager = @import("ScriptManager.zig");

const polyfill = @import("polyfill/polyfill.zig");

const Parser = @import("parser/Parser.zig");

const URL = @import("webapi/URL.zig");
Expand Down Expand Up @@ -124,8 +122,6 @@ _upgrading_element: ?*Node = null,
// List of custom elements that were created before their definition was registered
_undefined_custom_elements: std.ArrayList(*Element.Html.Custom) = .{},

_polyfill_loader: polyfill.Loader = .{},

// for heap allocations and managing WebAPI objects
_factory: Factory,

Expand Down Expand Up @@ -230,6 +226,7 @@ fn reset(self: *Page, comptime initializing: bool) !void {

self._parse_state = .pre;
self._load_state = .parsing;
self._parse_mode = .document;
self._attribute_lookup = .empty;
self._attribute_named_node_map_lookup = .empty;
self._event_manager = EventManager.init(self);
Expand All @@ -238,7 +235,7 @@ fn reset(self: *Page, comptime initializing: bool) !void {
errdefer self._script_manager.deinit();

if (comptime initializing == true) {
self.js = try self._session.executor.createContext(self, true, JS.GlobalMissingCallback.init(&self._polyfill_loader));
self.js = try self._session.executor.createContext(self, true);
errdefer self.js.deinit();
}

Expand Down
43 changes: 36 additions & 7 deletions src/browser/js/Context.zig
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,6 @@ module_identifier: std.AutoHashMapUnmanaged(u32, [:0]const u8) = .empty,
// the page's script manager
script_manager: ?*ScriptManager,

// Global callback is called on missing property.
global_callback: ?js.GlobalMissingCallback = null,

const ModuleEntry = struct {
// Can be null if we're asynchrously loading the module, in
// which case resolver_promise cannot be null.
Expand Down Expand Up @@ -645,7 +642,12 @@ pub fn mapZigInstanceToJs(self: *Context, js_obj_: ?v8.Object, value: anytype) !
.prototype_len = @intCast(resolved.prototype_chain.len),
.subtype = if (@hasDecl(JsApi.Meta, "subtype")) JsApi.Meta.subype else .node,
};
js_obj.setInternalField(0, v8.External.init(isolate, tao));

// Skip setting internal field for the global object (Window)
// Window accessors get the instance from context.page.window instead
if (resolved.class_id != @import("../webapi/Window.zig").JsApi.Meta.class_id) {
js_obj.setInternalField(0, v8.External.init(isolate, tao));
}
} else {
// If the struct is empty, we don't need to do all
// the TOA stuff and setting the internal data.
Expand Down Expand Up @@ -1028,7 +1030,7 @@ const valueToStringOpts = struct {
pub fn valueToString(self: *const Context, js_val: v8.Value, opts: valueToStringOpts) ![]u8 {
const allocator = opts.allocator orelse self.call_arena;
if (js_val.isSymbol()) {
const js_sym = v8.Symbol{.handle = js_val.handle};
const js_sym = v8.Symbol{ .handle = js_val.handle };
const js_sym_desc = js_sym.getDescription(self.isolate);
return self.valueToString(js_sym_desc, .{});
}
Expand All @@ -1039,7 +1041,7 @@ pub fn valueToString(self: *const Context, js_val: v8.Value, opts: valueToString
pub fn valueToStringZ(self: *const Context, js_val: v8.Value, opts: valueToStringOpts) ![:0]u8 {
const allocator = opts.allocator orelse self.call_arena;
if (js_val.isSymbol()) {
const js_sym = v8.Symbol{.handle = js_val.handle};
const js_sym = v8.Symbol{ .handle = js_val.handle };
const js_sym_desc = js_sym.getDescription(self.isolate);
return self.valueToStringZ(js_sym_desc, .{});
}
Expand Down Expand Up @@ -1094,7 +1096,7 @@ fn _debugValue(self: *const Context, js_val: v8.Value, seen: *std.AutoHashMapUnm
}

if (js_val.isSymbol()) {
const js_sym = v8.Symbol{.handle = js_val.handle};
const js_sym = v8.Symbol{ .handle = js_val.handle };
const js_sym_desc = js_sym.getDescription(self.isolate);
const js_sym_str = try self.valueToString(js_sym_desc, .{});
return writer.print("{s} (symbol)", .{js_sym_str});
Expand Down Expand Up @@ -1596,6 +1598,33 @@ pub fn typeTaggedAnyOpaque(comptime R: type, js_obj: v8.Object) !R {
return @constCast(@as(*const T, &.{}));
}

// Special case for Window: the global object doesn't have internal fields
// Window instance is stored in context.page.window instead
if (js_obj.internalFieldCount() == 0) {
// Normally, this would be an error. All JsObject that map to a Zig type
// are either `empty_with_no_proto` (handled above) or have an
// interalFieldCount. The only exception to that is the Window...
const isolate = js_obj.getIsolate();
const context = fromIsolate(isolate);

const Window = @import("../webapi/Window.zig");
if (T == Window) {
return context.page.window;
}

// ... Or the window's prototype.
// We could make this all comptime-fancy, but it's easier to hard-code
// the EventTarget

const EventTarget = @import("../webapi/EventTarget.zig");
if (T == EventTarget) {
return context.page.window._proto;
}

// Type not found in Window's prototype chain
return error.InvalidArgument;
}

// if it isn't an empty struct, then the v8.Object should have an
// InternalFieldCount > 0, since our toa pointer should be embedded
// at index 0 of the internal field count.
Expand Down
Loading
Loading