Merge pull request #593 from lightpanda-io/crypto_web_api

add crypto web api
This commit is contained in:
Pierre Tachoire
2025-05-05 08:56:27 +02:00
committed by GitHub
4 changed files with 96 additions and 2 deletions

View File

@@ -0,0 +1,82 @@
// Copyright (C) 2023-2024 Lightpanda (Selecy SAS)
//
// Francis Bouvier <francis@lightpanda.io>
// Pierre Tachoire <pierre@lightpanda.io>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std");
const uuidv4 = @import("../../id.zig").uuidv4;
// https://w3c.github.io/webcrypto/#crypto-interface
pub const Crypto = struct {
pub fn _getRandomValues(_: *const Crypto, into: RandomValues) !void {
const buf = into.asBuffer();
if (buf.len > 65_536) {
return error.QuotaExceededError;
}
std.crypto.random.bytes(buf);
}
pub fn _randomUUID(_: *const Crypto) [36]u8 {
var hex: [36]u8 = undefined;
uuidv4(&hex);
return hex;
}
};
const RandomValues = union(enum) {
int8: []i8,
uint8: []u8,
int16: []i16,
uint16: []u16,
int32: []i32,
uint32: []u32,
int64: []i64,
uint64: []u64,
fn asBuffer(self: RandomValues) []u8 {
switch (self) {
.int8 => |b| return (@as([]u8, @ptrCast(b)))[0..b.len],
.uint8 => |b| return (@as([]u8, @ptrCast(b)))[0..b.len],
.int16 => |b| return (@as([]u8, @ptrCast(b)))[0 .. b.len * 2],
.uint16 => |b| return (@as([]u8, @ptrCast(b)))[0 .. b.len * 2],
.int32 => |b| return (@as([]u8, @ptrCast(b)))[0 .. b.len * 4],
.uint32 => |b| return (@as([]u8, @ptrCast(b)))[0 .. b.len * 4],
.int64 => |b| return (@as([]u8, @ptrCast(b)))[0 .. b.len * 8],
.uint64 => |b| return (@as([]u8, @ptrCast(b)))[0 .. b.len * 8],
}
}
};
const testing = @import("../../testing.zig");
test "Browser.Crypto" {
var runner = try testing.jsRunner(testing.tracking_allocator, .{});
defer runner.deinit();
try runner.testCases(&.{
.{ "const a = crypto.randomUUID();", "undefined" },
.{ "const b = crypto.randomUUID();", "undefined" },
.{ "a.length;", "36" },
.{ "a.length;", "36" },
.{ "a == b;", "false" },
}, .{});
try runner.testCases(&.{
.{ "try { crypto.getRandomValues(new BigUint64Array(8193)) } catch(e) { e.message == 'QuotaExceededError' }", "true" },
.{ "let r1 = new Int32Array(5)", "undefined" },
.{ "crypto.getRandomValues(r1)", "undefined" },
.{ "new Set(r1).size", "5" },
}, .{});
}

View File

@@ -10,6 +10,7 @@ const HttpClient = @import("../http/client.zig").Client;
const Renderer = @import("browser.zig").Renderer;
const Interfaces = generate.Tuple(.{
@import("crypto/crypto.zig").Crypto,
@import("console/console.zig").Console,
@import("dom/dom.zig").Interfaces,
@import("events/event.zig").Interfaces,

View File

@@ -25,6 +25,7 @@ const SessionState = @import("../env.zig").SessionState;
const Navigator = @import("navigator.zig").Navigator;
const History = @import("history.zig").History;
const Location = @import("location.zig").Location;
const Crypto = @import("../crypto/crypto.zig").Crypto;
const Console = @import("../console/console.zig").Console;
const EventTarget = @import("../dom/event_target.zig").EventTarget;
@@ -49,6 +50,7 @@ pub const Window = struct {
timeoutid: u32 = 0,
timeoutids: [512]u64 = undefined,
crypto: Crypto = .{},
console: Console = .{},
navigator: Navigator = .{},
@@ -91,6 +93,10 @@ pub const Window = struct {
return &self.console;
}
pub fn get_crypto(self: *Window) *Crypto {
return &self.crypto;
}
pub fn get_self(self: *Window) *Window {
return self;
}

View File

@@ -2532,9 +2532,14 @@ fn Caller(comptime E: type) type {
// We settle for just probing the first value. Ok, actually
// not tricky in this case either.
const context = self.contxt;
const context = self.context;
const js_obj = js_arr.castTo(v8.Object);
return self.probeJsValueToZig(named_function, ptr.child, try js_obj.getAtIndex(context, 0));
switch (try self.probeJsValueToZig(named_function, ptr.child, try js_obj.getAtIndex(context, 0))) {
.value, .ok => return .{ .ok = {} },
.compatible => return .{ .compatible = {} },
.coerce => return .{ .coerce = {} },
.invalid => return .{ .invalid = {} },
}
},
else => {},
},