mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-09-15 07:19:20 -04:00
mem: Swith EventTarget's _type to a bare tag
Same change as https://github.com/lightpanda-io/browser/pull/3128 but for EventTarget. This shrinks all EventTargets (i.e. most WebAPI interfaces) by 8 bytes.
This commit is contained in:
7 files changed
+165
-79
No files matched your search
@@ -25,9 +25,11 @@ const EventManagerBase = @import("EventManagerBase.zig");
|
||||
|
||||
const Node = @import("webapi/Node.zig");
|
||||
const Event = @import("webapi/Event.zig");
|
||||
const Window = @import("webapi/Window.zig");
|
||||
const EventTarget = @import("webapi/EventTarget.zig");
|
||||
const Element = @import("webapi/Element.zig");
|
||||
const ShadowRoot = @import("webapi/ShadowRoot.zig");
|
||||
const XMLHttpRequestEventTarget = @import("webapi/net/XMLHttpRequestEventTarget.zig");
|
||||
|
||||
const log = lp.log;
|
||||
const Allocator = std.mem.Allocator;
|
||||
@@ -83,15 +85,15 @@ pub fn dispatch(self: *EventManager, target: *EventTarget, event: *Event) Dispat
|
||||
}
|
||||
|
||||
switch (target._type) {
|
||||
.node => |node| try self.dispatchNode(node, event),
|
||||
.xhr => |xhr| try self.dispatchDirect(target, event, xhr.inlineHandler(event._type_string), .{ .context = "dispatch" }),
|
||||
.window => |w| try self.dispatchDirect(target, event, windowInlineHandler(w, event._type_string), .{ .context = "dispatch" }),
|
||||
.node => try self.dispatchNode(target.subtype(Node), event),
|
||||
.xhr => try self.dispatchDirect(target, event, target.subtype(XMLHttpRequestEventTarget).inlineHandler(event._type_string), .{ .context = "dispatch" }),
|
||||
.window => try self.dispatchDirect(target, event, windowInlineHandler(target.subtype(Window), event._type_string), .{ .context = "dispatch" }),
|
||||
else => try self.dispatchDirect(target, event, null, .{ .context = "dispatch" }),
|
||||
}
|
||||
}
|
||||
|
||||
// Resolves the Window's property event handler for the given event type.
|
||||
fn windowInlineHandler(window: *@import("webapi/Window.zig"), typ: lp.String) ?js.Function.Global {
|
||||
fn windowInlineHandler(window: *Window, typ: lp.String) ?js.Function.Global {
|
||||
const global_event_handlers = @import("webapi/global_event_handlers.zig");
|
||||
const handler_type = global_event_handlers.fromEventType(typ.str()) orelse return null;
|
||||
return switch (handler_type) {
|
||||
@@ -244,9 +246,10 @@ fn dispatchNode(self: *EventManager, target: *Node, event: *Event) !void {
|
||||
// root is the document (not for detached trees, and not when propagation
|
||||
// stopped at a shadow boundary). The only explicit exception is "load".
|
||||
if (event._type_string.eql(comptime .wrap("load")) == false and path_len < path_buffer.len) {
|
||||
const root_is_document = path_len > 0 and switch (path_buffer[path_len - 1]._type) {
|
||||
.node => |n| n._type == .document,
|
||||
else => false,
|
||||
const root_is_document = blk: {
|
||||
if (path_len == 0) break :blk false;
|
||||
const root = path_buffer[path_len - 1].is(Node) orelse break :blk false;
|
||||
break :blk root._type == .document;
|
||||
};
|
||||
if (root_is_document) {
|
||||
path_buffer[path_len] = frame.window.asEventTarget();
|
||||
@@ -508,17 +511,20 @@ fn getInlineHandler(self: *EventManager, target: *EventTarget, event: *Event) ?j
|
||||
|
||||
// Look up the inline handler for this target
|
||||
const html_element = switch (target._type) {
|
||||
.node => |n| n.is(Element.Html) orelse return null,
|
||||
.node => target.subtype(Node).is(Element.Html) orelse return null,
|
||||
// The Window stores its event handlers in dedicated fields; an event
|
||||
// propagating to the window must fire them too.
|
||||
.window => |w| return switch (handler_type) {
|
||||
.onerror => w._on_error,
|
||||
.onload => w._on_load,
|
||||
.onblur => w._on_blur,
|
||||
.onfocus => w._on_focus,
|
||||
.onresize => w._on_resize,
|
||||
.onscroll => w._on_scroll,
|
||||
else => null,
|
||||
.window => {
|
||||
const w = target.subtype(Window);
|
||||
return switch (handler_type) {
|
||||
.onerror => w._on_error,
|
||||
.onload => w._on_load,
|
||||
.onblur => w._on_blur,
|
||||
.onfocus => w._on_focus,
|
||||
.onresize => w._on_resize,
|
||||
.onscroll => w._on_scroll,
|
||||
else => null,
|
||||
};
|
||||
},
|
||||
else => return null,
|
||||
};
|
||||
@@ -639,14 +645,8 @@ fn eventPathParent(node: *Node, event: *Event, target_root: *Node, frame: ?*Fram
|
||||
// DOM spec "retarget": walk original_target out of shadow trees until the
|
||||
// node is visible from current_target's tree.
|
||||
fn getAdjustedTarget(original_target: ?*EventTarget, current_target: *EventTarget) ?*EventTarget {
|
||||
const orig_node = switch ((original_target orelse return null)._type) {
|
||||
.node => |n| n,
|
||||
else => return original_target,
|
||||
};
|
||||
const curr_node = switch (current_target._type) {
|
||||
.node => |n| n,
|
||||
else => return original_target,
|
||||
};
|
||||
const orig_node = (original_target orelse return null).is(Node) orelse return original_target;
|
||||
const curr_node = current_target.is(Node) orelse return original_target;
|
||||
|
||||
var node = orig_node;
|
||||
while (true) {
|
||||
@@ -678,10 +678,8 @@ fn isShadowIncludingInclusiveAncestor(ancestor: *Node, node: *Node) bool {
|
||||
// shadow root. Used for the spec's post-dispatch "clear targets" step.
|
||||
fn rootIsShadowRoot(target_: ?*EventTarget) bool {
|
||||
const target = target_ orelse return false;
|
||||
return switch (target._type) {
|
||||
.node => |n| n.containingShadowRoot() != null,
|
||||
else => false,
|
||||
};
|
||||
const node = target.is(Node) orelse return false;
|
||||
return node.containingShadowRoot() != null;
|
||||
}
|
||||
|
||||
// Check if ancestor is an ancestor of (or the same as) node
|
||||
|
||||
@@ -68,7 +68,7 @@ pub fn eventTargetWithAllocator(_: *const Factory, allocator: Allocator, child:
|
||||
|
||||
const event_ptr = chain.get(0);
|
||||
event_ptr.* = .{
|
||||
._type = unionInit(EventTarget.Type, chain.get(1)),
|
||||
._type = typeInit(EventTarget, chain.get(1)),
|
||||
};
|
||||
chain.setLeaf(1, child);
|
||||
|
||||
@@ -621,3 +621,6 @@ fn unionFieldName(comptime T: type, comptime V: type) []const u8 {
|
||||
}
|
||||
@compileError(@typeName(V) ++ " is not a valid type for " ++ @typeName(T) ++ ".type");
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -161,10 +161,9 @@ pub fn init(self: *Session, browser: *Browser, notification: *Notification) !voi
|
||||
errdefer arena.release();
|
||||
|
||||
const navigation = try Factory.chainedWithAllocator(arena.allocator(), .{
|
||||
EventTarget{ ._type = undefined },
|
||||
EventTarget{ ._type = .navigation },
|
||||
Navigation{ ._proto = undefined },
|
||||
});
|
||||
navigation._proto._type = .{ .navigation = navigation };
|
||||
|
||||
self.* = .{
|
||||
.arena = arena,
|
||||
|
||||
@@ -322,10 +322,7 @@ pub fn composedPath(self: *Event, exec: *Execution) ![]const *EventTarget {
|
||||
const target = self._dispatch_target orelse self._target orelse return &.{};
|
||||
|
||||
// Only nodes have a propagation path
|
||||
const target_node = switch (target._type) {
|
||||
.node => |n| n,
|
||||
else => return &.{},
|
||||
};
|
||||
const target_node = target.is(Node) orelse return &.{};
|
||||
|
||||
const frame_ = switch (exec.js.global) {
|
||||
.frame => |frame| frame,
|
||||
@@ -341,9 +338,9 @@ pub fn composedPath(self: *Event, exec: *Execution) ![]const *EventTarget {
|
||||
// Window follows the document at the end of the path. A path that stopped
|
||||
// early — at a shadow boundary, or at the relatedTarget — doesn't end on
|
||||
// the document and so doesn't reach it.
|
||||
const root_is_document = switch (path_buffer[path_len - 1]._type) {
|
||||
.node => |n| n._type == .document,
|
||||
else => false,
|
||||
const root_is_document = blk: {
|
||||
const root = path_buffer[path_len - 1].is(Node) orelse break :blk false;
|
||||
break :blk root._type == .document;
|
||||
};
|
||||
if (root_is_document and path_len < path_buffer.len) {
|
||||
if (frame_) |frame| {
|
||||
@@ -356,10 +353,7 @@ pub fn composedPath(self: *Event, exec: *Execution) ![]const *EventTarget {
|
||||
// it is inside that root and hidden from a currentTarget outside it.
|
||||
var closed_host_index: ?usize = null;
|
||||
for (path_buffer[0..path_len], 0..) |entry, i| {
|
||||
const node = switch (entry._type) {
|
||||
.node => |n| n,
|
||||
else => continue,
|
||||
};
|
||||
const node = entry.is(Node) orelse continue;
|
||||
const shadow = node.is(Node.ShadowRoot) orelse continue;
|
||||
if (shadow._mode == .closed) {
|
||||
closed_host_index = i + 1;
|
||||
|
||||
@@ -17,50 +17,142 @@
|
||||
// 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 Page = @import("../Page.zig");
|
||||
const Factory = @import("../Factory.zig");
|
||||
const EventManager = @import("../EventManager.zig");
|
||||
|
||||
const Node = @import("Node.zig");
|
||||
const Event = @import("Event.zig");
|
||||
const Screen = @import("Screen.zig");
|
||||
const Worker = @import("Worker.zig");
|
||||
const Window = @import("Window.zig");
|
||||
const AbortSignal = @import("AbortSignal.zig");
|
||||
const MessagePort = @import("MessagePort.zig");
|
||||
const FileReader = @import("FileReader.zig");
|
||||
const WebSocket = @import("net/WebSocket.zig");
|
||||
const Navigation = @import("navigation/Navigation.zig");
|
||||
const Notification = @import("Notification.zig");
|
||||
const EventSource = @import("net/EventSource.zig");
|
||||
const CookieStore = @import("storage/CookieStore.zig");
|
||||
const IDBRequest = @import("storage/idb/IDBRequest.zig");
|
||||
const SharedWorker = @import("SharedWorker.zig");
|
||||
const FontFaceSet = @import("css/FontFaceSet.zig");
|
||||
const IDBDatabase = @import("storage/idb/IDBDatabase.zig");
|
||||
const TextTrackCue = @import("media/TextTrackCue.zig");
|
||||
const VisualViewport = @import("VisualViewport.zig");
|
||||
const MediaQueryList = @import("css/MediaQueryList.zig");
|
||||
const IDBTransaction = @import("storage/idb/IDBTransaction.zig");
|
||||
const BroadcastChannel = @import("BroadcastChannel.zig");
|
||||
const WorkerGlobalScope = @import("WorkerGlobalScope.zig");
|
||||
const NavigationHistoryEntry = @import("navigation/NavigationHistoryEntry.zig");
|
||||
const XMLHttpRequestEventTarget = @import("net/XMLHttpRequestEventTarget.zig");
|
||||
|
||||
const RegisterOptions = EventManager.RegisterOptions;
|
||||
|
||||
const EventTarget = @This();
|
||||
|
||||
pub const _prototype_root = true;
|
||||
_type: Type,
|
||||
|
||||
pub const Type = union(enum) {
|
||||
generic: void,
|
||||
node: *@import("Node.zig"),
|
||||
window: *@import("Window.zig"),
|
||||
worker: *@import("Worker.zig"),
|
||||
shared_worker: *@import("SharedWorker.zig"),
|
||||
worker_global_scope: *@import("WorkerGlobalScope.zig"),
|
||||
xhr: *@import("net/XMLHttpRequestEventTarget.zig"),
|
||||
abort_signal: *@import("AbortSignal.zig"),
|
||||
media_query_list: *@import("css/MediaQueryList.zig"),
|
||||
message_port: *@import("MessagePort.zig"),
|
||||
broadcast_channel: *@import("BroadcastChannel.zig"),
|
||||
text_track_cue: *@import("media/TextTrackCue.zig"),
|
||||
navigation: *@import("navigation/Navigation.zig"),
|
||||
navigation_history_entry: *@import("navigation/NavigationHistoryEntry.zig"),
|
||||
screen: *@import("Screen.zig"),
|
||||
screen_orientation: *@import("Screen.zig").Orientation,
|
||||
visual_viewport: *@import("VisualViewport.zig"),
|
||||
file_reader: *@import("FileReader.zig"),
|
||||
font_face_set: *@import("css/FontFaceSet.zig"),
|
||||
websocket: *@import("net/WebSocket.zig"),
|
||||
event_source: *@import("net/EventSource.zig"),
|
||||
cookie_store: *@import("storage/CookieStore.zig"),
|
||||
idb_request: *@import("storage/idb/IDBRequest.zig"),
|
||||
idb_database: *@import("storage/idb/IDBDatabase.zig"),
|
||||
idb_transaction: *@import("storage/idb/IDBTransaction.zig"),
|
||||
notification: *@import("Notification.zig"),
|
||||
// `global_event_handlers.Key` reuses the low 3 bits of an EventTarget pointer,
|
||||
// so the type has to stay 8-byte aligned even though the tag is a single byte.
|
||||
// This costs nothing in a chain: EventTarget is always at offset 0 and every
|
||||
// subtype that follows it is itself 8-aligned.
|
||||
_type: Type align(8),
|
||||
|
||||
pub const Type = enum(u8) {
|
||||
generic,
|
||||
node,
|
||||
window,
|
||||
worker,
|
||||
shared_worker,
|
||||
worker_global_scope,
|
||||
xhr,
|
||||
abort_signal,
|
||||
media_query_list,
|
||||
message_port,
|
||||
broadcast_channel,
|
||||
text_track_cue,
|
||||
navigation,
|
||||
navigation_history_entry,
|
||||
screen,
|
||||
screen_orientation,
|
||||
visual_viewport,
|
||||
file_reader,
|
||||
font_face_set,
|
||||
websocket,
|
||||
event_source,
|
||||
cookie_store,
|
||||
idb_request,
|
||||
idb_database,
|
||||
idb_transaction,
|
||||
notification,
|
||||
};
|
||||
|
||||
// `.generic` maps to EventTarget itself: a standalone `new EventTarget()` has
|
||||
// no chain member of its own.
|
||||
pub fn Subtype(comptime tag: Type) type {
|
||||
return switch (tag) {
|
||||
.generic => EventTarget,
|
||||
.node => Node,
|
||||
.window => Window,
|
||||
.worker => Worker,
|
||||
.shared_worker => SharedWorker,
|
||||
.worker_global_scope => WorkerGlobalScope,
|
||||
.xhr => XMLHttpRequestEventTarget,
|
||||
.abort_signal => AbortSignal,
|
||||
.media_query_list => MediaQueryList,
|
||||
.message_port => MessagePort,
|
||||
.broadcast_channel => BroadcastChannel,
|
||||
.text_track_cue => TextTrackCue,
|
||||
.navigation => Navigation,
|
||||
.navigation_history_entry => NavigationHistoryEntry,
|
||||
.screen => Screen,
|
||||
.screen_orientation => Screen.Orientation,
|
||||
.visual_viewport => VisualViewport,
|
||||
.file_reader => FileReader,
|
||||
.font_face_set => FontFaceSet,
|
||||
.websocket => WebSocket,
|
||||
.event_source => EventSource,
|
||||
.cookie_store => CookieStore,
|
||||
.idb_request => IDBRequest,
|
||||
.idb_database => IDBDatabase,
|
||||
.idb_transaction => IDBTransaction,
|
||||
.notification => Notification,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn subtype(self: *const EventTarget, comptime T: type) *T {
|
||||
const offset = comptime Factory.chainOffsetOf(T, T) - Factory.chainOffsetOf(T, EventTarget);
|
||||
const sub: *T = @ptrFromInt(@intFromPtr(self) + offset);
|
||||
if (comptime lp.IS_DEBUG) {
|
||||
// This pointer dance only works because the factory allocates the chain
|
||||
// in a contiguous block of memory. In debug, we assert this holds via
|
||||
// the _proto_canary back pointer.
|
||||
std.debug.assert(Factory.protoOf(sub) == self);
|
||||
}
|
||||
return sub;
|
||||
}
|
||||
|
||||
// Returns the target as a more specific type, or null if it isn't a `T`.
|
||||
pub fn is(self: *EventTarget, comptime T: type) ?*T {
|
||||
switch (self._type) {
|
||||
.generic => {},
|
||||
inline else => |tag| {
|
||||
if (Subtype(tag) == T) {
|
||||
return self.subtype(T);
|
||||
}
|
||||
},
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn as(self: *EventTarget, comptime T: type) *T {
|
||||
return self.is(T).?;
|
||||
}
|
||||
|
||||
pub fn init(page: *Page) !*EventTarget {
|
||||
return page.factory.create(EventTarget{
|
||||
._type = .generic,
|
||||
@@ -121,8 +213,9 @@ fn defaultPassiveValue(self: *EventTarget, typ: []const u8) bool {
|
||||
|
||||
switch (self._type) {
|
||||
.window => return true,
|
||||
.node => |n| {
|
||||
.node => {
|
||||
const Element = @import("Element.zig");
|
||||
const n = self.subtype(Node);
|
||||
if (n._type == .document) {
|
||||
return true;
|
||||
}
|
||||
@@ -212,7 +305,7 @@ pub fn removeEventListener(self: *EventTarget, typ: []const u8, callback_: js.Nu
|
||||
|
||||
pub fn format(self: *EventTarget, writer: *std.Io.Writer) !void {
|
||||
return switch (self._type) {
|
||||
.node => |n| n.format(writer),
|
||||
.node => self.subtype(Node).format(writer),
|
||||
.generic => writer.writeAll("<EventTarget>"),
|
||||
.window => writer.writeAll("<Window>"),
|
||||
.worker => writer.writeAll("<Worker>"),
|
||||
@@ -293,6 +386,8 @@ test "WebApi: EventTarget" {
|
||||
testing.silenceLog(&.{ .js, .event });
|
||||
|
||||
// we create thousands of these per frame. Nothing should bloat it.
|
||||
try testing.expectEqual(16, @sizeOf(EventTarget));
|
||||
// The tag is 1 byte; the rest is the align(8) that `Key.fuse` depends on.
|
||||
try testing.expectEqual(8, @sizeOf(EventTarget));
|
||||
try testing.expectEqual(8, @alignOf(EventTarget));
|
||||
try testing.htmlRunner("events.html", .{});
|
||||
}
|
||||
@@ -143,7 +143,7 @@ pub fn init(
|
||||
|
||||
const factory = frame._factory;
|
||||
const leaf = try Factory.chainedWithAllocator(arena, .{
|
||||
EventTarget{ ._type = undefined },
|
||||
EventTarget{ ._type = .worker_global_scope },
|
||||
WorkerGlobalScope{
|
||||
.url = url,
|
||||
.arena = arena,
|
||||
@@ -173,7 +173,6 @@ pub fn init(
|
||||
});
|
||||
const self = leaf._proto;
|
||||
self._type = @unionInit(Type, @tagName(tag), leaf);
|
||||
self._proto._type = .{ .worker_global_scope = self };
|
||||
|
||||
self._http_owner = .init(&frame._page.blob_urls, &self.origin);
|
||||
|
||||
|
||||
@@ -216,7 +216,7 @@ pub fn pushEntry(
|
||||
const id_str = try std.fmt.allocPrint(arena.allocator(), "{d}", .{id});
|
||||
|
||||
const entry = try Factory.chainedWithAllocator(arena.allocator(), .{
|
||||
EventTarget{ ._type = undefined },
|
||||
EventTarget{ ._type = .navigation_history_entry },
|
||||
NavigationHistoryEntry{
|
||||
._proto = undefined,
|
||||
._id = id_str,
|
||||
@@ -225,7 +225,6 @@ pub fn pushEntry(
|
||||
._state = state,
|
||||
},
|
||||
});
|
||||
entry._proto._type = .{ .navigation_history_entry = entry };
|
||||
|
||||
// we don't always have a current entry...
|
||||
const previous = if (self._entries.items.len > 0) self.getCurrentEntry() else null;
|
||||
@@ -263,7 +262,7 @@ pub fn replaceEntry(
|
||||
const id_str = try std.fmt.allocPrint(arena.allocator(), "{d}", .{id});
|
||||
|
||||
const entry = try Factory.chainedWithAllocator(arena.allocator(), .{
|
||||
EventTarget{ ._type = undefined },
|
||||
EventTarget{ ._type = .navigation_history_entry },
|
||||
NavigationHistoryEntry{
|
||||
._proto = undefined,
|
||||
._id = id_str,
|
||||
@@ -272,7 +271,6 @@ pub fn replaceEntry(
|
||||
._state = state,
|
||||
},
|
||||
});
|
||||
entry._proto._type = .{ .navigation_history_entry = entry };
|
||||
|
||||
const old_entry = self._entries.items[self._index];
|
||||
self._entries.items[self._index] = entry;
|
||||
|
||||
Reference in new issue
Block a user