mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-02-04 14:33:47 +00:00
event isTrusted support and better composedPath for shadowroots
This commit is contained in:
@@ -83,6 +83,7 @@ pub fn register(self: *EventManager, target: *EventTarget, typ: []const u8, call
|
|||||||
var node = gop.value_ptr.*.first;
|
var node = gop.value_ptr.*.first;
|
||||||
while (node) |n| {
|
while (node) |n| {
|
||||||
const listener: *Listener = @alignCast(@fieldParentPtr("node", n));
|
const listener: *Listener = @alignCast(@fieldParentPtr("node", n));
|
||||||
|
if (listener.typ.eqlSlice(typ)) {
|
||||||
const is_duplicate = switch (callback) {
|
const is_duplicate = switch (callback) {
|
||||||
.object => |obj| listener.function.eqlObject(obj),
|
.object => |obj| listener.function.eqlObject(obj),
|
||||||
.function => |func| listener.function.eqlFunction(func),
|
.function => |func| listener.function.eqlFunction(func),
|
||||||
@@ -90,6 +91,7 @@ pub fn register(self: *EventManager, target: *EventTarget, typ: []const u8, call
|
|||||||
if (is_duplicate and listener.capture == opts.capture) {
|
if (is_duplicate and listener.capture == opts.capture) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
node = n.next;
|
node = n.next;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -132,6 +134,7 @@ pub fn dispatch(self: *EventManager, target: *EventTarget, event: *Event) !void
|
|||||||
}
|
}
|
||||||
|
|
||||||
event._target = target;
|
event._target = target;
|
||||||
|
event._dispatch_target = target; // Store original target for composedPath()
|
||||||
var was_handled = false;
|
var was_handled = false;
|
||||||
|
|
||||||
defer if (was_handled) {
|
defer if (was_handled) {
|
||||||
@@ -173,6 +176,7 @@ pub fn dispatchWithFunction(self: *EventManager, target: *EventTarget, event: *E
|
|||||||
|
|
||||||
if (comptime opts.inject_target) {
|
if (comptime opts.inject_target) {
|
||||||
event._target = target;
|
event._target = target;
|
||||||
|
event._dispatch_target = target; // Store original target for composedPath()
|
||||||
}
|
}
|
||||||
|
|
||||||
var was_dispatched = false;
|
var was_dispatched = false;
|
||||||
|
|||||||
@@ -503,7 +503,7 @@ pub fn documentIsLoaded(self: *Page) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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(
|
try self._event_manager.dispatch(
|
||||||
self.document.asEventTarget(),
|
self.document.asEventTarget(),
|
||||||
event,
|
event,
|
||||||
@@ -549,7 +549,7 @@ fn _documentIsComplete(self: *Page) !void {
|
|||||||
self.document._ready_state = .complete;
|
self.document._ready_state = .complete;
|
||||||
|
|
||||||
// dispatch window.load event
|
// 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
|
// this event is weird, it's dispatched directly on the window, but
|
||||||
// with the document as the target
|
// with the document as the target
|
||||||
event._target = self.document.asEventTarget();
|
event._target = self.document.asEventTarget();
|
||||||
@@ -560,7 +560,7 @@ fn _documentIsComplete(self: *Page) !void {
|
|||||||
.{ .inject_target = false, .context = "page load" },
|
.{ .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(
|
try self._event_manager.dispatchWithFunction(
|
||||||
self.window.asEventTarget(),
|
self.window.asEventTarget(),
|
||||||
pageshow_event.asEvent(),
|
pageshow_event.asEvent(),
|
||||||
@@ -1174,7 +1174,7 @@ pub fn deliverSlotchangeEvents(self: *Page) void {
|
|||||||
self._slots_pending_slotchange.clearRetainingCapacity();
|
self._slots_pending_slotchange.clearRetainingCapacity();
|
||||||
|
|
||||||
for (slots) |slot| {
|
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 });
|
log.err(.page, "deliverSlotchange.init", .{ .err = err });
|
||||||
continue;
|
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", .{
|
const event = try @import("webapi/event/MouseEvent.zig").init("click", .{
|
||||||
.bubbles = true,
|
.bubbles = true,
|
||||||
.cancelable = true,
|
.cancelable = true,
|
||||||
|
.composed = true,
|
||||||
.clientX = x,
|
.clientX = x,
|
||||||
.clientY = y,
|
.clientY = y,
|
||||||
}, self);
|
}, self);
|
||||||
|
|||||||
@@ -833,7 +833,7 @@ pub const Script = struct {
|
|||||||
const cb = cb_ orelse return;
|
const cb = cb_ orelse return;
|
||||||
|
|
||||||
const Event = @import("webapi/Event.zig");
|
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", .{
|
log.warn(.js, "script internal callback", .{
|
||||||
.url = self.url,
|
.url = self.url,
|
||||||
.type = typ,
|
.type = typ,
|
||||||
|
|||||||
@@ -88,3 +88,18 @@
|
|||||||
|
|
||||||
testing.expectEqual(true, isKeyPress);
|
testing.expectEqual(true, isKeyPress);
|
||||||
</script>
|
</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('click', evt.type);
|
||||||
testing.expectEqual(true, evt instanceof MouseEvent);
|
testing.expectEqual(true, evt instanceof MouseEvent);
|
||||||
</script>
|
</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'));
|
content.dispatchEvent(new Event('he2'));
|
||||||
}
|
}
|
||||||
</script>
|
</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.bubbles);
|
||||||
testing.expectEqual(true, evt.cancelable);
|
testing.expectEqual(true, evt.cancelable);
|
||||||
testing.expectEqual(true, evt.defaultPrevented);
|
testing.expectEqual(true, evt.defaultPrevented);
|
||||||
testing.expectEqual(true, evt.isTrusted);
|
testing.expectEqual(false, evt.isTrusted);
|
||||||
testing.expectEqual(true, evt.timeStamp >= Math.floor(startTime));
|
testing.expectEqual(true, evt.timeStamp >= Math.floor(startTime));
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -184,3 +184,100 @@
|
|||||||
host.remove();
|
host.remove();
|
||||||
}
|
}
|
||||||
</script>
|
</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
|
// Dispatch abort event
|
||||||
const event = try Event.init("abort", .{}, page);
|
const event = try Event.initTrusted("abort", .{}, page);
|
||||||
try page._event_manager.dispatchWithFunction(
|
try page._event_manager.dispatchWithFunction(
|
||||||
self.asEventTarget(),
|
self.asEventTarget(),
|
||||||
event,
|
event,
|
||||||
|
|||||||
@@ -570,7 +570,7 @@ pub fn getChildElementCount(self: *Document) u32 {
|
|||||||
return i;
|
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| {
|
if (self._adopted_style_sheets) |ass| {
|
||||||
return ass;
|
return ass;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -596,7 +596,7 @@ pub fn focus(self: *Element, page: *Page) !void {
|
|||||||
return;
|
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);
|
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;
|
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);
|
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;
|
page.document._active_element = null;
|
||||||
|
|
||||||
const Event = @import("Event.zig");
|
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);
|
try page._event_manager.dispatch(self.asEventTarget(), blur_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,12 +35,14 @@ _composed: bool = false,
|
|||||||
_type_string: String,
|
_type_string: String,
|
||||||
_target: ?*EventTarget = null,
|
_target: ?*EventTarget = null,
|
||||||
_current_target: ?*EventTarget = null,
|
_current_target: ?*EventTarget = null,
|
||||||
|
_dispatch_target: ?*EventTarget = null, // Original target for composedPath()
|
||||||
_prevent_default: bool = false,
|
_prevent_default: bool = false,
|
||||||
_stop_propagation: bool = false,
|
_stop_propagation: bool = false,
|
||||||
_stop_immediate_propagation: bool = false,
|
_stop_immediate_propagation: bool = false,
|
||||||
_event_phase: EventPhase = .none,
|
_event_phase: EventPhase = .none,
|
||||||
_time_stamp: u64 = 0,
|
_time_stamp: u64 = 0,
|
||||||
_needs_retargeting: bool = false,
|
_needs_retargeting: bool = false,
|
||||||
|
_isTrusted: bool = false,
|
||||||
|
|
||||||
pub const EventPhase = enum(u8) {
|
pub const EventPhase = enum(u8) {
|
||||||
none = 0,
|
none = 0,
|
||||||
@@ -68,14 +70,22 @@ pub const Options = struct {
|
|||||||
composed: bool = false,
|
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 {
|
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{};
|
const opts = opts_ orelse Options{};
|
||||||
|
|
||||||
// Round to 2ms for privacy (browsers do this)
|
// Round to 2ms for privacy (browsers do this)
|
||||||
const raw_timestamp = @import("../../datetime.zig").milliTimestamp(.monotonic);
|
const raw_timestamp = @import("../../datetime.zig").milliTimestamp(.monotonic);
|
||||||
const time_stamp = (raw_timestamp / 2) * 2;
|
const time_stamp = (raw_timestamp / 2) * 2;
|
||||||
|
|
||||||
return page._factory.create(Event{
|
const event = try page._factory.create(Event{
|
||||||
._type = .generic,
|
._type = .generic,
|
||||||
._bubbles = opts.bubbles,
|
._bubbles = opts.bubbles,
|
||||||
._time_stamp = time_stamp,
|
._time_stamp = time_stamp,
|
||||||
@@ -83,6 +93,21 @@ pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*Event {
|
|||||||
._composed = opts.composed,
|
._composed = opts.composed,
|
||||||
._type_string = try String.init(page.arena, typ, .{}),
|
._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 {
|
pub fn as(self: *Event, comptime T: type) *T {
|
||||||
@@ -159,14 +184,27 @@ pub fn getTimeStamp(self: *const Event) u64 {
|
|||||||
return self._time_stamp;
|
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 {
|
pub fn composedPath(self: *Event, page: *Page) ![]const *EventTarget {
|
||||||
// Return empty array if event is not being dispatched
|
// Return empty array if event is not being dispatched
|
||||||
if (self._event_phase == .none) {
|
if (self._event_phase == .none) {
|
||||||
return &.{};
|
return &.{};
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there's no target, return empty array
|
// Use dispatch_target (original target) if available, otherwise fall back to target
|
||||||
const target = self._target orelse return &.{};
|
// 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
|
// Only nodes have a propagation path
|
||||||
const target_node = switch (target._type) {
|
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 path_buffer: [128]*EventTarget = undefined;
|
||||||
var stopped_at_shadow_boundary = false;
|
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;
|
var node: ?*Node = target_node;
|
||||||
while (node) |n| {
|
while (node) |n| {
|
||||||
if (path_len >= path_buffer.len) {
|
if (path_len >= path_buffer.len) {
|
||||||
@@ -198,7 +239,17 @@ pub fn composedPath(self: *Event, page: *Page) ![]const *EventTarget {
|
|||||||
break;
|
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();
|
node = shadow._host.asNode();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -215,9 +266,40 @@ pub fn composedPath(self: *Event, page: *Page) ![]const *EventTarget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocate and return the path using call_arena (short-lived)
|
// Determine visible path based on current_target and closed shadow boundaries
|
||||||
const path = try page.call_arena.alloc(*EventTarget, path_len);
|
var visible_start_index: usize = 0;
|
||||||
@memcpy(path, path_buffer[0..path_len]);
|
|
||||||
|
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;
|
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.*);
|
const T = @TypeOf(self.*);
|
||||||
|
|
||||||
if (@hasField(T, "_proto")) {
|
if (@hasField(T, "_proto")) {
|
||||||
populatePrototypes(self._proto, opts);
|
populatePrototypes(self._proto, opts, trusted);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (@hasDecl(T, "populateFromOptions")) {
|
if (@hasDecl(T, "populateFromOptions")) {
|
||||||
T.populateFromOptions(self, opts);
|
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 {
|
pub const JsApi = struct {
|
||||||
@@ -289,10 +376,12 @@ pub const JsApi = struct {
|
|||||||
pub const eventPhase = bridge.accessor(Event.getEventPhase, null, .{});
|
pub const eventPhase = bridge.accessor(Event.getEventPhase, null, .{});
|
||||||
pub const defaultPrevented = bridge.accessor(Event.getDefaultPrevented, null, .{});
|
pub const defaultPrevented = bridge.accessor(Event.getDefaultPrevented, null, .{});
|
||||||
pub const timeStamp = bridge.accessor(Event.getTimeStamp, 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 preventDefault = bridge.function(Event.preventDefault, .{});
|
||||||
pub const stopPropagation = bridge.function(Event.stopPropagation, .{});
|
pub const stopPropagation = bridge.function(Event.stopPropagation, .{});
|
||||||
pub const stopImmediatePropagation = bridge.function(Event.stopImmediatePropagation, .{});
|
pub const stopImmediatePropagation = bridge.function(Event.stopImmediatePropagation, .{});
|
||||||
pub const composedPath = bridge.function(Event.composedPath, .{});
|
pub const composedPath = bridge.function(Event.composedPath, .{});
|
||||||
|
pub const initEvent = bridge.function(Event.initEvent, .{});
|
||||||
|
|
||||||
// Event phase constants
|
// Event phase constants
|
||||||
pub const NONE = bridge.property(@intFromEnum(EventPhase.none));
|
pub const NONE = bridge.property(@intFromEnum(EventPhase.none));
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ fn goInner(delta: i32, page: *Page) !void {
|
|||||||
|
|
||||||
if (entry._url) |url| {
|
if (entry._url) |url| {
|
||||||
if (try page.isSameOrigin(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(
|
try page._event_manager.dispatchWithFunction(
|
||||||
page.window.asEventTarget(),
|
page.window.asEventTarget(),
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ const PostMessageCallback = struct {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const event = MessageEvent.init("message", .{
|
const event = MessageEvent.initTrusted("message", .{
|
||||||
.data = self.message,
|
.data = self.message,
|
||||||
.origin = "",
|
.origin = "",
|
||||||
.source = null,
|
.source = null,
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ pub fn asAbstractRange(self: *Range) *AbstractRange {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(page: *Page) !*Range {
|
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 {
|
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 {
|
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._end_offset = self._proto._end_offset;
|
||||||
clone._proto._start_offset = self._proto._start_offset;
|
clone._proto._start_offset = self._proto._start_offset;
|
||||||
clone._proto._end_container = self._proto._end_container;
|
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 {
|
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,
|
.@"error" = err,
|
||||||
.message = err.toString() catch "Unknown error",
|
.message = err.toString() catch "Unknown error",
|
||||||
.bubbles = false,
|
.bubbles = false,
|
||||||
@@ -410,7 +410,7 @@ pub fn scrollTo(self: *Window, opts: ScrollToOpts, y: ?i32, page: *Page) !void {
|
|||||||
return null;
|
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);
|
try p._event_manager.dispatch(p.document.asEventTarget(), event);
|
||||||
|
|
||||||
pos.state = .end;
|
pos.state = .end;
|
||||||
@@ -437,7 +437,7 @@ pub fn scrollTo(self: *Window, opts: ScrollToOpts, y: ?i32, page: *Page) !void {
|
|||||||
.end => {},
|
.end => {},
|
||||||
.done => return null,
|
.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);
|
try p._event_manager.dispatch(p.document.asEventTarget(), event);
|
||||||
|
|
||||||
pos.state = .done;
|
pos.state = .done;
|
||||||
@@ -586,7 +586,7 @@ const PostMessageCallback = struct {
|
|||||||
const self: *PostMessageCallback = @ptrCast(@alignCast(ctx));
|
const self: *PostMessageCallback = @ptrCast(@alignCast(ctx));
|
||||||
defer self.deinit();
|
defer self.deinit();
|
||||||
|
|
||||||
const message_event = try MessageEvent.init("message", .{
|
const message_event = try MessageEvent.initTrusted("message", .{
|
||||||
.data = self.message,
|
.data = self.message,
|
||||||
.origin = self.origin,
|
.origin = self.origin,
|
||||||
.source = self.window,
|
.source = self.window,
|
||||||
|
|||||||
@@ -306,6 +306,7 @@ pub fn click(self: *HtmlElement, page: *Page) !void {
|
|||||||
const event = try @import("../event/MouseEvent.zig").init("click", .{
|
const event = try @import("../event/MouseEvent.zig").init("click", .{
|
||||||
.bubbles = true,
|
.bubbles = true,
|
||||||
.cancelable = true,
|
.cancelable = true,
|
||||||
|
.composed = true,
|
||||||
.clientX = 0,
|
.clientX = 0,
|
||||||
.clientY = 0,
|
.clientY = 0,
|
||||||
}, page);
|
}, 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 "",
|
._data = if (opts.data) |str| try page.dupeString(str) else "",
|
||||||
});
|
});
|
||||||
|
|
||||||
Event.populatePrototypes(event, opts);
|
Event.populatePrototypes(event, opts, false);
|
||||||
return event;
|
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;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn initCustomEvent(
|
pub fn initCustomEvent(
|
||||||
self: *CustomEvent,
|
self: *CustomEvent,
|
||||||
event_string: []const u8,
|
event_string: []const u8,
|
||||||
bubbles: bool,
|
bubbles: ?bool,
|
||||||
cancelable: bool,
|
cancelable: ?bool,
|
||||||
detail_: ?js.Object,
|
detail_: ?js.Object,
|
||||||
page: *Page,
|
page: *Page,
|
||||||
) !void {
|
) !void {
|
||||||
// This function can only be called after the constructor has called.
|
// This function can only be called after the constructor has called.
|
||||||
// So we assume proto is initialized already by constructor.
|
// So we assume proto is initialized already by constructor.
|
||||||
self._proto._type_string = try String.init(page.arena, event_string, .{});
|
self._proto._type_string = try String.init(page.arena, event_string, .{});
|
||||||
self._proto._bubbles = bubbles;
|
self._proto._bubbles = bubbles orelse false;
|
||||||
self._proto._cancelable = cancelable;
|
self._proto._cancelable = cancelable orelse false;
|
||||||
// Detail is stored separately.
|
// Detail is stored separately.
|
||||||
if (detail_) |detail| {
|
if (detail_) |detail| {
|
||||||
self._detail = try detail.persist();
|
self._detail = try detail.persist();
|
||||||
|
|||||||
@@ -44,6 +44,14 @@ pub const ErrorEventOptions = struct {
|
|||||||
const Options = Event.inheritOptions(ErrorEvent, ErrorEventOptions);
|
const Options = Event.inheritOptions(ErrorEvent, ErrorEventOptions);
|
||||||
|
|
||||||
pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*ErrorEvent {
|
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 arena = page.arena;
|
||||||
const opts = opts_ orelse Options{};
|
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;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -182,7 +182,15 @@ const Options = Event.inheritOptions(
|
|||||||
KeyboardEventOptions,
|
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 {
|
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 opts = _opts orelse Options{};
|
||||||
|
|
||||||
const event = try page._factory.uiEvent(
|
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;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,14 @@ const MessageEventOptions = struct {
|
|||||||
const Options = Event.inheritOptions(MessageEvent, MessageEventOptions);
|
const Options = Event.inheritOptions(MessageEvent, MessageEventOptions);
|
||||||
|
|
||||||
pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*MessageEvent {
|
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 opts = opts_ orelse Options{};
|
||||||
|
|
||||||
const event = try page._factory.event(
|
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;
|
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;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,9 +40,18 @@ const Options = Event.inheritOptions(
|
|||||||
NavigationCurrentEntryChangeEventOptions,
|
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,
|
typ: []const u8,
|
||||||
opts: Options,
|
opts: Options,
|
||||||
|
trusted: bool,
|
||||||
page: *Page,
|
page: *Page,
|
||||||
) !*NavigationCurrentEntryChangeEvent {
|
) !*NavigationCurrentEntryChangeEvent {
|
||||||
const navigation_type = if (opts.navigationType) |nav_type_str|
|
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;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,14 @@ const PageTransitionEventOptions = struct {
|
|||||||
const Options = Event.inheritOptions(PageTransitionEvent, PageTransitionEventOptions);
|
const Options = Event.inheritOptions(PageTransitionEvent, PageTransitionEventOptions);
|
||||||
|
|
||||||
pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*PageTransitionEvent {
|
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 opts = _opts orelse Options{};
|
||||||
|
|
||||||
const event = try page._factory.event(
|
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;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,14 @@ const PopStateEventOptions = struct {
|
|||||||
const Options = Event.inheritOptions(PopStateEvent, PopStateEventOptions);
|
const Options = Event.inheritOptions(PopStateEvent, PopStateEventOptions);
|
||||||
|
|
||||||
pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*PopStateEvent {
|
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 opts = _opts orelse Options{};
|
||||||
|
|
||||||
const event = try page._factory.event(
|
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;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,14 @@ const ProgressEventOptions = struct {
|
|||||||
const Options = Event.inheritOptions(ProgressEvent, ProgressEventOptions);
|
const Options = Event.inheritOptions(ProgressEvent, ProgressEventOptions);
|
||||||
|
|
||||||
pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*ProgressEvent {
|
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 opts = _opts orelse Options{};
|
||||||
|
|
||||||
const event = try page._factory.event(
|
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;
|
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;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -201,7 +201,7 @@ pub fn pushEntry(
|
|||||||
|
|
||||||
if (previous) |prev| {
|
if (previous) |prev| {
|
||||||
if (dispatch) {
|
if (dispatch) {
|
||||||
const event = try NavigationCurrentEntryChangeEvent.init(
|
const event = try NavigationCurrentEntryChangeEvent.initTrusted(
|
||||||
"currententrychange",
|
"currententrychange",
|
||||||
.{ .from = prev, .navigationType = @tagName(.push) },
|
.{ .from = prev, .navigationType = @tagName(.push) },
|
||||||
page,
|
page,
|
||||||
@@ -240,7 +240,7 @@ pub fn replaceEntry(
|
|||||||
self._entries.items[self._index] = entry;
|
self._entries.items[self._index] = entry;
|
||||||
|
|
||||||
if (dispatch) {
|
if (dispatch) {
|
||||||
const event = try NavigationCurrentEntryChangeEvent.init(
|
const event = try NavigationCurrentEntryChangeEvent.initTrusted(
|
||||||
"currententrychange",
|
"currententrychange",
|
||||||
.{ .from = previous, .navigationType = @tagName(.replace) },
|
.{ .from = previous, .navigationType = @tagName(.replace) },
|
||||||
page,
|
page,
|
||||||
@@ -324,7 +324,7 @@ pub fn navigateInner(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If we haven't navigated off, let us fire off an a currententrychange.
|
// If we haven't navigated off, let us fire off an a currententrychange.
|
||||||
const event = try NavigationCurrentEntryChangeEvent.init(
|
const event = try NavigationCurrentEntryChangeEvent.initTrusted(
|
||||||
"currententrychange",
|
"currententrychange",
|
||||||
.{ .from = previous, .navigationType = @tagName(kind) },
|
.{ .from = previous, .navigationType = @tagName(kind) },
|
||||||
page,
|
page,
|
||||||
@@ -363,7 +363,7 @@ pub fn reload(self: *Navigation, _opts: ?ReloadOptions, page: *Page) !Navigation
|
|||||||
const previous = entry;
|
const previous = entry;
|
||||||
entry._state = .{ .source = .navigation, .value = state.toJson(arena) catch return error.DataClone };
|
entry._state = .{ .source = .navigation, .value = state.toJson(arena) catch return error.DataClone };
|
||||||
|
|
||||||
const event = try NavigationCurrentEntryChangeEvent.init(
|
const event = try NavigationCurrentEntryChangeEvent.initTrusted(
|
||||||
"currententrychange",
|
"currententrychange",
|
||||||
.{ .from = previous, .navigationType = @tagName(.reload) },
|
.{ .from = previous, .navigationType = @tagName(.reload) },
|
||||||
page,
|
page,
|
||||||
@@ -405,7 +405,7 @@ pub fn updateCurrentEntry(self: *Navigation, options: UpdateCurrentEntryOptions,
|
|||||||
.value = options.state.toJson(arena) catch return error.DataClone,
|
.value = options.state.toJson(arena) catch return error.DataClone,
|
||||||
};
|
};
|
||||||
|
|
||||||
const event = try NavigationCurrentEntryChangeEvent.init(
|
const event = try NavigationCurrentEntryChangeEvent.initTrusted(
|
||||||
"currententrychange",
|
"currententrychange",
|
||||||
.{ .from = previous, .navigationType = null },
|
.{ .from = previous, .navigationType = null },
|
||||||
page,
|
page,
|
||||||
|
|||||||
@@ -416,7 +416,7 @@ fn stateChanged(self: *XMLHttpRequest, state: ReadyState, page: *Page) !void {
|
|||||||
|
|
||||||
self._ready_state = state;
|
self._ready_state = state;
|
||||||
|
|
||||||
const event = try Event.init("readystatechange", .{}, page);
|
const event = try Event.initTrusted("readystatechange", .{}, page);
|
||||||
try page._event_manager.dispatchWithFunction(
|
try page._event_manager.dispatchWithFunction(
|
||||||
self.asEventTarget(),
|
self.asEventTarget(),
|
||||||
event,
|
event,
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ pub fn dispatch(self: *XMLHttpRequestEventTarget, comptime event_type: DispatchT
|
|||||||
};
|
};
|
||||||
|
|
||||||
const progress = progress_ orelse Progress{};
|
const progress = progress_ orelse Progress{};
|
||||||
const event = try ProgressEvent.init(
|
const event = try ProgressEvent.initTrusted(
|
||||||
typ,
|
typ,
|
||||||
.{ .total = progress.total, .loaded = progress.loaded },
|
.{ .total = progress.total, .loaded = progress.loaded },
|
||||||
page,
|
page,
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ fn dispatchKeyEvent(cmd: anytype) !void {
|
|||||||
const page = bc.session.currentPage() orelse return;
|
const page = bc.session.currentPage() orelse return;
|
||||||
|
|
||||||
const KeyboardEvent = @import("../../browser/webapi/event/KeyboardEvent.zig");
|
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,
|
.key = params.key,
|
||||||
.code = params.code,
|
.code = params.code,
|
||||||
.altKey = params.modifiers & 1 == 1,
|
.altKey = params.modifiers & 1 == 1,
|
||||||
|
|||||||
Reference in New Issue
Block a user