mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-03-22 04:34:44 +00:00
fix: stub navigator.permissions, storage, deviceMemory to unblock Turnstile
This commit is contained in:
@@ -725,6 +725,9 @@ pub const JsApis = flattenTypes(&.{
|
||||
@import("../webapi/collections.zig"),
|
||||
@import("../webapi/Console.zig"),
|
||||
@import("../webapi/Crypto.zig"),
|
||||
@import("../webapi/Permissions.zig"),
|
||||
@import("../webapi/Permissions.zig").PermissionStatus,
|
||||
@import("../webapi/StorageManager.zig"),
|
||||
@import("../webapi/CSS.zig"),
|
||||
@import("../webapi/css/CSSRule.zig"),
|
||||
@import("../webapi/css/CSSRuleList.zig"),
|
||||
|
||||
37
src/browser/tests/navigator/permissions.html
Normal file
37
src/browser/tests/navigator/permissions.html
Normal file
@@ -0,0 +1,37 @@
|
||||
<!DOCTYPE html>
|
||||
<html><body>
|
||||
<script id=permissions_query_returns_promise>
|
||||
const p = navigator.permissions.query({ name: 'notifications' });
|
||||
console.assert(p instanceof Promise, 'permissions.query must return a Promise');
|
||||
p.then(status => {
|
||||
console.assert(typeof status.state === 'string', 'state must be a string');
|
||||
console.assert(
|
||||
['granted','denied','prompt'].includes(status.state),
|
||||
'state must be valid'
|
||||
);
|
||||
});
|
||||
</script>
|
||||
|
||||
<script id=storage_estimate_returns_promise>
|
||||
const p = navigator.storage.estimate();
|
||||
console.assert(p instanceof Promise, 'storage.estimate must return a Promise');
|
||||
p.then(e => {
|
||||
console.assert(typeof e.quota === 'number', 'quota must be a number');
|
||||
console.assert(typeof e.usage === 'number', 'usage must be a number');
|
||||
});
|
||||
</script>
|
||||
|
||||
<script id=device_memory_is_number>
|
||||
console.assert(
|
||||
typeof navigator.deviceMemory === 'number',
|
||||
'deviceMemory must be a number, got: ' + typeof navigator.deviceMemory
|
||||
);
|
||||
console.assert(navigator.deviceMemory > 0, 'deviceMemory must be positive');
|
||||
</script>
|
||||
|
||||
<script id=get_battery_returns_rejected_promise>
|
||||
const p = navigator.getBattery();
|
||||
console.assert(p instanceof Promise, 'getBattery must return a Promise');
|
||||
p.catch(e => console.log('getBattery correctly rejected:', e));
|
||||
</script>
|
||||
</body></html>
|
||||
@@ -21,10 +21,14 @@ const builtin = @import("builtin");
|
||||
const js = @import("../js/js.zig");
|
||||
const Page = @import("../Page.zig");
|
||||
const PluginArray = @import("PluginArray.zig");
|
||||
const Permissions = @import("Permissions.zig");
|
||||
const StorageManager = @import("StorageManager.zig");
|
||||
|
||||
const Navigator = @This();
|
||||
_pad: bool = false,
|
||||
_plugins: PluginArray = .{},
|
||||
_permissions: Permissions = Permissions.init,
|
||||
_storage: StorageManager = StorageManager.init,
|
||||
|
||||
pub const init: Navigator = .{};
|
||||
|
||||
@@ -55,6 +59,20 @@ pub fn getPlugins(self: *Navigator) *PluginArray {
|
||||
return &self._plugins;
|
||||
}
|
||||
|
||||
pub fn getPermissions(self: *Navigator) *Permissions {
|
||||
return &self._permissions;
|
||||
}
|
||||
|
||||
pub fn getStorage(self: *Navigator) *StorageManager {
|
||||
return &self._storage;
|
||||
}
|
||||
|
||||
pub fn getBattery(_: *const Navigator, page: *Page) !js.Promise {
|
||||
// Battery API is not supported in headless mode.
|
||||
// Return a rejected Promise — callers must .catch() this.
|
||||
return js.Promise.reject(error.NotSupportedError, page);
|
||||
}
|
||||
|
||||
pub fn registerProtocolHandler(_: *const Navigator, scheme: []const u8, url: [:0]const u8, page: *const Page) !void {
|
||||
try validateProtocolHandlerScheme(scheme);
|
||||
try validateProtocolHandlerURL(url, page);
|
||||
@@ -144,6 +162,7 @@ pub const JsApi = struct {
|
||||
pub const onLine = bridge.property(true, .{ .template = false });
|
||||
pub const cookieEnabled = bridge.property(true, .{ .template = false });
|
||||
pub const hardwareConcurrency = bridge.property(4, .{ .template = false });
|
||||
pub const deviceMemory = bridge.property(@as(f64, 8.0), .{ .template = false });
|
||||
pub const maxTouchPoints = bridge.property(0, .{ .template = false });
|
||||
pub const vendor = bridge.property("", .{ .template = false });
|
||||
pub const product = bridge.property("Gecko", .{ .template = false });
|
||||
@@ -156,4 +175,7 @@ pub const JsApi = struct {
|
||||
|
||||
// Methods
|
||||
pub const javaEnabled = bridge.function(Navigator.javaEnabled, .{});
|
||||
pub const getBattery = bridge.function(Navigator.getBattery, .{});
|
||||
pub const permissions = bridge.accessor(Navigator.getPermissions, null, .{});
|
||||
pub const storage = bridge.accessor(Navigator.getStorage, null, .{});
|
||||
};
|
||||
|
||||
61
src/browser/webapi/Permissions.zig
Normal file
61
src/browser/webapi/Permissions.zig
Normal file
@@ -0,0 +1,61 @@
|
||||
// src/browser/webapi/Permissions.zig
|
||||
//
|
||||
// Minimal Permissions API stub.
|
||||
// https://www.w3.org/TR/permissions/
|
||||
//
|
||||
// Turnstile probes: navigator.permissions.query({ name: 'notifications' })
|
||||
// It expects a Promise resolving to { state: 'granted' | 'denied' | 'prompt' }
|
||||
|
||||
const std = @import("std");
|
||||
const js = @import("../js/js.zig");
|
||||
const Page = @import("../Page.zig");
|
||||
|
||||
const Permissions = @This();
|
||||
|
||||
// Padding to avoid zero-size struct pointer collisions
|
||||
_pad: bool = false,
|
||||
|
||||
pub const init: Permissions = .{};
|
||||
|
||||
const QueryDescriptor = struct {
|
||||
name: []const u8,
|
||||
};
|
||||
|
||||
const PermissionStatus = struct {
|
||||
state: []const u8,
|
||||
|
||||
pub const JsApi = struct {
|
||||
pub const bridge = js.Bridge(PermissionStatus);
|
||||
pub const Meta = struct {
|
||||
pub const name = "PermissionStatus";
|
||||
pub const prototype_chain = bridge.prototypeChain();
|
||||
pub var class_id: bridge.ClassId = undefined;
|
||||
pub const empty_with_no_proto = true;
|
||||
};
|
||||
pub const state = bridge.accessor(getState, null, .{});
|
||||
};
|
||||
|
||||
fn getState(self: *const PermissionStatus) []const u8 {
|
||||
return self.state;
|
||||
}
|
||||
};
|
||||
|
||||
// query() returns a Promise<PermissionStatus>.
|
||||
// We always report 'prompt' (the default safe value — neither granted nor denied).
|
||||
pub fn query(_: *const Permissions, _: QueryDescriptor, page: *Page) !js.Promise {
|
||||
const status = try page._factory.create(PermissionStatus{ .state = "prompt" });
|
||||
return js.Promise.resolve(status, page);
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
pub const bridge = js.Bridge(Permissions);
|
||||
|
||||
pub const Meta = struct {
|
||||
pub const name = "Permissions";
|
||||
pub const prototype_chain = bridge.prototypeChain();
|
||||
pub var class_id: bridge.ClassId = undefined;
|
||||
pub const empty_with_no_proto = true;
|
||||
};
|
||||
|
||||
pub const query = bridge.function(Permissions.query, .{ .dom_exception = true });
|
||||
};
|
||||
56
src/browser/webapi/StorageManager.zig
Normal file
56
src/browser/webapi/StorageManager.zig
Normal file
@@ -0,0 +1,56 @@
|
||||
// src/browser/webapi/StorageManager.zig
|
||||
// Minimal stub for navigator.storage
|
||||
// https://storage.spec.whatwg.org/#storagemanager
|
||||
|
||||
const js = @import("../js/js.zig");
|
||||
const Page = @import("../Page.zig");
|
||||
|
||||
const StorageManager = @This();
|
||||
_pad: bool = false,
|
||||
|
||||
pub const init: StorageManager = .{};
|
||||
|
||||
const StorageEstimate = struct {
|
||||
quota: u64,
|
||||
usage: u64,
|
||||
|
||||
pub const JsApi = struct {
|
||||
pub const bridge = js.Bridge(StorageEstimate);
|
||||
pub const Meta = struct {
|
||||
pub const name = "StorageEstimate";
|
||||
pub const prototype_chain = bridge.prototypeChain();
|
||||
pub var class_id: bridge.ClassId = undefined;
|
||||
pub const empty_with_no_proto = true;
|
||||
};
|
||||
pub const quota = bridge.accessor(getQuota, null, .{});
|
||||
pub const usage = bridge.accessor(getUsage, null, .{});
|
||||
};
|
||||
|
||||
fn getQuota(self: *const StorageEstimate) u64 {
|
||||
return self.quota;
|
||||
}
|
||||
fn getUsage(self: *const StorageEstimate) u64 {
|
||||
return self.usage;
|
||||
}
|
||||
};
|
||||
|
||||
// Returns a resolved Promise<StorageEstimate> with plausible stub values.
|
||||
// quota = 1GB, usage = 0 (headless browser has no real storage)
|
||||
pub fn estimate(_: *const StorageManager, page: *Page) !js.Promise {
|
||||
const est = try page._factory.create(StorageEstimate{
|
||||
.quota = 1024 * 1024 * 1024, // 1 GiB
|
||||
.usage = 0,
|
||||
});
|
||||
return js.Promise.resolve(est, page);
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
pub const bridge = js.Bridge(StorageManager);
|
||||
pub const Meta = struct {
|
||||
pub const name = "StorageManager";
|
||||
pub const prototype_chain = bridge.prototypeChain();
|
||||
pub var class_id: bridge.ClassId = undefined;
|
||||
pub const empty_with_no_proto = true;
|
||||
};
|
||||
pub const estimate = bridge.function(StorageManager.estimate, .{});
|
||||
};
|
||||
Reference in New Issue
Block a user