mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-09-17 08:27:11 -04:00
add basic NavigateEvent
This commit is contained in:
1 file changed
+197
@@ -0,0 +1,197 @@
|
||||
// Copyright (C) 2023-2026 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 lp = @import("lightpanda");
|
||||
|
||||
const js = @import("../../js/js.zig");
|
||||
const Frame = @import("../../Frame.zig");
|
||||
|
||||
const Event = @import("../Event.zig");
|
||||
const NavigationHistoryEntry = @import("../navigation/NavigationHistoryEntry.zig");
|
||||
const NavigationType = @import("../navigation/root.zig").NavigationType;
|
||||
const AbortSignal = @import("../AbortSignal.zig");
|
||||
|
||||
const String = lp.String;
|
||||
|
||||
const NavigateEvent = @This();
|
||||
|
||||
pub const Proto = Event;
|
||||
|
||||
_proto: *Event,
|
||||
_navigation_type: NavigationType,
|
||||
_can_intercept: bool,
|
||||
_user_initiated: bool,
|
||||
_hash_change: bool,
|
||||
_signal: *AbortSignal,
|
||||
_download_request: ?[]const u8,
|
||||
_info: ?js.Value.Global,
|
||||
_has_ua_visual_transition: bool,
|
||||
|
||||
_intercepted: bool = false,
|
||||
|
||||
const NavigateEventOptions = struct {
|
||||
navigationType: ?[]const u8 = null,
|
||||
canIntercept: bool = false,
|
||||
userInitiated: bool = false,
|
||||
hashChange: bool = false,
|
||||
signal: *AbortSignal,
|
||||
downloadRequest: ?[]const u8 = null,
|
||||
info: ?js.Value = null,
|
||||
hasUAVisualTransition: bool = false,
|
||||
};
|
||||
|
||||
const Options = Event.inheritOptions(NavigateEvent, NavigateEventOptions);
|
||||
|
||||
const InterceptOptions = struct {
|
||||
handler: ?js.Function = null,
|
||||
focusReset: ?[]const u8 = null,
|
||||
scroll: ?[]const u8 = null,
|
||||
};
|
||||
|
||||
pub fn init(typ: []const u8, opts: Options, frame: *Frame) !*NavigateEvent {
|
||||
const arena = try frame.getArena(.tiny, "NavigateEvent");
|
||||
errdefer arena.release();
|
||||
const type_string = try String.init(arena.allocator(), typ, .{});
|
||||
return initWithTrusted(arena, type_string, opts, false, frame);
|
||||
}
|
||||
|
||||
pub fn initTrusted(typ: String, opts: Options, frame: *Frame) !*NavigateEvent {
|
||||
const arena = try frame.getArena(.tiny, "NavigateEvent.trusted");
|
||||
errdefer arena.release();
|
||||
return initWithTrusted(arena, typ, opts, true, frame);
|
||||
}
|
||||
|
||||
fn initWithTrusted(
|
||||
arena: *lp.Arena,
|
||||
typ: String,
|
||||
opts: Options,
|
||||
trusted: bool,
|
||||
frame: *Frame,
|
||||
) !*NavigateEvent {
|
||||
// Per NavigateEventInit, navigationType defaults to "push" when omitted.
|
||||
const navigation_type = if (opts.navigationType) |nav_type_str|
|
||||
std.meta.stringToEnum(NavigationType, nav_type_str) orelse .push
|
||||
else
|
||||
.push;
|
||||
|
||||
const info: ?js.Value.Global = if (opts.info) |v| try v.persist() else null;
|
||||
|
||||
const event = try frame._factory.event(
|
||||
arena,
|
||||
typ,
|
||||
NavigateEvent{
|
||||
._proto = undefined,
|
||||
._navigation_type = navigation_type,
|
||||
._destination = opts.destination,
|
||||
._can_intercept = opts.canIntercept,
|
||||
._user_initiated = opts.userInitiated,
|
||||
._hash_change = opts.hashChange,
|
||||
._signal = opts.signal,
|
||||
._form_data = opts.formData,
|
||||
._download_request = opts.downloadRequest,
|
||||
._info = info,
|
||||
._has_ua_visual_transition = opts.hasUAVisualTransition,
|
||||
},
|
||||
);
|
||||
Event.populatePrototypes(event, opts, trusted);
|
||||
return event;
|
||||
}
|
||||
|
||||
pub fn asEvent(self: *NavigateEvent) *Event {
|
||||
return self._proto;
|
||||
}
|
||||
|
||||
pub fn getNavigationType(self: *const NavigateEvent) []const u8 {
|
||||
return @tagName(self._navigation_type);
|
||||
}
|
||||
|
||||
pub fn getCanIntercept(self: *const NavigateEvent) bool {
|
||||
return self._can_intercept;
|
||||
}
|
||||
|
||||
pub fn getUserInitiated(self: *const NavigateEvent) bool {
|
||||
return self._user_initiated;
|
||||
}
|
||||
|
||||
pub fn getHashChange(self: *const NavigateEvent) bool {
|
||||
return self._hash_change;
|
||||
}
|
||||
|
||||
pub fn getSignal(self: *NavigateEvent) *AbortSignal {
|
||||
return self._signal;
|
||||
}
|
||||
|
||||
pub fn getDownloadRequest(self: *const NavigateEvent) ?[]const u8 {
|
||||
return self._download_request;
|
||||
}
|
||||
|
||||
pub fn getInfo(self: *const NavigateEvent, exec: *const js.Execution) js.Value {
|
||||
if (self._info) |info| {
|
||||
return exec.js.local.?.toLocal(info);
|
||||
}
|
||||
return exec.js.local.?.undefined_();
|
||||
}
|
||||
|
||||
pub fn getHasUAVisualTransition(self: *const NavigateEvent) bool {
|
||||
return self._has_ua_visual_transition;
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/#dom-navigateevent-intercept
|
||||
// TODO: this only records interception state; it does not yet hook into
|
||||
// navigation commit timing to actually run `handler` or apply
|
||||
// `focusReset`/`scroll` at the correct point in the algorithm.
|
||||
pub fn intercept(self: *NavigateEvent, opts: ?InterceptOptions, exec: *const js.Execution) !void {
|
||||
if (!self._can_intercept) {
|
||||
return error.InvalidStateError;
|
||||
}
|
||||
if (!self._proto.getCancelable()) {
|
||||
return error.InvalidStateError;
|
||||
}
|
||||
self._intercepted = true;
|
||||
|
||||
if (opts) |o| {
|
||||
if (o.handler) |handler| {
|
||||
_ = handler; // TODO: queue and run during the navigate event's commit step.
|
||||
}
|
||||
_ = o.focusReset;
|
||||
_ = o.scroll;
|
||||
}
|
||||
_ = exec;
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
pub const bridge = js.Bridge(NavigateEvent);
|
||||
|
||||
pub const Meta = struct {
|
||||
pub const name = "NavigateEvent";
|
||||
pub const prototype_chain = bridge.prototypeChain();
|
||||
pub var class_id: bridge.ClassId = undefined;
|
||||
};
|
||||
|
||||
pub const constructor = bridge.constructor(NavigateEvent.init, .{});
|
||||
pub const navigationType = bridge.accessor(NavigateEvent.getNavigationType, null, .{});
|
||||
pub const canIntercept = bridge.accessor(NavigateEvent.getCanIntercept, null, .{});
|
||||
pub const userInitiated = bridge.accessor(NavigateEvent.getUserInitiated, null, .{});
|
||||
pub const hashChange = bridge.accessor(NavigateEvent.getHashChange, null, .{});
|
||||
pub const signal = bridge.accessor(NavigateEvent.getSignal, null, .{});
|
||||
pub const downloadRequest = bridge.accessor(NavigateEvent.getDownloadRequest, null, .{});
|
||||
pub const info = bridge.accessor(NavigateEvent.getInfo, null, .{});
|
||||
pub const hasUAVisualTransition = bridge.accessor(NavigateEvent.getHasUAVisualTransition, null, .{});
|
||||
pub const intercept = bridge.function(NavigateEvent.intercept, .{});
|
||||
};
|
||||
Reference in new issue
Block a user