mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-02-04 06:23:45 +00:00
event isTrusted support and better composedPath for shadowroots
This commit is contained in:
@@ -83,12 +83,14 @@ pub fn register(self: *EventManager, target: *EventTarget, typ: []const u8, call
|
||||
var node = gop.value_ptr.*.first;
|
||||
while (node) |n| {
|
||||
const listener: *Listener = @alignCast(@fieldParentPtr("node", n));
|
||||
const is_duplicate = switch (callback) {
|
||||
.object => |obj| listener.function.eqlObject(obj),
|
||||
.function => |func| listener.function.eqlFunction(func),
|
||||
};
|
||||
if (is_duplicate and listener.capture == opts.capture) {
|
||||
return;
|
||||
if (listener.typ.eqlSlice(typ)) {
|
||||
const is_duplicate = switch (callback) {
|
||||
.object => |obj| listener.function.eqlObject(obj),
|
||||
.function => |func| listener.function.eqlFunction(func),
|
||||
};
|
||||
if (is_duplicate and listener.capture == opts.capture) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
node = n.next;
|
||||
}
|
||||
@@ -132,6 +134,7 @@ pub fn dispatch(self: *EventManager, target: *EventTarget, event: *Event) !void
|
||||
}
|
||||
|
||||
event._target = target;
|
||||
event._dispatch_target = target; // Store original target for composedPath()
|
||||
var was_handled = false;
|
||||
|
||||
defer if (was_handled) {
|
||||
@@ -173,6 +176,7 @@ pub fn dispatchWithFunction(self: *EventManager, target: *EventTarget, event: *E
|
||||
|
||||
if (comptime opts.inject_target) {
|
||||
event._target = target;
|
||||
event._dispatch_target = target; // Store original target for composedPath()
|
||||
}
|
||||
|
||||
var was_dispatched = false;
|
||||
|
||||
@@ -503,7 +503,7 @@ pub fn documentIsLoaded(self: *Page) void {
|
||||
}
|
||||
|
||||
pub fn _documentIsLoaded(self: *Page) !void {
|
||||
const event = try Event.init("DOMContentLoaded", .{ .bubbles = true }, self);
|
||||
const event = try Event.initTrusted("DOMContentLoaded", .{ .bubbles = true }, self);
|
||||
try self._event_manager.dispatch(
|
||||
self.document.asEventTarget(),
|
||||
event,
|
||||
@@ -549,7 +549,7 @@ fn _documentIsComplete(self: *Page) !void {
|
||||
self.document._ready_state = .complete;
|
||||
|
||||
// dispatch window.load event
|
||||
const event = try Event.init("load", .{}, self);
|
||||
const event = try Event.initTrusted("load", .{}, self);
|
||||
// this event is weird, it's dispatched directly on the window, but
|
||||
// with the document as the target
|
||||
event._target = self.document.asEventTarget();
|
||||
@@ -560,7 +560,7 @@ fn _documentIsComplete(self: *Page) !void {
|
||||
.{ .inject_target = false, .context = "page load" },
|
||||
);
|
||||
|
||||
const pageshow_event = try PageTransitionEvent.init("pageshow", .{}, self);
|
||||
const pageshow_event = try PageTransitionEvent.initTrusted("pageshow", .{}, self);
|
||||
try self._event_manager.dispatchWithFunction(
|
||||
self.window.asEventTarget(),
|
||||
pageshow_event.asEvent(),
|
||||
@@ -1174,7 +1174,7 @@ pub fn deliverSlotchangeEvents(self: *Page) void {
|
||||
self._slots_pending_slotchange.clearRetainingCapacity();
|
||||
|
||||
for (slots) |slot| {
|
||||
const event = Event.init("slotchange", .{ .bubbles = true }, self) catch |err| {
|
||||
const event = Event.initTrusted("slotchange", .{ .bubbles = true }, self) catch |err| {
|
||||
log.err(.page, "deliverSlotchange.init", .{ .err = err });
|
||||
continue;
|
||||
};
|
||||
@@ -2419,6 +2419,7 @@ pub fn triggerMouseClick(self: *Page, x: f64, y: f64) !void {
|
||||
const event = try @import("webapi/event/MouseEvent.zig").init("click", .{
|
||||
.bubbles = true,
|
||||
.cancelable = true,
|
||||
.composed = true,
|
||||
.clientX = x,
|
||||
.clientY = y,
|
||||
}, self);
|
||||
|
||||
@@ -833,7 +833,7 @@ pub const Script = struct {
|
||||
const cb = cb_ orelse return;
|
||||
|
||||
const Event = @import("webapi/Event.zig");
|
||||
const event = Event.init(typ, .{}, page) catch |err| {
|
||||
const event = Event.initTrusted(typ, .{}, page) catch |err| {
|
||||
log.warn(.js, "script internal callback", .{
|
||||
.url = self.url,
|
||||
.type = typ,
|
||||
|
||||
@@ -88,3 +88,18 @@
|
||||
|
||||
testing.expectEqual(true, isKeyPress);
|
||||
</script>
|
||||
|
||||
<script id=isTrusted>
|
||||
// Test isTrusted on KeyboardEvent
|
||||
let keyEvent = new KeyboardEvent('keydown', {key: 'a'});
|
||||
testing.expectEqual(false, keyEvent.isTrusted);
|
||||
|
||||
// Test isTrusted on dispatched KeyboardEvent
|
||||
let keyIsTrusted = null;
|
||||
document.addEventListener('keytest', (e) => {
|
||||
keyIsTrusted = e.isTrusted;
|
||||
testing.expectEqual(true, e instanceof KeyboardEvent);
|
||||
});
|
||||
document.dispatchEvent(new KeyboardEvent('keytest', {key: 'b'}));
|
||||
testing.expectEqual(false, keyIsTrusted);
|
||||
</script>
|
||||
|
||||
@@ -35,3 +35,18 @@
|
||||
testing.expectEqual('click', evt.type);
|
||||
testing.expectEqual(true, evt instanceof MouseEvent);
|
||||
</script>
|
||||
|
||||
<script id=isTrusted>
|
||||
// Test isTrusted on MouseEvent
|
||||
let mouseEvent = new MouseEvent('click');
|
||||
testing.expectEqual(false, mouseEvent.isTrusted);
|
||||
|
||||
// Test isTrusted on dispatched MouseEvent
|
||||
let mouseIsTrusted = null;
|
||||
document.addEventListener('mousetest', (e) => {
|
||||
mouseIsTrusted = e.isTrusted;
|
||||
testing.expectEqual(true, e instanceof MouseEvent);
|
||||
});
|
||||
document.dispatchEvent(new MouseEvent('mousetest'));
|
||||
testing.expectEqual(false, mouseIsTrusted);
|
||||
</script>
|
||||
|
||||
@@ -612,3 +612,21 @@
|
||||
content.dispatchEvent(new Event('he2'));
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=isTrusted>
|
||||
// Test isTrusted property on generic Event
|
||||
let untrustedEvent = new Event('test');
|
||||
testing.expectEqual(false, untrustedEvent.isTrusted);
|
||||
|
||||
// Test isTrusted on dispatched events
|
||||
let isTrustedValue = null;
|
||||
document.addEventListener('trusttest', (e) => {
|
||||
isTrustedValue = e.isTrusted;
|
||||
});
|
||||
document.dispatchEvent(new Event('trusttest'));
|
||||
testing.expectEqual(false, isTrustedValue);
|
||||
|
||||
// Test isTrusted with bubbling events
|
||||
let bubbledEvent = new Event('bubble', {bubbles: true});
|
||||
testing.expectEqual(false, bubbledEvent.isTrusted);
|
||||
</script>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
testing.expectEqual(true, evt.bubbles);
|
||||
testing.expectEqual(true, evt.cancelable);
|
||||
testing.expectEqual(true, evt.defaultPrevented);
|
||||
testing.expectEqual(true, evt.isTrusted);
|
||||
testing.expectEqual(false, evt.isTrusted);
|
||||
testing.expectEqual(true, evt.timeStamp >= Math.floor(startTime));
|
||||
</script>
|
||||
|
||||
|
||||
@@ -184,3 +184,100 @@
|
||||
host.remove();
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="composedPath_open_shadow_from_host">
|
||||
{
|
||||
// Test that open shadow root exposes internals in composedPath
|
||||
const host = document.createElement('div');
|
||||
const shadow = host.attachShadow({ mode: 'open' });
|
||||
const button = document.createElement('button');
|
||||
|
||||
shadow.appendChild(button);
|
||||
document.body.appendChild(host);
|
||||
|
||||
let capturedPath = null;
|
||||
|
||||
host.addEventListener('click', (e) => {
|
||||
capturedPath = e.composedPath();
|
||||
});
|
||||
|
||||
const event = new Event('click', { bubbles: true, composed: true });
|
||||
button.dispatchEvent(event);
|
||||
|
||||
// Open shadow: external listener should see shadow internals
|
||||
testing.expectEqual(7, capturedPath.length);
|
||||
testing.expectEqual(button, capturedPath[0]);
|
||||
testing.expectEqual(shadow, capturedPath[1]);
|
||||
testing.expectEqual(host, capturedPath[2]);
|
||||
testing.expectEqual(document.body, capturedPath[3]);
|
||||
testing.expectEqual(document.documentElement, capturedPath[4]);
|
||||
testing.expectEqual(document, capturedPath[5]);
|
||||
testing.expectEqual(window, capturedPath[6]);
|
||||
|
||||
host.remove();
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="composedPath_closed_shadow_from_host">
|
||||
{
|
||||
// Test that closed shadow root hides internals in composedPath
|
||||
const host = document.createElement('div');
|
||||
const shadow = host.attachShadow({ mode: 'closed' });
|
||||
const button = document.createElement('button');
|
||||
|
||||
shadow.appendChild(button);
|
||||
document.body.appendChild(host);
|
||||
|
||||
let capturedPath = null;
|
||||
|
||||
host.addEventListener('click', (e) => {
|
||||
capturedPath = e.composedPath();
|
||||
});
|
||||
|
||||
const event = new Event('click', { bubbles: true, composed: true });
|
||||
button.dispatchEvent(event);
|
||||
|
||||
// Closed shadow: external listener should NOT see shadow internals
|
||||
testing.expectEqual(5, capturedPath.length);
|
||||
testing.expectEqual(host, capturedPath[0]);
|
||||
testing.expectEqual(document.body, capturedPath[1]);
|
||||
testing.expectEqual(document.documentElement, capturedPath[2]);
|
||||
testing.expectEqual(document, capturedPath[3]);
|
||||
testing.expectEqual(window, capturedPath[4]);
|
||||
|
||||
host.remove();
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="composedPath_closed_shadow_from_inside">
|
||||
{
|
||||
// Test that closed shadow root still exposes internals to internal listeners
|
||||
const host = document.createElement('div');
|
||||
const shadow = host.attachShadow({ mode: 'closed' });
|
||||
const button = document.createElement('button');
|
||||
|
||||
shadow.appendChild(button);
|
||||
document.body.appendChild(host);
|
||||
|
||||
let capturedPath = null;
|
||||
|
||||
button.addEventListener('click', (e) => {
|
||||
capturedPath = e.composedPath();
|
||||
});
|
||||
|
||||
const event = new Event('click', { bubbles: true, composed: true });
|
||||
button.dispatchEvent(event);
|
||||
|
||||
// Inside the shadow: should see full path including shadow internals
|
||||
testing.expectEqual(7, capturedPath.length);
|
||||
testing.expectEqual(button, capturedPath[0]);
|
||||
testing.expectEqual(shadow, capturedPath[1]);
|
||||
testing.expectEqual(host, capturedPath[2]);
|
||||
testing.expectEqual(document.body, capturedPath[3]);
|
||||
testing.expectEqual(document.documentElement, capturedPath[4]);
|
||||
testing.expectEqual(document, capturedPath[5]);
|
||||
testing.expectEqual(window, capturedPath[6]);
|
||||
|
||||
host.remove();
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -80,7 +80,7 @@ pub fn abort(self: *AbortSignal, reason_: ?Reason, page: *Page) !void {
|
||||
}
|
||||
|
||||
// Dispatch abort event
|
||||
const event = try Event.init("abort", .{}, page);
|
||||
const event = try Event.initTrusted("abort", .{}, page);
|
||||
try page._event_manager.dispatchWithFunction(
|
||||
self.asEventTarget(),
|
||||
event,
|
||||
|
||||
@@ -251,7 +251,7 @@ pub fn createTextNode(_: *const Document, data: []const u8, page: *Page) !*Node
|
||||
|
||||
pub fn createCDATASection(self: *const Document, data: []const u8, page: *Page) !*Node {
|
||||
switch (self._type) {
|
||||
.html => return error.NotSupported, // cannot create a CDataSection in an HTMLDocument
|
||||
.html => return error.NotSupported, // cannot create a CDataSection in an HTMLDocument
|
||||
.xml => return page.createCDATASection(data),
|
||||
.generic => return page.createCDATASection(data),
|
||||
}
|
||||
@@ -570,7 +570,7 @@ pub fn getChildElementCount(self: *Document) u32 {
|
||||
return i;
|
||||
}
|
||||
|
||||
pub fn getAdoptedStyleSheets(self: *Document, page: *Page) !js.Object {
|
||||
pub fn getAdoptedStyleSheets(self: *Document, page: *Page) !js.Object {
|
||||
if (self._adopted_style_sheets) |ass| {
|
||||
return ass;
|
||||
}
|
||||
|
||||
@@ -596,7 +596,7 @@ pub fn focus(self: *Element, page: *Page) !void {
|
||||
return;
|
||||
}
|
||||
|
||||
const blur_event = try Event.init("blur", null, page);
|
||||
const blur_event = try Event.initTrusted("blur", null, page);
|
||||
try page._event_manager.dispatch(old.asEventTarget(), blur_event);
|
||||
}
|
||||
|
||||
@@ -604,7 +604,7 @@ pub fn focus(self: *Element, page: *Page) !void {
|
||||
page.document._active_element = self;
|
||||
}
|
||||
|
||||
const focus_event = try Event.init("focus", null, page);
|
||||
const focus_event = try Event.initTrusted("focus", null, page);
|
||||
try page._event_manager.dispatch(self.asEventTarget(), focus_event);
|
||||
}
|
||||
|
||||
@@ -614,7 +614,7 @@ pub fn blur(self: *Element, page: *Page) !void {
|
||||
page.document._active_element = null;
|
||||
|
||||
const Event = @import("Event.zig");
|
||||
const blur_event = try Event.init("blur", null, page);
|
||||
const blur_event = try Event.initTrusted("blur", null, page);
|
||||
try page._event_manager.dispatch(self.asEventTarget(), blur_event);
|
||||
}
|
||||
|
||||
|
||||
@@ -35,12 +35,14 @@ _composed: bool = false,
|
||||
_type_string: String,
|
||||
_target: ?*EventTarget = null,
|
||||
_current_target: ?*EventTarget = null,
|
||||
_dispatch_target: ?*EventTarget = null, // Original target for composedPath()
|
||||
_prevent_default: bool = false,
|
||||
_stop_propagation: bool = false,
|
||||
_stop_immediate_propagation: bool = false,
|
||||
_event_phase: EventPhase = .none,
|
||||
_time_stamp: u64 = 0,
|
||||
_needs_retargeting: bool = false,
|
||||
_isTrusted: bool = false,
|
||||
|
||||
pub const EventPhase = enum(u8) {
|
||||
none = 0,
|
||||
@@ -68,14 +70,22 @@ pub const Options = struct {
|
||||
composed: bool = false,
|
||||
};
|
||||
|
||||
pub fn initTrusted(typ: []const u8, opts_: ?Options, page: *Page) !*Event {
|
||||
return initWithTrusted(typ, opts_, true, page);
|
||||
}
|
||||
|
||||
pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*Event {
|
||||
return initWithTrusted(typ, opts_, false, page);
|
||||
}
|
||||
|
||||
fn initWithTrusted(typ: []const u8, opts_: ?Options, trusted: bool, page: *Page) !*Event {
|
||||
const opts = opts_ orelse Options{};
|
||||
|
||||
// Round to 2ms for privacy (browsers do this)
|
||||
const raw_timestamp = @import("../../datetime.zig").milliTimestamp(.monotonic);
|
||||
const time_stamp = (raw_timestamp / 2) * 2;
|
||||
|
||||
return page._factory.create(Event{
|
||||
const event = try page._factory.create(Event{
|
||||
._type = .generic,
|
||||
._bubbles = opts.bubbles,
|
||||
._time_stamp = time_stamp,
|
||||
@@ -83,6 +93,21 @@ pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*Event {
|
||||
._composed = opts.composed,
|
||||
._type_string = try String.init(page.arena, typ, .{}),
|
||||
});
|
||||
|
||||
event._isTrusted = trusted;
|
||||
return event;
|
||||
}
|
||||
|
||||
pub fn initEvent(
|
||||
self: *Event,
|
||||
event_string: []const u8,
|
||||
bubbles: ?bool,
|
||||
cancelable: ?bool,
|
||||
page: *Page,
|
||||
) !void {
|
||||
self._type_string = try String.init(page.arena, event_string, .{});
|
||||
self._bubbles = bubbles orelse false;
|
||||
self._cancelable = cancelable orelse false;
|
||||
}
|
||||
|
||||
pub fn as(self: *Event, comptime T: type) *T {
|
||||
@@ -159,14 +184,27 @@ pub fn getTimeStamp(self: *const Event) u64 {
|
||||
return self._time_stamp;
|
||||
}
|
||||
|
||||
pub fn setTrusted(self: *Event) void {
|
||||
self._isTrusted = true;
|
||||
}
|
||||
|
||||
pub fn setUntrusted(self: *Event) void {
|
||||
self._isTrusted = false;
|
||||
}
|
||||
|
||||
pub fn getIsTrusted(self: *const Event) bool {
|
||||
return self._isTrusted;
|
||||
}
|
||||
|
||||
pub fn composedPath(self: *Event, page: *Page) ![]const *EventTarget {
|
||||
// Return empty array if event is not being dispatched
|
||||
if (self._event_phase == .none) {
|
||||
return &.{};
|
||||
}
|
||||
|
||||
// If there's no target, return empty array
|
||||
const target = self._target orelse return &.{};
|
||||
// Use dispatch_target (original target) if available, otherwise fall back to target
|
||||
// This is important because _target gets retargeted during event dispatch
|
||||
const target = self._dispatch_target orelse self._target orelse return &.{};
|
||||
|
||||
// Only nodes have a propagation path
|
||||
const target_node = switch (target._type) {
|
||||
@@ -179,6 +217,9 @@ pub fn composedPath(self: *Event, page: *Page) ![]const *EventTarget {
|
||||
var path_buffer: [128]*EventTarget = undefined;
|
||||
var stopped_at_shadow_boundary = false;
|
||||
|
||||
// Track closed shadow boundaries (position in path and host position)
|
||||
var closed_shadow_boundary: ?struct { shadow_end: usize, host_start: usize } = null;
|
||||
|
||||
var node: ?*Node = target_node;
|
||||
while (node) |n| {
|
||||
if (path_len >= path_buffer.len) {
|
||||
@@ -198,7 +239,17 @@ pub fn composedPath(self: *Event, page: *Page) ![]const *EventTarget {
|
||||
break;
|
||||
}
|
||||
|
||||
// Otherwise, jump to the shadow host and continue
|
||||
// Track the first closed shadow boundary we encounter
|
||||
if (shadow._mode == .closed and closed_shadow_boundary == null) {
|
||||
// Mark where the shadow root is in the path
|
||||
// The next element will be the host
|
||||
closed_shadow_boundary = .{
|
||||
.shadow_end = path_len - 1, // index of shadow root
|
||||
.host_start = path_len, // index where host will be
|
||||
};
|
||||
}
|
||||
|
||||
// Jump to the shadow host and continue
|
||||
node = shadow._host.asNode();
|
||||
continue;
|
||||
}
|
||||
@@ -215,9 +266,40 @@ pub fn composedPath(self: *Event, page: *Page) ![]const *EventTarget {
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate and return the path using call_arena (short-lived)
|
||||
const path = try page.call_arena.alloc(*EventTarget, path_len);
|
||||
@memcpy(path, path_buffer[0..path_len]);
|
||||
// Determine visible path based on current_target and closed shadow boundaries
|
||||
var visible_start_index: usize = 0;
|
||||
|
||||
if (closed_shadow_boundary) |boundary| {
|
||||
// Check if current_target is outside the closed shadow
|
||||
// If current_target is null or is at/after the host position, hide shadow internals
|
||||
const current_target = self._current_target;
|
||||
|
||||
if (current_target) |ct| {
|
||||
// Find current_target in the path
|
||||
var ct_index: ?usize = null;
|
||||
for (path_buffer[0..path_len], 0..) |elem, i| {
|
||||
if (elem == ct) {
|
||||
ct_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If current_target is at or after the host (outside the closed shadow),
|
||||
// hide everything from target up to the host
|
||||
if (ct_index) |idx| {
|
||||
if (idx >= boundary.host_start) {
|
||||
visible_start_index = boundary.host_start;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the visible portion of the path
|
||||
const visible_path_len = if (path_len > visible_start_index) path_len - visible_start_index else 0;
|
||||
|
||||
// Allocate and return the visible path using call_arena (short-lived)
|
||||
const path = try page.call_arena.alloc(*EventTarget, visible_path_len);
|
||||
@memcpy(path, path_buffer[visible_start_index..path_len]);
|
||||
return path;
|
||||
}
|
||||
|
||||
@@ -257,16 +339,21 @@ pub fn inheritOptions(comptime T: type, comptime additions: anytype) type {
|
||||
});
|
||||
}
|
||||
|
||||
pub fn populatePrototypes(self: anytype, opts: anytype) void {
|
||||
pub fn populatePrototypes(self: anytype, opts: anytype, trusted: bool) void {
|
||||
const T = @TypeOf(self.*);
|
||||
|
||||
if (@hasField(T, "_proto")) {
|
||||
populatePrototypes(self._proto, opts);
|
||||
populatePrototypes(self._proto, opts, trusted);
|
||||
}
|
||||
|
||||
if (@hasDecl(T, "populateFromOptions")) {
|
||||
T.populateFromOptions(self, opts);
|
||||
}
|
||||
|
||||
// Set isTrusted at the Event level (base of prototype chain)
|
||||
if (T == Event or @hasField(T, "_isTrusted")) {
|
||||
self._isTrusted = trusted;
|
||||
}
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
@@ -289,10 +376,12 @@ pub const JsApi = struct {
|
||||
pub const eventPhase = bridge.accessor(Event.getEventPhase, null, .{});
|
||||
pub const defaultPrevented = bridge.accessor(Event.getDefaultPrevented, null, .{});
|
||||
pub const timeStamp = bridge.accessor(Event.getTimeStamp, null, .{});
|
||||
pub const isTrusted = bridge.accessor(Event.getIsTrusted, null, .{});
|
||||
pub const preventDefault = bridge.function(Event.preventDefault, .{});
|
||||
pub const stopPropagation = bridge.function(Event.stopPropagation, .{});
|
||||
pub const stopImmediatePropagation = bridge.function(Event.stopImmediatePropagation, .{});
|
||||
pub const composedPath = bridge.function(Event.composedPath, .{});
|
||||
pub const initEvent = bridge.function(Event.initEvent, .{});
|
||||
|
||||
// Event phase constants
|
||||
pub const NONE = bridge.property(@intFromEnum(EventPhase.none));
|
||||
|
||||
@@ -79,7 +79,7 @@ fn goInner(delta: i32, page: *Page) !void {
|
||||
|
||||
if (entry._url) |url| {
|
||||
if (try page.isSameOrigin(url)) {
|
||||
const event = try PopStateEvent.init("popstate", .{ .state = entry._state.value }, page);
|
||||
const event = try PopStateEvent.initTrusted("popstate", .{ .state = entry._state.value }, page);
|
||||
|
||||
try page._event_manager.dispatchWithFunction(
|
||||
page.window.asEventTarget(),
|
||||
|
||||
@@ -129,7 +129,7 @@ const PostMessageCallback = struct {
|
||||
return null;
|
||||
}
|
||||
|
||||
const event = MessageEvent.init("message", .{
|
||||
const event = MessageEvent.initTrusted("message", .{
|
||||
.data = self.message,
|
||||
.origin = "",
|
||||
.source = null,
|
||||
|
||||
@@ -33,7 +33,7 @@ pub fn asAbstractRange(self: *Range) *AbstractRange {
|
||||
}
|
||||
|
||||
pub fn init(page: *Page) !*Range {
|
||||
return page._factory.abstractRange(Range{._proto = undefined}, page);
|
||||
return page._factory.abstractRange(Range{ ._proto = undefined }, page);
|
||||
}
|
||||
|
||||
pub fn setStart(self: *Range, node: *Node, offset: u32) !void {
|
||||
@@ -106,7 +106,7 @@ pub fn collapse(self: *Range, to_start: ?bool) void {
|
||||
}
|
||||
|
||||
pub fn cloneRange(self: *const Range, page: *Page) !*Range {
|
||||
const clone = try page._factory.abstractRange(Range{._proto = undefined}, page);
|
||||
const clone = try page._factory.abstractRange(Range{ ._proto = undefined }, page);
|
||||
clone._proto._end_offset = self._proto._end_offset;
|
||||
clone._proto._start_offset = self._proto._start_offset;
|
||||
clone._proto._end_container = self._proto._end_container;
|
||||
|
||||
@@ -270,7 +270,7 @@ pub fn cancelIdleCallback(self: *Window, id: u32) void {
|
||||
}
|
||||
|
||||
pub fn reportError(self: *Window, err: js.Object, page: *Page) !void {
|
||||
const error_event = try ErrorEvent.init("error", .{
|
||||
const error_event = try ErrorEvent.initTrusted("error", .{
|
||||
.@"error" = err,
|
||||
.message = err.toString() catch "Unknown error",
|
||||
.bubbles = false,
|
||||
@@ -410,7 +410,7 @@ pub fn scrollTo(self: *Window, opts: ScrollToOpts, y: ?i32, page: *Page) !void {
|
||||
return null;
|
||||
}
|
||||
|
||||
const event = try Event.init("scroll", .{ .bubbles = true }, p);
|
||||
const event = try Event.initTrusted("scroll", .{ .bubbles = true }, p);
|
||||
try p._event_manager.dispatch(p.document.asEventTarget(), event);
|
||||
|
||||
pos.state = .end;
|
||||
@@ -437,7 +437,7 @@ pub fn scrollTo(self: *Window, opts: ScrollToOpts, y: ?i32, page: *Page) !void {
|
||||
.end => {},
|
||||
.done => return null,
|
||||
}
|
||||
const event = try Event.init("scrollend", .{ .bubbles = true }, p);
|
||||
const event = try Event.initTrusted("scrollend", .{ .bubbles = true }, p);
|
||||
try p._event_manager.dispatch(p.document.asEventTarget(), event);
|
||||
|
||||
pos.state = .done;
|
||||
@@ -586,7 +586,7 @@ const PostMessageCallback = struct {
|
||||
const self: *PostMessageCallback = @ptrCast(@alignCast(ctx));
|
||||
defer self.deinit();
|
||||
|
||||
const message_event = try MessageEvent.init("message", .{
|
||||
const message_event = try MessageEvent.initTrusted("message", .{
|
||||
.data = self.message,
|
||||
.origin = self.origin,
|
||||
.source = self.window,
|
||||
|
||||
@@ -306,6 +306,7 @@ pub fn click(self: *HtmlElement, page: *Page) !void {
|
||||
const event = try @import("../event/MouseEvent.zig").init("click", .{
|
||||
.bubbles = true,
|
||||
.cancelable = true,
|
||||
.composed = true,
|
||||
.clientX = 0,
|
||||
.clientY = 0,
|
||||
}, page);
|
||||
|
||||
@@ -40,7 +40,7 @@ pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*CompositionEvent {
|
||||
._data = if (opts.data) |str| try page.dupeString(str) else "",
|
||||
});
|
||||
|
||||
Event.populatePrototypes(event, opts);
|
||||
Event.populatePrototypes(event, opts, false);
|
||||
return event;
|
||||
}
|
||||
|
||||
|
||||
@@ -49,23 +49,23 @@ pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*CustomEvent {
|
||||
},
|
||||
);
|
||||
|
||||
Event.populatePrototypes(event, opts);
|
||||
Event.populatePrototypes(event, opts, false);
|
||||
return event;
|
||||
}
|
||||
|
||||
pub fn initCustomEvent(
|
||||
pub fn initCustomEvent(
|
||||
self: *CustomEvent,
|
||||
event_string: []const u8,
|
||||
bubbles: bool,
|
||||
cancelable: bool,
|
||||
bubbles: ?bool,
|
||||
cancelable: ?bool,
|
||||
detail_: ?js.Object,
|
||||
page: *Page,
|
||||
) !void {
|
||||
// This function can only be called after the constructor has called.
|
||||
// So we assume proto is initialized already by constructor.
|
||||
self._proto._type_string = try String.init(page.arena, event_string, .{});
|
||||
self._proto._bubbles = bubbles;
|
||||
self._proto._cancelable = cancelable;
|
||||
self._proto._bubbles = bubbles orelse false;
|
||||
self._proto._cancelable = cancelable orelse false;
|
||||
// Detail is stored separately.
|
||||
if (detail_) |detail| {
|
||||
self._detail = try detail.persist();
|
||||
|
||||
@@ -44,6 +44,14 @@ pub const ErrorEventOptions = struct {
|
||||
const Options = Event.inheritOptions(ErrorEvent, ErrorEventOptions);
|
||||
|
||||
pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*ErrorEvent {
|
||||
return initWithTrusted(typ, opts_, false, page);
|
||||
}
|
||||
|
||||
pub fn initTrusted(typ: []const u8, opts_: ?Options, page: *Page) !*ErrorEvent {
|
||||
return initWithTrusted(typ, opts_, true, page);
|
||||
}
|
||||
|
||||
fn initWithTrusted(typ: []const u8, opts_: ?Options, trusted: bool, page: *Page) !*ErrorEvent {
|
||||
const arena = page.arena;
|
||||
const opts = opts_ orelse Options{};
|
||||
|
||||
@@ -60,7 +68,7 @@ pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*ErrorEvent {
|
||||
},
|
||||
);
|
||||
|
||||
Event.populatePrototypes(event, opts);
|
||||
Event.populatePrototypes(event, opts, trusted);
|
||||
return event;
|
||||
}
|
||||
|
||||
|
||||
@@ -182,7 +182,15 @@ const Options = Event.inheritOptions(
|
||||
KeyboardEventOptions,
|
||||
);
|
||||
|
||||
pub fn initTrusted(typ: []const u8, _opts: ?Options, page: *Page) !*KeyboardEvent {
|
||||
return initWithTrusted(typ, _opts, true, page);
|
||||
}
|
||||
|
||||
pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*KeyboardEvent {
|
||||
return initWithTrusted(typ, _opts, false, page);
|
||||
}
|
||||
|
||||
fn initWithTrusted(typ: []const u8, _opts: ?Options, trusted: bool, page: *Page) !*KeyboardEvent {
|
||||
const opts = _opts orelse Options{};
|
||||
|
||||
const event = try page._factory.uiEvent(
|
||||
@@ -201,7 +209,7 @@ pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*KeyboardEvent {
|
||||
},
|
||||
);
|
||||
|
||||
Event.populatePrototypes(event, opts);
|
||||
Event.populatePrototypes(event, opts, trusted);
|
||||
return event;
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,14 @@ const MessageEventOptions = struct {
|
||||
const Options = Event.inheritOptions(MessageEvent, MessageEventOptions);
|
||||
|
||||
pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*MessageEvent {
|
||||
return initWithTrusted(typ, opts_, false, page);
|
||||
}
|
||||
|
||||
pub fn initTrusted(typ: []const u8, opts_: ?Options, page: *Page) !*MessageEvent {
|
||||
return initWithTrusted(typ, opts_, true, page);
|
||||
}
|
||||
|
||||
fn initWithTrusted(typ: []const u8, opts_: ?Options, trusted: bool, page: *Page) !*MessageEvent {
|
||||
const opts = opts_ orelse Options{};
|
||||
|
||||
const event = try page._factory.event(
|
||||
@@ -50,7 +58,7 @@ pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*MessageEvent {
|
||||
},
|
||||
);
|
||||
|
||||
Event.populatePrototypes(event, opts);
|
||||
Event.populatePrototypes(event, opts, trusted);
|
||||
return event;
|
||||
}
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*MouseEvent {
|
||||
},
|
||||
);
|
||||
|
||||
Event.populatePrototypes(event, opts);
|
||||
Event.populatePrototypes(event, opts, false);
|
||||
return event;
|
||||
}
|
||||
|
||||
|
||||
@@ -40,9 +40,18 @@ const Options = Event.inheritOptions(
|
||||
NavigationCurrentEntryChangeEventOptions,
|
||||
);
|
||||
|
||||
pub fn init(
|
||||
pub fn init(typ: []const u8, opts: Options, page: *Page) !*NavigationCurrentEntryChangeEvent {
|
||||
return initWithTrusted(typ, opts, false, page);
|
||||
}
|
||||
|
||||
pub fn initTrusted(typ: []const u8, opts: Options, page: *Page) !*NavigationCurrentEntryChangeEvent {
|
||||
return initWithTrusted(typ, opts, true, page);
|
||||
}
|
||||
|
||||
fn initWithTrusted(
|
||||
typ: []const u8,
|
||||
opts: Options,
|
||||
trusted: bool,
|
||||
page: *Page,
|
||||
) !*NavigationCurrentEntryChangeEvent {
|
||||
const navigation_type = if (opts.navigationType) |nav_type_str|
|
||||
@@ -59,7 +68,7 @@ pub fn init(
|
||||
},
|
||||
);
|
||||
|
||||
Event.populatePrototypes(event, opts);
|
||||
Event.populatePrototypes(event, opts, trusted);
|
||||
return event;
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,14 @@ const PageTransitionEventOptions = struct {
|
||||
const Options = Event.inheritOptions(PageTransitionEvent, PageTransitionEventOptions);
|
||||
|
||||
pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*PageTransitionEvent {
|
||||
return initWithTrusted(typ, _opts, false, page);
|
||||
}
|
||||
|
||||
pub fn initTrusted(typ: []const u8, _opts: ?Options, page: *Page) !*PageTransitionEvent {
|
||||
return initWithTrusted(typ, _opts, true, page);
|
||||
}
|
||||
|
||||
fn initWithTrusted(typ: []const u8, _opts: ?Options, trusted: bool, page: *Page) !*PageTransitionEvent {
|
||||
const opts = _opts orelse Options{};
|
||||
|
||||
const event = try page._factory.event(
|
||||
@@ -45,7 +53,7 @@ pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*PageTransitionEvent
|
||||
},
|
||||
);
|
||||
|
||||
Event.populatePrototypes(event, opts);
|
||||
Event.populatePrototypes(event, opts, trusted);
|
||||
return event;
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,14 @@ const PopStateEventOptions = struct {
|
||||
const Options = Event.inheritOptions(PopStateEvent, PopStateEventOptions);
|
||||
|
||||
pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*PopStateEvent {
|
||||
return initWithTrusted(typ, _opts, false, page);
|
||||
}
|
||||
|
||||
pub fn initTrusted(typ: []const u8, _opts: ?Options, page: *Page) !*PopStateEvent {
|
||||
return initWithTrusted(typ, _opts, true, page);
|
||||
}
|
||||
|
||||
fn initWithTrusted(typ: []const u8, _opts: ?Options, trusted: bool, page: *Page) !*PopStateEvent {
|
||||
const opts = _opts orelse Options{};
|
||||
|
||||
const event = try page._factory.event(
|
||||
@@ -45,7 +53,7 @@ pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*PopStateEvent {
|
||||
},
|
||||
);
|
||||
|
||||
Event.populatePrototypes(event, opts);
|
||||
Event.populatePrototypes(event, opts, trusted);
|
||||
return event;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,14 @@ const ProgressEventOptions = struct {
|
||||
const Options = Event.inheritOptions(ProgressEvent, ProgressEventOptions);
|
||||
|
||||
pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*ProgressEvent {
|
||||
return initWithTrusted(typ, _opts, false, page);
|
||||
}
|
||||
|
||||
pub fn initTrusted(typ: []const u8, _opts: ?Options, page: *Page) !*ProgressEvent {
|
||||
return initWithTrusted(typ, _opts, true, page);
|
||||
}
|
||||
|
||||
fn initWithTrusted(typ: []const u8, _opts: ?Options, trusted: bool, page: *Page) !*ProgressEvent {
|
||||
const opts = _opts orelse Options{};
|
||||
|
||||
const event = try page._factory.event(
|
||||
@@ -45,7 +53,7 @@ pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*ProgressEvent {
|
||||
},
|
||||
);
|
||||
|
||||
Event.populatePrototypes(event, opts);
|
||||
Event.populatePrototypes(event, opts, trusted);
|
||||
return event;
|
||||
}
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*UIEvent {
|
||||
},
|
||||
);
|
||||
|
||||
Event.populatePrototypes(event, opts);
|
||||
Event.populatePrototypes(event, opts, false);
|
||||
return event;
|
||||
}
|
||||
|
||||
|
||||
@@ -201,7 +201,7 @@ pub fn pushEntry(
|
||||
|
||||
if (previous) |prev| {
|
||||
if (dispatch) {
|
||||
const event = try NavigationCurrentEntryChangeEvent.init(
|
||||
const event = try NavigationCurrentEntryChangeEvent.initTrusted(
|
||||
"currententrychange",
|
||||
.{ .from = prev, .navigationType = @tagName(.push) },
|
||||
page,
|
||||
@@ -240,7 +240,7 @@ pub fn replaceEntry(
|
||||
self._entries.items[self._index] = entry;
|
||||
|
||||
if (dispatch) {
|
||||
const event = try NavigationCurrentEntryChangeEvent.init(
|
||||
const event = try NavigationCurrentEntryChangeEvent.initTrusted(
|
||||
"currententrychange",
|
||||
.{ .from = previous, .navigationType = @tagName(.replace) },
|
||||
page,
|
||||
@@ -324,7 +324,7 @@ pub fn navigateInner(
|
||||
}
|
||||
|
||||
// If we haven't navigated off, let us fire off an a currententrychange.
|
||||
const event = try NavigationCurrentEntryChangeEvent.init(
|
||||
const event = try NavigationCurrentEntryChangeEvent.initTrusted(
|
||||
"currententrychange",
|
||||
.{ .from = previous, .navigationType = @tagName(kind) },
|
||||
page,
|
||||
@@ -363,7 +363,7 @@ pub fn reload(self: *Navigation, _opts: ?ReloadOptions, page: *Page) !Navigation
|
||||
const previous = entry;
|
||||
entry._state = .{ .source = .navigation, .value = state.toJson(arena) catch return error.DataClone };
|
||||
|
||||
const event = try NavigationCurrentEntryChangeEvent.init(
|
||||
const event = try NavigationCurrentEntryChangeEvent.initTrusted(
|
||||
"currententrychange",
|
||||
.{ .from = previous, .navigationType = @tagName(.reload) },
|
||||
page,
|
||||
@@ -405,7 +405,7 @@ pub fn updateCurrentEntry(self: *Navigation, options: UpdateCurrentEntryOptions,
|
||||
.value = options.state.toJson(arena) catch return error.DataClone,
|
||||
};
|
||||
|
||||
const event = try NavigationCurrentEntryChangeEvent.init(
|
||||
const event = try NavigationCurrentEntryChangeEvent.initTrusted(
|
||||
"currententrychange",
|
||||
.{ .from = previous, .navigationType = null },
|
||||
page,
|
||||
|
||||
@@ -416,7 +416,7 @@ fn stateChanged(self: *XMLHttpRequest, state: ReadyState, page: *Page) !void {
|
||||
|
||||
self._ready_state = state;
|
||||
|
||||
const event = try Event.init("readystatechange", .{}, page);
|
||||
const event = try Event.initTrusted("readystatechange", .{}, page);
|
||||
try page._event_manager.dispatchWithFunction(
|
||||
self.asEventTarget(),
|
||||
event,
|
||||
|
||||
@@ -57,7 +57,7 @@ pub fn dispatch(self: *XMLHttpRequestEventTarget, comptime event_type: DispatchT
|
||||
};
|
||||
|
||||
const progress = progress_ orelse Progress{};
|
||||
const event = try ProgressEvent.init(
|
||||
const event = try ProgressEvent.initTrusted(
|
||||
typ,
|
||||
.{ .total = progress.total, .loaded = progress.loaded },
|
||||
page,
|
||||
|
||||
@@ -60,7 +60,7 @@ fn dispatchKeyEvent(cmd: anytype) !void {
|
||||
const page = bc.session.currentPage() orelse return;
|
||||
|
||||
const KeyboardEvent = @import("../../browser/webapi/event/KeyboardEvent.zig");
|
||||
const keyboard_event = try KeyboardEvent.init("keydown", .{
|
||||
const keyboard_event = try KeyboardEvent.initTrusted("keydown", .{
|
||||
.key = params.key,
|
||||
.code = params.code,
|
||||
.altKey = params.modifiers & 1 == 1,
|
||||
|
||||
Reference in New Issue
Block a user