diff --git a/.github/actions/install/action.yml b/.github/actions/install/action.yml index e9ccde0b..727eb4d4 100644 --- a/.github/actions/install/action.yml +++ b/.github/actions/install/action.yml @@ -13,7 +13,7 @@ inputs: zig-v8: description: 'zig v8 version to install' required: false - default: 'v0.4.1' + default: 'v0.4.2' v8: description: 'v8 version to install' required: false diff --git a/Dockerfile b/Dockerfile index 33501041..75c3e3fb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ FROM debian:stable-slim ARG MINISIG=0.12 ARG ZIG_MINISIG=RWSGOq2NVecA2UPNdBUZykf1CCb147pkmdtYxgb3Ti+JO/wCYvhbAb/U ARG V8=14.0.365.4 -ARG ZIG_V8=v0.4.1 +ARG ZIG_V8=v0.4.2 ARG TARGETPLATFORM RUN apt-get update -yq && \ diff --git a/build.zig.zon b/build.zig.zon index 16bd2378..ce289daa 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -5,8 +5,8 @@ .minimum_zig_version = "0.15.2", .dependencies = .{ .v8 = .{ - .url = "https://github.com/lightpanda-io/zig-v8-fork/archive/refs/tags/v0.4.1.tar.gz", - .hash = "v8-0.0.0-xddH672HBAA1hQIa2Uv4mzs_qHC9-Py-M5ssqSSVhWtK", + .url = "https://github.com/lightpanda-io/zig-v8-fork/archive/refs/tags/v0.4.2.tar.gz", + .hash = "v8-0.0.0-xddH672HBABNrbtyNk9o4QXxQJTlpjiCscmdEQuMvKnR", }, // .v8 = .{ .path = "../zig-v8-fork" }, .brotli = .{ diff --git a/src/browser/Frame.zig b/src/browser/Frame.zig index 8b0c462b..8aedf41e 100644 --- a/src/browser/Frame.zig +++ b/src/browser/Frame.zig @@ -3753,7 +3753,16 @@ pub fn submitForm(self: *Frame, submitter_: ?*Element, form_: ?*Element.Html.For const arena = try self._session.getArena(.medium, "submitForm"); errdefer self._session.releaseArena(arena); - const enctype = form_element.getAttributeSafe(comptime .wrap("enctype")); + // Per HTML spec form-submission algorithm, when the submitter is a submit + // button, its formaction/formmethod/formenctype attributes override the + // form's corresponding attributes (matching how formtarget is honored above). + // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#concept-form-submit + const enctype = blk: { + if (submitter_) |s| { + if (s.getAttributeSafe(comptime .wrap("formenctype"))) |fe| break :blk fe; + } + break :blk form_element.getAttributeSafe(comptime .wrap("enctype")); + }; // Get charset from accept-charset attribute or fall back to document charset const charset: []const u8 = blk: { @@ -3770,8 +3779,18 @@ pub fn submitForm(self: *Frame, submitter_: ?*Element, form_: ?*Element.Html.For var buf = std.Io.Writer.Allocating.init(arena); try form_data.write(.{ .enctype = enctype, .charset = charset, .allocator = arena }, &buf.writer); - const method = form_element.getAttributeSafe(comptime .wrap("method")) orelse ""; - var action = form_element.getAttributeSafe(comptime .wrap("action")) orelse self.url; + const method = blk: { + if (submitter_) |s| { + if (s.getAttributeSafe(comptime .wrap("formmethod"))) |fm| break :blk fm; + } + break :blk form_element.getAttributeSafe(comptime .wrap("method")) orelse ""; + }; + var action = blk: { + if (submitter_) |s| { + if (s.getAttributeSafe(comptime .wrap("formaction"))) |fa| break :blk fa; + } + break :blk form_element.getAttributeSafe(comptime .wrap("action")) orelse self.url; + }; var opts = NavigateOpts{ .reason = .form, diff --git a/src/browser/js/Snapshot.zig b/src/browser/js/Snapshot.zig index 533d479c..93bf9f6b 100644 --- a/src/browser/js/Snapshot.zig +++ b/src/browser/js/Snapshot.zig @@ -569,7 +569,12 @@ pub fn generateConstructor(comptime JsApi: type, isolate: *v8.Isolate) *const v8 break :blk illegalConstructorCallback; }; - const template = v8.v8__FunctionTemplate__New__DEFAULT2(isolate, callback).?; + const arity: c_int = if (@hasDecl(JsApi, "constructor")) JsApi.constructor.arity else 0; + const template = v8.v8__FunctionTemplate__New__Config(isolate, &.{ + .length = arity, + .callback = callback, + .behavior = v8.kConstructorBehavior_Allow, + }).?; { const internal_field_count = comptime countInternalFields(JsApi); if (internal_field_count > 0) { diff --git a/src/browser/js/bridge.zig b/src/browser/js/bridge.zig index 73ddccab..ebe96a1f 100644 --- a/src/browser/js/bridge.zig +++ b/src/browser/js/bridge.zig @@ -107,6 +107,7 @@ pub fn Builder(comptime T: type) type { } pub const Constructor = struct { + arity: c_int, func: *const fn (?*const v8.FunctionCallbackInfo) callconv(.c) void, const Opts = struct { @@ -118,21 +119,24 @@ pub const Constructor = struct { }; fn init(comptime T: type, comptime func: anytype, comptime opts: Opts) Constructor { - return .{ .func = struct { - fn wrap(handle: ?*const v8.FunctionCallbackInfo) callconv(.c) void { - const v8_isolate = v8.v8__FunctionCallbackInfo__GetIsolate(handle).?; - var caller: Caller = undefined; - if (!caller.init(v8_isolate)) { - return; - } - defer caller.deinit(); + return .{ + .arity = comptime Function.getArity(@TypeOf(func), if (opts.new_target) 1 else 0), + .func = struct { + fn wrap(handle: ?*const v8.FunctionCallbackInfo) callconv(.c) void { + const v8_isolate = v8.v8__FunctionCallbackInfo__GetIsolate(handle).?; + var caller: Caller = undefined; + if (!caller.init(v8_isolate)) { + return; + } + defer caller.deinit(); - caller.constructor(T, func, handle.?, .{ - .dom_exception = opts.dom_exception, - .new_target = opts.new_target, - }); - } - }.wrap }; + caller.constructor(T, func, handle.?, .{ + .dom_exception = opts.dom_exception, + .new_target = opts.new_target, + }); + } + }.wrap, + }; } }; @@ -149,7 +153,7 @@ pub const Function = struct { .cache = opts.cache, .static = opts.static, .wpt_only = opts.wpt_only, - .arity = getArity(@TypeOf(func)), + .arity = getArity(@TypeOf(func), 1), .func = if (opts.noop) noopFunction else struct { fn wrap(handle: ?*const v8.FunctionCallbackInfo) callconv(.c) void { Caller.Function.call(T, handle.?, func, opts); @@ -160,14 +164,32 @@ pub const Function = struct { pub fn noopFunction(_: ?*const v8.FunctionCallbackInfo) callconv(.c) void {} - fn getArity(comptime T: type) usize { + fn getArity(comptime T: type, comptime start: usize) usize { + const Execution = js.Execution; + + const Page = @import("../Page.zig"); + const Session = @import("../Session.zig"); + var count: usize = 0; var params = @typeInfo(T).@"fn".params; - for (params[1..]) |p| { // start at 1, skip self + for (params[start..]) |p| { // start at 1, skip self const PT = p.type.?; if (PT == *Frame or PT == *const Frame) { break; } + + if (PT == *Page or PT == *const Page) { + break; + } + + if (PT == *Execution or PT == *const Execution) { + break; + } + + if (PT == *Session or PT == *const Session) { + break; + } + if (@typeInfo(PT) == .optional) { break; } diff --git a/src/browser/tests/element/html/script/script.html b/src/browser/tests/element/html/script/script.html index d49d025a..0b7fc0f8 100644 --- a/src/browser/tests/element/html/script/script.html +++ b/src/browser/tests/element/html/script/script.html @@ -35,5 +35,11 @@ const append = Object.getOwnPropertyDescriptor(Element.prototype, 'append'); testing.expectEqual('append', append.value.name); + + // Constructor.length should equal the number of required arguments (1 for + // events: the type). Sentinel for the New__Config wiring in Snapshot.zig. + testing.expectEqual(1, MouseEvent.length); + testing.expectEqual(1, KeyboardEvent.length); + testing.expectEqual(1, Event.length); } diff --git a/src/browser/tests/frames/target.html b/src/browser/tests/frames/target.html index dbf23242..924e0e59 100644 --- a/src/browser/tests/frames/target.html +++ b/src/browser/tests/frames/target.html @@ -56,3 +56,73 @@ }); } + + + +
+ +
+ + + + + +
+ + +
+ + + + + +
+ + +
+ +